Repository navigation
feat(messaging): persist message nonces across restarts (Stellar Wave) - #1251
Merged
mijinummi merged 1 commit intoSep 28, 2026
Merged
Conversation
Add a durable NonceStore (InMemory / FileNonceStore) and a per-chain NonceManager. Executors reserve a nonce and persist the chain high-water mark and per-message pin before broadcasting, so a crash, failover, or queue redelivery never re-issues a nonce and a re-executed message reuses its original nonce instead of double-broadcasting. Repricing retries resubmit with the reserved nonce. Corrupt state files fail fast rather than resetting to zero.
|
@barry01-hash Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
12 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1161
What
Every chain now keeps a persisted high-water mark (next nonce) and every message a persisted pin of the nonce it was first delivered with, so a restart, replica failover, or queue redelivery can never re-issue a nonce — and a re-executed message reuses its original nonce instead of double-broadcasting on the destination chain.
Historically the nonce lived only in process memory; restarting the relayer mid-delivery could mint the same nonce twice.
How it works
NonceStorecontract (types.ts) +InMemoryNonceStore(default, this is today's behavior) andFileNonceStore(src/nonce/).FileNonceStorewrites one JSON file atomically (temp sibling + fsync + rename),0600, corrupt/unsupported files fail fast instead of silently resetting the counter to zero.NonceManager(src/nonce/nonce-manager.ts) hands out per-chain nonces with serialised acquire/release and the key crash-safety order: a nonce is persisted before it may be broadcast. Persist failure rolls the in-memory counter back and fails the delivery rather than burning an unrecorded nonce.EvmExecutorandSorobanExecutorwire the manager, reserve once per delivery, reuse the reserved nonce on repricing retries, and release the pin after on-chain confirmation.executor.updateNonce()/getNonce()now delegate to the manager;raiseOnlysemantics mean a stale operator input can never lower the counter.nonce-loaded,nonce-assigned,nonce-reused,nonce-error {phase: load|persist|release},nonce-declined, plus Sorobannonce-bookkeeping.Files
apps/relayer-service/src/nonce/{in-memory-nonce-store,file-nonce-store,nonce-manager}.ts(new)apps/relayer-service/src/executors/{evm,soroban}-executor.ts(wired)apps/relayer-service/src/types.ts,src/index.tssrc/nonce/__tests__/*incl. a cross-component pipeline spec (executor + file store + mocked EVM RPC)docs/MESSAGE_NONCE_PERSISTENCE.mdVerification
tsc --noEmit: cleanjest --ci: 342 tests / 15 suites, all passing (43 new)src/nonce96.66% stmts / 91.37% branch (file-nonce-store.ts98.57% stmts,in-memory-nonce-store.ts100%,nonce-manager.ts93.1%)evm-executor.ts96.38% stmts / 100% funcs (Soroban's low coverage is pre-existing carried-over untested RPC paths, not from this change)npm run build: cleanSafety rationale
FileNonceStorenever silently resets to zero on a corrupt file (the one situation that could re-issue consumed nonces) — it fails fast and asks the operator to restore from backup.Note:
apps/relayer-service/node_modulesis tracked in the repo (pre-existing); it is intentionally not touched in this PR. The CI workflow runs the relayer-service suite.Assignees: @barry01-hash