Skip to content

fix: compute SML entry hash with SER_GETHASH semantics in dash#798

Open
xdustinface wants to merge 1 commit into
devfrom
fix/sml-entry-hash-gethash
Open

fix: compute SML entry hash with SER_GETHASH semantics in dash#798
xdustinface wants to merge 1 commit into
devfrom
fix/sml-entry-hash-gethash

Conversation

@xdustinface
Copy link
Copy Markdown
Collaborator

@xdustinface xdustinface commented Jun 2, 2026

MasternodeListEntry::calculate_entry_hash hashed the full network consensus serialization, including the leading version. Dash Core's CSimplifiedMNListEntry::CalcHash uses CHashWriter(SER_GETHASH, ...), and SER_GETHASH does not set SER_NETWORK, so the SER_NETWORK-gated leading nVersion is omitted from the hash pre-image. The two contexts therefore diverged for every entry.

Extract the post-version body of the wire encoder into a shared consensus_encode_body helper. consensus_encode writes version then the body (byte-identical to before), while calculate_entry_hash hashes the body alone, matching Core's CalcHash. Verified against Core for both a version 1 and a version 2 Evo entry.

Also fix SocketAddr decoding to use to_ipv4_mapped instead of to_ipv4, so an all-zero (::) service address round-trips to the same 16 bytes instead of acquiring an ::ffff: mapped prefix. The prefix corruption changed the wire bytes and thus the entry hash for masternodes with an unset service.

Summary by CodeRabbit

Bug Fixes

  • Fixed IPv6 address consensus encoding to preserve address format correctly
  • Corrected masternode entry hash calculation for improved protocol compatibility

Tests

  • Added regression tests for IPv6 address handling and masternode list encoding validation

`MasternodeListEntry::calculate_entry_hash` hashed the full network consensus serialization, including the leading `version`. Dash Core's `CSimplifiedMNListEntry::CalcHash` uses `CHashWriter(SER_GETHASH, ...)`, and `SER_GETHASH` does not set `SER_NETWORK`, so the `SER_NETWORK`-gated leading `nVersion` is omitted from the hash pre-image. The two contexts therefore diverged for every entry.

Extract the post-`version` body of the wire encoder into a shared `consensus_encode_body` helper. `consensus_encode` writes `version` then the body (byte-identical to before), while `calculate_entry_hash` hashes the body alone, matching Core's `CalcHash`. Verified against Core for both a `version` 1 and a `version` 2 Evo entry.

Also fix `SocketAddr` decoding to use `to_ipv4_mapped` instead of `to_ipv4`, so an all-zero (`::`) service address round-trips to the same 16 bytes instead of acquiring an `::ffff:` mapped prefix. The prefix corruption changed the wire bytes and thus the entry hash for masternodes with an unset service.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 2, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

The PR refactors MasternodeListEntry consensus encoding to separate version-dependent logic from body serialization, updates hash calculation to use the new body-only preimage, and corrects IPv6 address decoding to properly handle IPv4-mapped addresses.

Changes

Consensus Encoding and Hashing Alignment

Layer / File(s) Summary
MasternodeListEntry encoding refactor: version and body separation
dash/src/sml/masternode_list_entry/mod.rs
A new consensus_encode_body inherent method encodes all fields after version (including conditional mn_type for version >= 2), and consensus_encode delegates body encoding to this helper after encoding version.
Hash calculation: use body encoding as preimage
dash/src/sml/masternode_list_entry/hash.rs
calculate_entry_hash now uses consensus_encode_body to build the hash preimage; a fixture-driven test validates computed hashes match Dash Core ground-truth values.
SocketAddr decoding: IPv4-mapped IPv6 detection
dash/src/sml/address.rs
Decoding now uses to_ipv4_mapped() to detect IPv4 addresses; a regression test confirms all-zero IPv6 addresses preserve their exact byte representation across decode-encode cycles.

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

ready-for-review

Suggested reviewers

  • QuantumExplorer
  • ZocoLini

Poem

🐰 A rabbit's ode to cleaner code:

