Repository navigation
bug: fresh MasternodeListEngine rejects first QRInfo with AllCommitmentAggregatedSignatureNotValid (rotated quorum, testnet) #907
Description
Summary
During the first QRInfo message processed by a freshly-created MasternodeListEngine (headers fully synced, masternode list sync not yet started), feed_qr_info deterministically rejects the rotated quorum's aggregated commitment signature with:
QuorumValidationError::AllCommitmentAggregatedSignatureNotValid("invalid signature")
This is a genuine BLS verify_secure() failure (blsful), not a "missing signature" condition — the commitment signature is present but the reconstructed member set apparently doesn't match the real signers, which our own reading of this crate's test comments (see below) predicts happens when quorum-rotation member reconstruction ("the previous-cycle path") picks the wrong work block.
The error is fatal: it propagates up through dash-spv's masternode SyncManager as MasternodeSyncFailed, and there is no retry/recovery path for this specific error variant (unlike RequiredRotatedChainLockSigNotPresent, which does have retry logic per #470/#736).
Environment
- rust-dashcore rev:
6bb0c5f24175fb8f9c1a4b9919836de7fb8a8f8e(merge ofdevintofeat/address-reservation/ PR feat(key-wallet): reserve receive addresses on hand-out #818, 2026-07-20) - Consumer: dashpay/platform
packages/rs-platform-wallet, e2e test suite (tests/e2e), viadash-spv→dash_sdk - Network: Dash Testnet
- Client config: full validation, bloom-filter mempool tracking, DAPI peers seeded from
address_list, max 3 peers - Trigger:
dash-spv's masternodeSyncManager::handle_messagereceiving the firstNetworkMessage::QRInfoand callingengine.feed_qr_info(qr_info, true, true)(both verification flags on — this is the default/mandatory path, not an opt-in)
Reproduction
RUST_MIN_STACK=16777216 cargo test -p platform-wallet --test e2e --features e2e -- \
--nocapture --test-threads=3 \
tk_001b_token_transfer_zero_rejected tk_013 tk_014Reproduced on 2 independent live runs against testnet, ~3 minutes apart, 6 total occurrences (3 concurrent test processes × 2 runs), each spinning up its own fresh MasternodeListEngine / SPV workdir. Wiping the local spv-data/ cache between runs made no difference — the second run reproduced identically on a completely fresh on-disk state, ruling out local corruption/staleness.
Notably, this exact error string appears zero times in a separate, larger e2e run (188 tests, --test-threads=4, ~35 min) against the same testnet completed ~2.5 hours earlier the same day — so whatever triggers it is either time/chain-state correlated, or specific to the concurrency/pacing of the smaller reruns. All observed failures happen on the engine's first-ever QRInfo (qr_infos_requested: 1, validated_cycles: 0, rotation_cycles: 0), immediately after header sync completes but before any masternode-list history exists — i.e. exclusively during the initial-sync path.
Call trace (at 6bb0c5f)
dash-spv/src/sync/masternodes/sync_manager.rs:249—engine.feed_qr_info(qr_info.clone(), true, true)dash/src/sml/masternode_list_engine/mod.rs:770(feed_qr_info) → validates rotated quorumsdash/src/sml/masternode_list_engine/validation.rs:25-42(validate_quorum) →
find_valid_masternodes_for_quorum→ (rotating type) →dash/src/sml/masternode_list_engine/rotated_quorum_construction.rs:30-72(find_rotated_masternodes_for_quorum) →masternode_list_entry_members_for_rotated_quorum(lines 201-257), which reconstructs the 4 quarters (h-3c,h-2c,h-c, new) viaquorum_quarter_members_by_reconstruction_type, keyed offwork_block_height_for_indexvalidate_quorumzips the reconstructed member list againstquorum.quorum_entry.signersby index position and calls:dash/src/sml/quorum_entry/validation.rs:26-66(verify_aggregated_commitment_signature) — collects operator BLS public keys from the (index-matched) member set, deserializes the aggregate signature, and callsblsful::Signature::verify_secure(&public_keys, message)→ fails- Error surfaces as
QuorumValidationError::AllCommitmentAggregatedSignatureNotValid(dash/src/sml/quorum_validation_error.rs:72-73)
Why this looks like a genuine bug, not just "flaky testnet"
This crate's own test suite documents the exact failure shape we're hitting, in a comment on masternode_list_engine/mod.rs (~line 1758):
"Every stored rotated quorum must re-validate under its captured CL sigs. If the previous-cycle path picked the wrong work block, the reconstructed members would not match the real signers and
validate_quorumwould returnAllCommitmentAggregatedSignatureNotValid."
That is precisely our symptom. We did not find Failed to deserialize operator key warnings in any of our logs (the filter_map in verify_aggregated_commitment_signature that silently drops undeserializable keys), which argues against a legacy/modern BLS-key-format misdetection causing a partial key set — pointing instead at the member-reconstruction/ordering path itself (steps 4-5 above), specifically under fresh/initial-sync conditions where the engine's block-height and snapshot history is minimal.
We have not been able to pin down the exact defect in the quarter-reconstruction logic itself — that requires domain knowledge of the DIP-24 rotation scheme we don't have — but the trace above should get a maintainer to the right neighborhood quickly.
Relevant logs
Two independent occurrences (different runs, different test processes), each showing sync state immediately before the failure:
Occurrence 1 — tk_013_token_claim_pre_programmed, run at 17:40:07Z:
2026-07-20T17:40:04.634655Z INFO platform_wallet::e2e::spv: still waiting for mn-list sync elapsed=90.59384556s timeout=600s headers=Some((Synced, 1518369, 1518369)) filter_headers=Some((Syncing, 1198000, 1518369)) filters=Some((Syncing, 84000, 1518369)) mn=Some((Syncing, 0, 1518369))
2026-07-20T17:40:07.268560Z INFO dash_spv::sync::masternodes::sync_manager: Processing QRInfo message
2026-07-20T17:40:07.272676Z INFO dash_spv::sync::masternodes::sync_manager: Fed 45 block heights to engine
2026-07-20T17:40:07.597520Z ERROR dash_spv::sync::masternodes::sync_manager: QRInfo feed into engine failed: All commitment aggregated signature not valid: invalid signature
2026-07-20T17:40:07.597681Z ERROR dash_spv::sync::sync_manager: Masternode error handling message: Masternode sync failed: All commitment aggregated signature not valid: invalid signature
2026-07-20T17:40:07.597835Z INFO dash_spv::client::event_handler: SyncEvent: ManagerError(Masternode, Masternode sync failed: All commitment aggregated signature not valid: invalid signature)
2026-07-20T17:40:07.604702Z ERROR platform_wallet::e2e::spv: dash-spv reported ManagerError before mn-list synced error=Masternode manager error: Masternode sync failed: All commitment aggregated signature not valid: invalid signature elapsed=93.5639083s
thread 'cases::tk_013_token_claim_pre_programmed::tk_013_token_claim_from_pre_programmed_distribution' panicked at packages/rs-platform-wallet/tests/e2e/cases/tk_013_token_claim_pre_programmed.rs:66:50:
init e2e context: Spv("dash-spv reported ManagerError before mn-list synced: Masternode manager error: Masternode sync failed: All commitment aggregated signature not valid: invalid signature. Likely a stale workdir / testnet ChainLock cycle issue. Try wiping spv-data/ and retry, or wait 10-20 min for the next testnet ChainLock cycle.")
Occurrence 2 — tk_014_token_group_action, same run, 17:40:24Z (independent SPV client instance, same process):
2026-07-20T17:40:24.213950Z INFO dash_spv::client::event_handler: SyncProgress:
Headers: Synced 1518369/1518369 (100.0%) processed: 0, buffered: 0, last_activity: 4s
Filter Headers: Syncing 1240000/1518369 (81.7%) processed: 24000, last_activity: 0s
Filters: Syncing 79999/1518369 (5.3%) stored:84000, downloaded: 0, processed: 0, matched: 0, last_activity: 0s
Blocks: WaitForEvents last_relevant: 0 | requested: 0, from_storage: 0, downloaded: 0, processed: 0, relevant: 0, transactions: 0, last_activity: 5s
Masternodes: Syncing 0/1518369 | diffs_processed: 0, qr_infos_requested: 1, validated_cycles: 0, rotation_cycles: 0, last_activity: 4s
ChainLocks: WaitForEvents best_validated_height: 0 | valid: 0, invalid: 0, last_activity: 5s
InstantSend: WaitForEvents valid: 0, invalid: 0, pending: 0, last_activity: 5s
Mempool: WaitForEvents received: 0, relevant: 0, tracked: 0, removed: 0, last_activity: 5s
2026-07-20T17:40:24.329990Z ERROR dash_spv::sync::masternodes::sync_manager: QRInfo feed into engine failed: All commitment aggregated signature not valid: invalid signature
2026-07-20T17:40:24.330259Z INFO dash_spv::client::event_handler: SyncEvent: ManagerError(Masternode, Masternode sync failed: All commitment aggregated signature not valid: invalid signature)
thread 'cases::tk_014_token_group_action::tk_014_token_group_action_mint_co_sign' panicked at packages/rs-platform-wallet/tests/e2e/cases/tk_014_token_group_action.rs:78:50:
init e2e context: Spv("dash-spv reported ManagerError before mn-list synced: ... All commitment aggregated signature not valid: invalid signature. ...")
Note in occurrence 2: qr_infos_requested: 1, validated_cycles: 0, rotation_cycles: 0 — this is the engine's very first QRInfo, confirming the failure is specific to the initial-sync cold-start path in every observed case.
Related prior issues (not duplicates, but same subsystem)
- bug: QRInfo masternode sync fails with missing rotated chain lock sig at tip after retries #470 (closed, fixed by fix(dash): make
feed_qr_inforesilient to missing rotation CL sigs #736, already present at our pin6bb0c5f) — a differentQuorumValidationErrorvariant (RequiredRotatedChainLockSigNotPresent, "missing signature data"), with its own retry logic insync_manager.rs. Our error isAllCommitmentAggregatedSignatureNotValid— signature data is present but fails cryptographic verification — a distinct failure mode with no equivalent retry/recovery path. - dash-spv/dashcore: SML masternode-list-entry decoder fails on Dash Core 23.1.x (ExtNetInfo / extended core-P2P addresses) — "Invalid MasternodeType variant" desync #795 (open) —
MasternodeTypedecode desync on Core 23.1.x. Different mechanism (deserialization, not signature verification); not obviously related.
Suggested next steps
- Reproduce with
RUST_LOG=tracescoped todash::sml/dash_spv::sync::masternodesto capture the exact quorum hash,cycle_base_height, and per-quarter member counts feeding intoverify_aggregated_commitment_signature, and compare the reconstructed member set against the real signer set for that quorum (independently derivable from a full node / block explorer) to confirm the "wrong work block" hypothesis. - Consider whether
AllCommitmentAggregatedSignatureNotValidon a fresh engine's first QRInfo should be retried (re-fetch QRInfo from a different peer, or delay until more block/snapshot history is available) the same way fix(dash): makefeed_qr_inforesilient to missing rotation CL sigs #736 madeRequiredRotatedChainLockSigNotPresentrecoverable, rather than being immediately fatal. - Since our tests never saw
Failed to deserialize operator keywarnings, the partial-key-set (legacy/modern BLS format misdetection) path is likely not the cause — worth ruling in/out explicitly with a debug assertion or added logging.
🤖 Co-authored by Claudius the Magnificent AI Agent
Activity