Problem
Block signing is asynchronous: the chain orchestrator issues a SignBlock request and receives the SignedBlock result on a later select iteration. If a reorg or an administrative reset unwinds the chain between request and result, the orchestrator still processes the stale result — it persists the L2 head block number and signature and announces the block to the network (handle_signer_event), even though the block was built on a chain state the unwind just discarded.
Consequences: a signed block for a discarded chain generation can be gossiped to peers, and the persisted L2 head/signature state can point at an unwound block.
This is pre-existing on feat/tsuki-hardfork-2 and independent of signer rotation — an ordinary L1 reorg during payload building is sufficient.
Reference fix (mine for parts)
PR #7 (closed unmerged) addressed this in commit 7496ee8 with generation tagging: the signer echoes a caller-supplied generation tag through SignerRequest::SignBlock → SignerEvent::SignedBlock, the orchestrator bumps an unwind_generation counter exactly once per committed unwind (reset or reorg), and stale-tagged results are discarded before persistence/announcement. The mechanism is sound and has unit coverage on the PR branch; re-implement it at whatever size the next abstraction needs.
Found during the PR #7 review/remediation cycle.
Problem
Block signing is asynchronous: the chain orchestrator issues a
SignBlockrequest and receives theSignedBlockresult on a later select iteration. If a reorg or an administrative reset unwinds the chain between request and result, the orchestrator still processes the stale result — it persists the L2 head block number and signature and announces the block to the network (handle_signer_event), even though the block was built on a chain state the unwind just discarded.Consequences: a signed block for a discarded chain generation can be gossiped to peers, and the persisted L2 head/signature state can point at an unwound block.
This is pre-existing on
feat/tsuki-hardfork-2and independent of signer rotation — an ordinary L1 reorg during payload building is sufficient.Reference fix (mine for parts)
PR #7 (closed unmerged) addressed this in commit
7496ee8with generation tagging: the signer echoes a caller-suppliedgenerationtag throughSignerRequest::SignBlock→SignerEvent::SignedBlock, the orchestrator bumps anunwind_generationcounter exactly once per committed unwind (reset or reorg), and stale-tagged results are discarded before persistence/announcement. The mechanism is sound and has unit coverage on the PR branch; re-implement it at whatever size the next abstraction needs.Found during the PR #7 review/remediation cycle.