fix(validator): reject block signing when signing key mismatches genesis validator key#2203
Merged
Mirko-von-Leipzig merged 5 commits intoJun 5, 2026
Conversation
0ed6d1b to
d3e7e60
Compare
…sis validator key The block's `validator_key` is carried forward from the genesis block, so a validator started with a different key than the one used to bootstrap genesis produces a signature that does not verify against `header.validator_key()`. The block producer then rejects the block with an opaque `InvalidSignature`, which is what causes block 1 to be rejected on a misconfigured network. `validate_block` now compares the signer's public key against the header's `validator_key` before signing and fails with a clear `ValidatorKeyMismatch` error naming both keys. Adds two tests in the validator server suite: - `block_one_signature_verifies_against_header_key` mirrors the block producer's post-signing check for block 1 (previously untested, which is why the bug slipped through). - `signing_key_mismatch_rejected` asserts that validating a block with a signer whose key differs from the chain's validator key is rejected. `TestValidator::new()` was itself constructing a misconfigured validator (two different random keys); it now signs with the genesis key. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
d3e7e60 to
628301d
Compare
bobbinth
approved these changes
Jun 4, 2026
Contributor
bobbinth
left a comment
There was a problem hiding this comment.
Looks good! Thank you! (I reviewed non-test code)
auto-merge was automatically disabled
June 5, 2026 11:13
Head branch was modified
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.
Problem
On a real network deployment, the block producer rejects block 1 with
InvalidSignature(crates/block-producer/src/block_builder/mod.rs):The block header's
validator_keyis carried forward from the genesis block (miden-protocolProposedBlock::into_header_and_bodycopiesprev_block_header.validator_key()), and the genesisvalidator_keyis set once, at bootstrap, from the bootstrap signer's public key. If the running validator signs with a different key than the one used to bootstrap genesis, block 1 carries the bootstrap key in its header but is signed by the start key, so verification fails.This is invisible on the validator side:
validate_blocksigns the header with its own key and never checks that key against the header'svalidator_key. The validator's own KMS self-check passes (it verifies against its own key), so the only place the mismatch surfaces is the block producer, as an opaqueInvalidSignatureon block 1.It passed in integration tests because they use the same key for both bootstrap and signing. The likely production trigger is the silent insecure-key fallback in
start(--key.hexhas a default and KMS is only used when--key.kms-idis present), so a missingMIDEN_VALIDATOR_KMS_KEY_IDenv var causes the validator to silently sign with the insecure default key.Fix
validate_blocknow compares the signer's public key against the header'svalidator_keybefore signing, and returns a clearBlockValidationError::ValidatorKeyMismatch(naming both keys) instead of handing back a signature the block producer cannot use. The check is placed after the existing prev-commitment check to preserve error ordering.This is a defense-in-depth + clear-error change. The underlying root cause is operational: the validator must sign with the same key embedded in genesis at bootstrap (same
--key.kms-id/ hex on bothbootstrapandstart).Tests
This path had no coverage: no test exercised the block-producer's post-signing verification for block 1, and
TestValidator::new()was itself constructing a misconfigured validator (genesis key != signing key) yet every test passed.block_one_signature_verifies_against_header_keymirrors the block producer'ssignature.verify(header.commitment(), header.validator_key())check for block 1. It fails on the pre-fix code (reproducing the bug) and passes afterTestValidator::new()is corrected.signing_key_mismatch_rejectedasserts a validator with genesis key != signing key rejects block 1 withValidatorKeyMismatch, both via the gRPC handler andvalidate_blockdirectly.TestValidator::new()is now correctly configured (one key for both), with awith_keys(genesis_key, signing_key)constructor for the mismatch case.Verification
cargo test -p miden-validator --lib: 15 passed, 1 ignored (needs diesel CLI).cargo clippy --locked -p miden-validator --all-targets --all-features -- -D warnings: clean.make lintformatters): clean.Possible follow-up (not in this PR)
Fail fast at validator startup: compare
signer.public_key()against the stored chain-tip'svalidator_keyinValidator::serve()and refuse to start with a clear error, so a misconfigured key is caught before serving rather than per-block. Optionally remove the silent insecure-key default instart(or gate it behind an explicit--insecureflag).🤖 Generated with Claude Code