Skip to content

Commit d4d77e3

Browse files
authored
fix(ethereum): prevent unhandled L1 rejections from tx monitor loops during shutdown (v5 line, partial) (#24551)
## Flake `multi-node/slashing/sentinel_status_slash.parallel.test.ts` ("slashes an attestor that gets stopped after the network is running") flaked on PR #24507 (unrelated change). CI log: http://ci.aztec-labs.com/9bb09d2f904e290a ## What actually failed The test itself **passed**. Immediately after, the node process crashed with an **unhandled rejection** (`ECONNREFUSED` on anvil after teardown) — something was still polling L1 after shutdown. Root cause: `L1TxUtils.monitorTransaction`'s loop calls `getL1Timestamp()` at the top of its loop, outside the loop's own try/catch, and nothing waited for in-flight monitor iterations to actually stop during shutdown. ## Scope of this PR (rebased onto `merge-train/spartan-v5`) This PR was originally written against `next` and touched 4 files. After rebasing onto `merge-train/spartan-v5`, 2 of those files cherry-picked cleanly and are included here: - `yarn-project/ethereum/src/l1_tx_utils/l1_tx_utils.ts` — guards the loop-top `getL1Timestamp()` call to break quietly when interrupted. - `yarn-project/sequencer-client/src/publisher/sequencer-publisher.ts` — adds `.catch` to a bare fire-and-forget `backupDroppedInSim(...)` call. The other 2 files (`publisher_manager.ts`, `checkpoint_proposal_job.ts`) have diverged in structure on this branch and needed their own investigation rather than a blind cherry-pick — that's covered separately in [#24560](#24560), which found `publisher_manager.ts` had the same defect (fixed there) and `checkpoint_proposal_job.ts` did not (this branch already routes fire-and-forget L1 requests through a `RequestsTracker` that attaches its own rejection handler). No tracked issue exists for this flake; reference only. --- *Created by [claudebox](https://claudebox.work/v2/sessions/0959caa9f03f59fa) · group: `slackbot`*
1 parent 5627161 commit d4d77e3

2 files changed

Lines changed: 18 additions & 3 deletions

File tree

yarn-project/ethereum/src/l1_tx_utils/l1_tx_utils.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -436,10 +436,23 @@ export class L1TxUtils extends ReadOnlyL1TxUtils {
436436

437437
const initialTxHash = txHashes[0];
438438
let currentTxHash = txHashes.at(-1)!;
439-
let l1Timestamp: number;
439+
let l1Timestamp = 0;
440440

441441
while (true) {
442-
l1Timestamp = await this.getL1Timestamp();
442+
try {
443+
l1Timestamp = await this.getL1Timestamp();
444+
} catch (err) {
445+
// A transient RPC failure here must not abort monitoring with a rejection: callers that
446+
// fire this loop in the background would surface it as an unhandled rejection (e.g. when a
447+
// test tears down its L1 node while a monitor iteration is in flight). Exit quietly if we
448+
// are shutting down, otherwise retry on the next interval.
449+
if (this.interrupted) {
450+
break;
451+
}
452+
this.logger.error(`Error fetching L1 timestamp while monitoring tx ${currentTxHash}`, err, { nonce, account });
453+
await sleep(gasConfig.checkIntervalMs!);
454+
continue;
455+
}
443456

444457
try {
445458
const timePassed = l1Timestamp - state.lastSentAtL1Ts.getTime();

yarn-project/sequencer-client/src/publisher/sequencer-publisher.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,9 @@ export class SequencerPublisher {
469469

470470
if (bundleResult.kind === 'aborted') {
471471
this.logDroppedInSim(bundleResult.droppedRequests);
472-
void this.backupDroppedInSim(bundleResult.droppedRequests);
472+
void this.backupDroppedInSim(bundleResult.droppedRequests).catch(err =>
473+
this.log.error(`Failed to backup requests dropped in simulation`, err),
474+
);
473475
return undefined;
474476
}
475477

0 commit comments

Comments
 (0)