feat: validate masternode list merkle root#799
Conversation
Recompute the masternode-list Merkle root over the fully assembled list right after it is built from an `MnListDiff` and hard-reject any diff whose recomputed root does not match the `merkleRootMNList` committed in the diff's coinbase `cbTx`. The list is never advanced on mismatch. `MasternodeList::validate_mn_list_root` recomputes via the existing `calculate_masternodes_merkle_root` machinery (full list sorted by raw `proRegTxHash`, `SER_GETHASH` entry hash) and compares it to the coinbase root extracted by `coinbase_mn_list_root`. It replaces the previously-unused `has_valid_mn_list_root`, which only compared against a stored root rather than recomputing. It is invoked from both production build paths: `MasternodeList::try_from_with_block_hash_lookup` (`from_diff`) and `MasternodeList::apply_diff`, so the new `SmlError::MasternodeListRootMismatch` (carrying expected coinbase root, recomputed root, and block height/hash) propagates through `MasternodeListEngine::apply_diff` and `feed_qr_info` up to `dash-spv` sync without being swallowed. `from_diff` no longer trusts the bogus `merkle_hashes.first()` value and instead computes the root via the builder. The historical capture fixtures predate root validation and commit coinbase roots over full mainnet lists their captured `new_masternodes` subset does not reproduce. The entry hash and Merkle ordering were verified byte-identical to Dash Core's `CSimplifiedMNListEntry::CalcHash` and `CSimplifiedMNList::CalcMerkleRoot` over all 3147 entries of the genesis fixture, so the discrepancy is in the fixture data, not the logic. Test-only helpers rewrite each fixture diff's coinbase root to the value the assembled list produces so the unrelated quorum and chainlock tests stay meaningful, and new tests assert the hard-reject path.
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## fix/sml-entry-hash-gethash #799 +/- ##
==============================================================
- Coverage 72.70% 72.62% -0.09%
==============================================================
Files 322 322
Lines 71402 71200 -202
==============================================================
- Hits 51913 51706 -207
- Misses 19489 19494 +5
|
…t in
dashRecompute the masternode-list Merkle root over the fully assembled list right after it is built from an
MnListDiffand hard-reject any diff whose recomputed root does not match themerkleRootMNListcommitted in the diff's coinbasecbTx. The list is never advanced on mismatch.MasternodeList::validate_mn_list_rootrecomputes via the existingcalculate_masternodes_merkle_rootmachinery (full list sorted by rawproRegTxHash,SER_GETHASHentry hash) and compares it to the coinbase root extracted bycoinbase_mn_list_root. It replaces the previously-unusedhas_valid_mn_list_root, which only compared against a stored root rather than recomputing. It is invoked from both production build paths:MasternodeList::try_from_with_block_hash_lookup(from_diff) andMasternodeList::apply_diff, so the newSmlError::MasternodeListRootMismatch(carrying expected coinbase root, recomputed root, and block height/hash) propagates throughMasternodeListEngine::apply_diffandfeed_qr_infoup todash-spvsync without being swallowed.from_diffno longer trusts the bogusmerkle_hashes.first()value and instead computes the root via the builder.The historical capture fixtures predate root validation and commit coinbase roots over full mainnet lists their captured
new_masternodessubset does not reproduce. The entry hash and Merkle ordering were verified byte-identical to Dash Core'sCSimplifiedMNListEntry::CalcHashandCSimplifiedMNList::CalcMerkleRootover all 3147 entries of the genesis fixture, so the discrepancy is in the fixture data, not the logic. Test-only helpers rewrite each fixture diff's coinbase root to the value the assembled list produces so the unrelated quorum and chainlock tests stay meaningful, and new tests assert the hard-reject path.Based on:
SER_GETHASHsemantics indash#798