Skip to content

Fix/prune batch consensus halt - #131

Merged
nol4lej merged 3 commits into
mainfrom
fix/prune-batch-consensus-halt
Aug 13, 2026
Merged

Fix/prune batch consensus halt#131
nol4lej merged 3 commits into
mainfrom
fix/prune-batch-consensus-halt

Conversation

@nol4lej

@nol4lej nol4lej commented Aug 13, 2026

Copy link
Copy Markdown
Member

Three fixes, one runtime version. spec_version 8 → 9, transaction_version stays at 2 — no dispatch signature changes, no migration, no storage change.

Consensus — halted the testnet at 406997

pallet-shielded-pool's on_idle sized the sealed-node sweep from the block's leftover weight. Leftover weight is not consensus: once post-dispatch refunds are in play an author and an importer measure the same block differently, so each pruned a different number of nodes and wrote different state. Frontier folds that state into the Ethereum header it builds in on_finalize, so the divergence surfaced as a mismatched "fron" digest and Executive::final_checks panicking with "Digest item must match that calculated."

Three validators with identical state and byte-identical extrinsics produced three mutually unimportable blocks; the chain stopped for 4 hours. It survived five days only because empty blocks leave the same leftover weight everywhere — the first block with real EVM traffic split the network three ways.

The sweep now runs in on_initialize over a constant batch (PRUNED_NODES_PER_BLOCK, unchanged at 512, ~6.5 ms of a 2 s block) and charges the full batch rather than the removals, since a miss costs the same read as a hit. Introduced in #122.

Security

  • Pool admission tags one entry per nullifier, in a namespace shared with unshield. and_provides contributes exactly one tag, so passing it a Vec let reordered inputs mint a second admissible entry for the same spend, let one note back unboundedly many entries, and let transfer/unshield each admit the same note. Admission policy only — consensus unaffected.
  • The relay selector fallback derives from the precompile decoder. The runtime's literals had drifted two signature versions behind, so relaying rejected every call as "unsupported selector" — silently, since that is indistinguishable from a legitimate rejection.

Tests

prune_batch_is_independent_of_block_fullness — verified by mutation: with the pre-fix on_idle restored it fails (9 nodes/tree 2 vs 8 nodes/tree 1). Plus three adversarial cases covering batch carving, sealing interleaved with sweeping, and the active tree surviving an exhaustive sweep. 351/351 pass.

@nol4lej
nol4lej merged commit 4c0501c into main Aug 13, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant