Skip to content

fix(script): don't panic when a broadcast sequence and its sensitive-cache file desync - #16580

Open
gomesalexandre wants to merge 1 commit into
foundry-rs:masterfrom
gomesalexandre:fix_script_sequence_desync_panic
Open

fix(script): don't panic when a broadcast sequence and its sensitive-cache file desync#16580
gomesalexandre wants to merge 1 commit into
foundry-rs:masterfrom
gomesalexandre:fix_script_sequence_desync_panic

Conversation

@gomesalexandre

Copy link
Copy Markdown
Contributor

What

fill_sensitive() indexed sensitive.transactions[i] directly with no bounds check:

self.transactions
    .iter_mut()
    .enumerate()
    .for_each(|(i, tx)| tx.rpc.clone_from(&sensitive.transactions[i].rpc));

save() writes the broadcast file, then the sensitive-cache file, as two separate writes after every transaction. A Ctrl-C or a full disk landing between those two writes leaves them with a different number of entries. The next load() then panics with an out-of-bounds index instead of erroring.

The same bug shape existed one level up in multi_sequence.rs::load() (sensitive_sequence.deployments[i]) for multi-chain deployments, fixed the same way since it calls fill_sensitive and needed updating for the signature change anyway.

Fix

fill_sensitive now returns Result<()> and validates the two entry counts match exactly before touching anything — catching a longer sensitive-cache file (e.g. a stale leftover from a prior, longer run), not just a shorter one, and never partially filling self.transactions before reporting the mismatch. An earlier draft of this fix only guarded the shorter case with a per-index .get(), which a synchronous adversarial review (Codex) caught as one-sided and capable of partial mutation before erroring — fixed by moving to an upfront length check.

Testing

  • Real repro: constructed a ScriptSequence with 2 transactions and a SensitiveScriptSequence with only 1 entry — confirmed the original code path panics on this input, confirmed the fix errors cleanly instead.
  • 3 new unit tests in crates/script-sequence/src/sequence.rs: shorter cache errors (and doesn't partially mutate), longer cache errors, and a synced cache still fills correctly.
  • cargo test -p forge-script-sequence: 5/5 passing.
  • cargo check -p forge-script-sequence -p forge-script --tests: clean.
  • cargo clippy -p forge-script-sequence --tests -- -D warnings: clean. (-p forge-script pulls in a pre-existing, unrelated clippy failure in the anvil crate via dev-dependencies — confirmed via git stash that it reproduces identically on unmodified origin/master, nothing to do with this diff.)
  • cargo fmt --check: clean.
  • Synchronous adversarial review (Codex): caught the one-sided length-check gap described above; addressed before opening this PR.

closes nothing — filed independently, no corresponding issue was open (dupe-checked; the only related hit, #9095/#9096, is a distinct nonce/RPC-latency desync bug, not this indexing panic).

…cache file desync

fill_sensitive() indexed sensitive.transactions[i] directly with no bounds
check. save() writes the broadcast file then the sensitive-cache file as two
separate writes; an interruption between the two (Ctrl-C, disk full) leaves
them with a different number of entries, and the next load() panicked on the
out-of-bounds index instead of erroring.

Fixed to validate the entry counts match exactly before any mutation -
catching both a shorter AND a longer cache, not just the shorter case, and
never partially filling transactions before reporting the mismatch. The same
bug shape existed one level up in multi_sequence.rs::load() for multi-chain
deployments, fixed the same way.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

✅ Changelog found

The deterministic check will validate the changed entry.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

1 participant