Split the version from the rest,
Hash the body—oh, what's best!
IPv6 now maps just right,
Consensus bytes shining bright.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main objective: fixing the computation of SML entry hash to use SER_GETHASH semantics, which is the core change across all modified files.
Docstring Coverage ✅ Passed Docstring coverage is 90.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/sml-entry-hash-gethash

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
dash/src/sml/masternode_list_entry/hash.rs (1)

6-10: 💤 Low value

Consider returning Result or documenting infallibility.

The expect("encoding failed") violates the guideline to avoid expect() in library code. However, encoding to a Vec<u8> is practically infallible since Vec's Write impl only fails on allocation exhaustion (which would panic anyway).

Two options:

  1. Keep as-is but add a comment explaining why failure is impossible
  2. Change signature to return Result<sha256d::Hash, std::io::Error> for API consistency

Given that this is a public method and allocation failure would abort regardless, option 1 is reasonable here.

📝 Suggested documentation
 impl MasternodeListEntry {
     pub fn calculate_entry_hash(&self) -> sha256d::Hash {
+        // Vec<u8> write is infallible (barring OOM which aborts), so expect is safe here.
         let mut writer = Vec::new();
         self.consensus_encode_body(&mut writer).expect("encoding failed");
         sha256d::Hash::hash(&writer)
     }
 }

As per coding guidelines: "Avoid unwrap() and expect() in library code; use proper error types".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dash/src/sml/masternode_list_entry/hash.rs` around lines 6 - 10, The method
calculate_entry_hash currently calls self.consensus_encode_body(&mut
writer).expect("encoding failed"), which uses expect in library code; replace
this by keeping the current behavior but add a concise comment explaining why
encoding to a Vec<u8> is effectively infallible (the Write impl for Vec only
fails on allocation exhaustion which would abort) and that returning a Result
was considered but omitted for API stability; reference the calculate_entry_hash
and consensus_encode_body symbols and state that sha256d::Hash::hash(&writer)
remains unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@dash/src/sml/masternode_list_entry/hash.rs`:
- Around line 6-10: The method calculate_entry_hash currently calls
self.consensus_encode_body(&mut writer).expect("encoding failed"), which uses
expect in library code; replace this by keeping the current behavior but add a
concise comment explaining why encoding to a Vec<u8> is effectively infallible
(the Write impl for Vec only fails on allocation exhaustion which would abort)
and that returning a Result was considered but omitted for API stability;
reference the calculate_entry_hash and consensus_encode_body symbols and state
that sha256d::Hash::hash(&writer) remains unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: fa716e82-9a61-4a6f-9dd8-5f0fb0b7d223

📥 Commits

Reviewing files that changed from the base of the PR and between a132945 and 458894f.

📒 Files selected for processing (3)
  • dash/src/sml/address.rs
  • dash/src/sml/masternode_list_entry/hash.rs
  • dash/src/sml/masternode_list_entry/mod.rs

@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 2, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 72.70%. Comparing base (3d0d5dc) to head (458894f).
⚠️ Report is 2 commits behind head on dev.

Additional details and impacted files
@@            Coverage Diff             @@
##              dev     #798      +/-   ##
==========================================
+ Coverage   72.55%   72.70%   +0.15%     
==========================================
  Files         322      322              
  Lines       71006    71402     +396     
==========================================
+ Hits        51518    51913     +395     
- Misses      19488    19489       +1     
Flag Coverage Δ
core 76.58% <100.00%> (+0.11%) ⬆️
ffi 46.42% <ø> (ø)
rpc 20.00% <ø> (ø)
spv 90.34% <ø> (+0.11%) ⬆️
wallet 71.29% <ø> (ø)
Files with missing lines Coverage Δ
dash/src/sml/address.rs 100.00% <100.00%> (ø)
dash/src/sml/masternode_list_entry/hash.rs 100.00% <100.00%> (ø)
dash/src/sml/masternode_list_entry/mod.rs 83.33% <100.00%> (+1.75%) ⬆️

... and 11 files with indirect coverage changes

@github-actions github-actions Bot added the ready-for-review CodeRabbit has approved this PR label Jun 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-review CodeRabbit has approved this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant