fix(validator): process block proposals from own validator keys in HA setups#21603
Merged
PhilWindle merged 1 commit intomerge-train/spartanfrom Mar 17, 2026
Merged
Conversation
4cd76ef to
8df7c64
Compare
… setups Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
8df7c64 to
5eedb7d
Compare
PhilWindle
approved these changes
Mar 17, 2026
Collaborator
|
❌ Failed to cherry-pick to |
AztecBot
added a commit
that referenced
this pull request
Mar 17, 2026
…e proposal test When PR #21603 changed the validator to process (not ignore) block proposals from HA peers (same validator key), the duplicate_proposal_slash test broke. The second malicious node now processes the first node's proposal, adds the block to its archiver, and the sequencer sees the slot as taken - preventing it from ever building its own conflicting proposal. Fix: set skipPushProposedBlocksToArchiver=true on the malicious nodes so that HA peer proposals are re-executed but not added to the archiver. This allows both malicious nodes to independently build and broadcast proposals for the same slot, which is what the test needs for equivocation detection.
spalladino
pushed a commit
that referenced
this pull request
Mar 17, 2026
…e proposal test (#21673) ## Summary When PR #21603 changed the validator to process (not ignore) block proposals from HA peers (same validator key), the `duplicate_proposal_slash` test broke. The second malicious node now processes the first node's proposal, adds the block to its archiver via `blockSource.addBlock()`, and the sequencer sees "slot was taken" — preventing it from ever building its own conflicting proposal. **Root cause**: `validateBlockProposal` no longer returns `false` for self-proposals (changed to process them for HA support). The block_proposal_handler re-executes the proposal and pushes it to the archiver. The sequencer then skips the slot. **Fix**: Set `skipPushProposedBlocksToArchiver=true` on the malicious nodes. This allows: 1. Node 1 builds and broadcasts its proposal 2. Node 2 receives it, re-executes (as HA peer), but does NOT add to archiver 3. Node 2's sequencer doesn't see "slot taken" → builds its own block with different coinbase 4. Node 2 broadcasts (allowed by `broadcastEquivocatedProposals=true`) 5. Honest nodes see both proposals → detect duplicate → offense recorded ## Test plan - The `duplicate_proposal_slash` e2e test should now pass consistently - Other slashing tests should be unaffected (only malicious nodes in this test are changed) ClaudeBox log: https://claudebox.work/s/ced449aa0eabbcb4?run=1
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.
Motivation
In an HA setup, two nodes (A and B) share the same validator keys. When node A proposes a block, node B receives it via gossipsub but ignores it because
validateBlockProposaldetects the proposer address matches its own validator keys and returns early. This means node B never re-executes the block, never pushes it to its archiver, and falls behind the proposed chain.Additionally, both HA peers independently try to build and propose blocks for the same slot. If the losing peer commits its block to the archiver before signing fails, it ends up with a stale block that prevents it from accepting the winning peer's proposal.
Approach
Three changes work together to fix HA proposed chain sync:
Remove self-filtering: Remove the early return in
validateBlockProposalfor self-proposals, letting them flow through the normal re-execution path so the HA peer pushes the winning block to its archiver.Sign before syncing to archiver: Reorder the checkpoint proposal job so that non-last blocks are signed via
createBlockProposalbefore being synced to the archiver. If the shared slashing protection DB rejects signing (because the HA peer already signed), the block is never added to the archiver, keeping it clean to accept the winning peer's block via gossipsub.Shared slashing protection for testing: Add
createSharedSlashingProtectionDb(backed by a shared LMDB store) andcreateSignerFromSharedDbfactories, and thread an optionalslashingProtectionDbthrough the validator creation chain. This allows e2e tests to simulate HA signing coordination without PostgreSQL.Changes
validateBlockProposal. Add optionalslashingProtectionDbparameter toValidatorClient.newandcreateValidatorClientfactory for injecting a shared signing protection DB.handleBlockProposal.checkpoint_proposal_jobso non-last blocks callcreateBlockProposalbeforesyncProposedBlockToArchiver. If signing fails (HA signer rejects), the block is never added to the archiver.createSharedSlashingProtectionDbandcreateSignerFromSharedDbfactory functions for testing HA setups with a shared in-memory LMDB store.slashingProtectionDbthroughAztecNodeService.createAndSyncdeps.epochs_ha_synce2e test with 4 nodes in 2 HA pairs (each pair sharing validator keys and a slashing protection DB), different coinbase addresses per node, MBPS enabled, checkpoint publishing disabled. Asserts all 4 nodes converge on the same proposed block hash before any checkpoint is published.Fixes A-675