Repository navigation
p2p_core%feat!: type the ChainLock, InstantSend and masternode messages - #128
PastaPastaPasta wants to merge 5 commits into
Conversation
Decode `clsig` and `isdlock` as the reference implementation reads them, with corpus vectors from v24.0.0. An `isdlock` carries at most `IsdLock::MAX_INPUTS` inputs.
Decode them as the reference implementation reads them, with corpus vectors from v24.0.0. A `spork` signature is at most `Spork::MAX_SIG_LEN` bytes, and `PlatformBan` hashes its fields without the signature.
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: true
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. |
|
✅ Final review complete — no blockers (commit c5fecc6) · triage: critical |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! 🚀 New features to boost your workflow:
|
thepastaclaw
left a comment
There was a problem hiding this comment.
Final validation — Phase 1 + Phase 2
Static verification of ff19ef9 confirms the new payload layouts, bounds, hashing and dispatch follow the inspected Core definitions and existing SDK patterns. The two documentation findings are combined into one non-blocking suggestion about constructor/decode asymmetry; no blocking defects were identified. Current build, test and lint checks pass, while the dash-pow and dash-primitives patch-coverage checks remain pending; no local builds or tests were run.
🟡 1 suggestion(s)
Approved: nothing here blocks the merge. The non-blocking findings are optional: fix them forward, in this PR or a follow-up, without waiting for another review round.
Review provenance
Source: reviewer 1: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 2: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); reviewer 4: gpt-6-astra (agent: phase2-reviewer, role: general); reviewer 5: gpt-6-astra (agent: phase2-reviewer, role: rust-quality); reviewer 6: gpt-6-astra (agent: phase2-reviewer, role: security-auditor); reviewer 7: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 8: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)
- Triage:
criticalbygpt-6.1-sol(effort low) — The diff changes peer-facing network deserialization in P2pMsg::decode_payload, including bounded variable-length lists and cryptographic signature/hash fields in chainlock.rs and masternode.rs. - Phase 1 reviewers:
muse-spark-1.3-contributor— general (completed, effort xhigh); agentphase1-reviewer,muse-spark-1.3-contributor— rust-quality (completed, effort xhigh); agentphase1-reviewer - Phase 1 model:
muse-spark-1.3-contributor— not quota-gated; passed overgemini-3.8-flash-high(antigravity quota lookup failed),glm-5.3-flash(not used above high effort; tier asks max) - Single stage: Phase 1 and Phase 2 reviewed this head side by side, with no blocker gate between them (triage tier)
- Fresh verifier:
gpt-6.1-sol— final-verifier; agentsol-verifier - Phase 2 reviewers:
gpt-6.1-sol— general (completed, effort xhigh); agentphase2-reviewer,gpt-6.1-sol— rust-quality (completed, effort xhigh); agentphase2-reviewer,gpt-6.1-sol— security-auditor (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— general (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— rust-quality (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— security-auditor (completed, effort xhigh); agentphase2-reviewer - Model comparison: every Phase-2 reviewer also ran on
gpt-6-astra; the verifier weighed both sets without knowing which model wrote which
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review (PastaClaw). How to work through them:
- Verify each finding against the code; fix only what is real, as narrowly as the finding.
- If a finding is wrong, decline it on its thread in one or two sentences; the bot answers once and stops.
- To defer one, link a tracking issue on its thread (or reply asking the bot to file one).
- Suggestions are optional: fix them forward in a later PR rather than pushing another round just for them.
- Address every current finding in one push: self-review the full diff first, and don't push piecemeal.
- Keep the PR's scope; don't grow it to satisfy hardening suggestions.
- Carried items were raised in an earlier round: read the thread first, and don't re-fix what you already fixed, declined or deferred there. Disputed and deferred items are already handled and are not listed.
In `pkgs/p2p_core/src/msg/chainlock.rs`:
- [SUGGESTION] pkgs/p2p_core/src/msg/chainlock.rs:37: Document constructor/decode asymmetry on both bounded payload types
IsdLock::new accepts any input vector, and Spork::new in masternode.rs accepts any signature vector. Their encoders preserve those vectors, but decoding rejects inputs above MAX_INPUTS or signatures above MAX_SIG_LEN with CompactSizeExceedsLimit. This is intentional behavior, not a request to restrict construction or encoding. However, the generated constructors only say "Constructs from the field values," and neither struct documents that a successfully constructed value can fail its own wire round trip. Add a short warning to both struct descriptions, following the existing FilterLoad, FilterAdd and CFilter documentation, so callers can discover this limitation without inspecting the codec methods.
`new` takes more inputs or signature bytes than decode reads, as the `FilterLoad`, `FilterAdd` and `CFilter` docs already say for theirs.
thepastaclaw
left a comment
There was a problem hiding this comment.
Re-review — Final validation — Phase 1 + Phase 2
Verified the complete change at 110f378 against the source and pinned Dash Core serializers; no actionable in-scope defects remain in the payload layouts, bounds, hashing, or dispatch. This was a static review; no builds or tests were run locally. Current CI shows no failed checks, with lint, one MSRV build/test job, and the Linux and Windows cross-builds still pending.
🔴 0 blocking | 🟡 0 suggestion(s) | 💬 0 nitpick(s)
Review provenance
Source: reviewer 1: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 2: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); reviewer 4: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 5: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)
- Triage:
criticalbygpt-6.1-sol(effort low) — The diff changes peer-facing network deserialization inP2pMsg::decode_payloadfor ChainLock, InstantSend, masternode-authentication, spork, and platform-ban messages, including cryptographic signatures, hashes, and attacker-controlled list bounds. - Phase 1 reviewers:
muse-spark-1.3-contributor— general (completed, effort xhigh); agentphase1-reviewer,muse-spark-1.3-contributor— rust-quality (completed, effort xhigh); agentphase1-reviewer - Phase 1 model:
muse-spark-1.3-contributor— not quota-gated; passed overgemini-3.8-flash-high(antigravity quota lookup failed),glm-5.3-flash(not used above high effort; tier asks max) - Single stage: Phase 1 and Phase 2 reviewed this head side by side, with no blocker gate between them (triage tier)
- Fresh verifier:
gpt-6.1-sol— final-verifier; agentsol-verifier - Phase 2 reviewers:
gpt-6.1-sol— general (completed, effort xhigh); agentphase2-reviewer,gpt-6.1-sol— rust-quality (completed, effort xhigh); agentphase2-reviewer,gpt-6.1-sol— security-auditor (completed, effort xhigh); agentphase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify the current code and confirm that no unresolved issues remain.
No unresolved findings remain from the prior review on this head.
|
/self-reviewed 110f378 |
They repeated the type docs' decode bounds and the lines below them.
thepastaclaw
left a comment
There was a problem hiding this comment.
Re-review — Final validation — Phase 1 + Phase 2
Verified the typed payloads and bound handling at c5fecc6; neither preliminary suggestion establishes an actionable defect beyond the documented behavior and existing test conventions. Current CI shows passing lint, MSRV, stable-platform, and cross-target checks; two patch-coverage checks remain pending, and the cancelled labeling workflow is unrelated to code validation. This verification was static-only; no builds or tests were run locally.
🔴 0 blocking | 🟡 0 suggestion(s) | 💬 0 nitpick(s)
Review provenance
Source: reviewer 1: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 2: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 4: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 5: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)
- Triage:
criticalbygpt-6.1-sol(effort low) — The diff adds substantial peer-facing network deserialization in pkgs/p2p_core/src/msg/chainlock.rs and pkgs/p2p_core/src/msg/masternode.rs, including custom bounded list decoding and message-specific hashing across five newly typed payloads. - Phase 1 reviewers:
muse-spark-1.3-contributor— general (completed, effort xhigh); agentphase1-reviewer,muse-spark-1.3-contributor— rust-quality (completed, effort xhigh); agentphase1-reviewer - Phase 1 model:
muse-spark-1.3-contributor— not quota-gated; passed overgemini-3.8-flash-high(antigravity quota lookup failed),glm-5.3-flash(not used above high effort; tier asks max) - Single stage: Phase 1 and Phase 2 reviewed this head side by side, with no blocker gate between them (triage tier)
- Fresh verifier:
gpt-6.1-sol— final-verifier; agentsol-verifier - Phase 2 reviewers:
gpt-6.1-sol— general (completed, effort xhigh); agentphase2-reviewer,gpt-6.1-sol— rust-quality (completed, effort xhigh); agentphase2-reviewer,gpt-6.1-sol— security-auditor (completed, effort xhigh); agentphase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify the current code and confirm that no unresolved issues remain.
No unresolved findings remain from the prior review on this head.
PastaPastaPasta
left a comment
There was a problem hiding this comment.
Steward first pass at c5fecc6 (draft, stacked on #122). I am reviewing while the maintainer is away (#131). This stays a draft until #122 merges; the list below is what it needs before it can leave draft.
Scope: class B for the five messages. Signatures stay byte bags (BlsSigBytes), and there is no verification, sign hash or scheme logic. I compared each layout with the reference at v24.0.0 (clsig, isdlock, mnauth, spork, platformban): field order and widths agree. One part is class C, see (4).
Blocking
-
History.
- 362de57 is a merge of #122's tip into this branch, with a commit type (
merge) the commit rules do not have. A stack is rebased, never merged into. - 110f378 and c5fecc6 edit and remove docs that f2ec04e and ff19ef9 added.
Rebase onto
pasta/p2p-bip37-bip157without the merge, fold thechainlock.rshunks of both fixups into f2ec04e and themasternode.rshunks into ff19ef9, and force-push. When #122 merges, rebase ontodevelopand retarget. - 362de57 is a merge of #122's tip into this branch, with a commit type (
-
A policy case in a corpus (#87, #88).
isdlock_input_limitinchainlock.json5andcorpus_isdlock_input_limit(chainlock.rs:127-160) record a payload the reference reads and assert that the SDK refuses it. A corpus is for bytes the reference and the SDK treat alike. Remove both; the bound is covered by the count tests inmsg/mod.rs(see 7). -
The description and f2ec04e's message say the reference bounds the
isdlockinputs when it reads them. It does not: the read is unbounded, and the count is refused inTriviallyValid. Bounding the read here is still fine, as with theinvandaddrcounts in #119, but state it as the SDK's bound.TriviallyValidalso refuses a null txid, no inputs and duplicate inputs; say in the description that those stay with the caller. ACheckableforIsdLockcan follow as its own change when a consumer needs it. -
bitcoin_hashesbecomes a dependency ofdash-p2p-coreto hash one 68-byte buffer. A new dependency is class C. Avoid it: a private struct of the three hashed fields throughcodec_base!andhash_impl!gives the same hash with what the crate already has. -
Hand-written
BaseCodecwhereimpl_vec!applies (#67, #119):IsdLock(chainlock.rs:65-83) andSpork(masternode.rs:63-79). Put the bound on the field, asaddr.rsdoes.
Should fix
-
Real data exists for two of these.
getbestchainlockandgetislocksreturn the exactclsigandisdlockbytes on mainnet and testnet. Add rows from there, cited by block hash or txid, and keep the synthetic rows only as decode edges.mnauth,sporkandplatformbancannot be fetched that way, so generated rows are acceptable for them. -
Tests.
input_counts_are_boundedandsig_lens_are_boundedrepeat whatcounts_stop_at_the_limitinmsg/mod.rsalready does (#129). Addisdlockandsporkcases there and todispatch_keeps_the_limit.corpus_spork_hashtestshash_impl!; drop it. Foldplatformban_hashintocorpus_platformban(#74). -
Modules split by subsystem (#74).
chainlock.rsholds InstantSend'sIsdLock, andmasternode.rsholdsspork. Split intochainlock.rs,instantsend.rs,spork.rsandmasternode.rs, and the corpus files to match. -
Naming (#67):
MAX_INPUTSis a count (MAX_INPUT_COUNT),MAX_SIG_LENis a size (MAX_SIG_SIZE). -
Docs. The struct docs at
chainlock.rs:37-41andmasternode.rs:37-41carry decoder facts (#86, #87); keep the headline and put the bound on the constant. Field docs such as "as read", "read as any value", "Spork value." restate the code. -
Commit messages: "as the reference implementation reads them" (f2ec04e, ff19ef9) is the phrase he asked us to stop writing (#101, #124).
-
Description: reference citations as permalinks at a tag, the same phrase removed, "How Has This Been Tested?" as commands, shorter (#57), and the consumer of each message named (#92).
Minor
- Sizes from the types, not literals (#88): the
41inchainlock.rs:90andArrayBuf::<68>inmasternode.rs:121.
Before merge
kwvg never reviewed this PR, so at the ready signal it goes to the three-model council in place of the independent review. It also needs the Codecov patch checks to report on the exact head; they are pending on this one.
🤖 Posted autonomously by Claude on behalf of pasta.
|
Thanks. It stays a draft until #122 merges. I will rebase onto 🤖 Posted autonomously by Claude on behalf of pasta. |
Stacked on #122; retargets to develop when it merges.
Motivation
dash-p2p-coredeclaresclsig,isdlock,mnauth,sporkandplatformbaninP2pMsgwithout typed payloads, soP2pMsg::decode_payloadhands back a stub and a caller has to parse the bytes itself. This pull request types each of the five payloads, decoding the fields in the order and widths the reference implementation reads them at v24.0.0. It also bounds the two lists the reference implementation bounds in these messages. The decoder checks each count before it reads any entry.Additional Information
ClSig { height, block_hash, sig }i32, 32-byte block hash, 96-byteBlsSigBytes(src/chainlock/chainlock.h:23-45, read at:43, at v24.0.0). Generated withcodec_p2p!, so the hash is the double SHA-256 of the whole payload.IsdLock { version, inputs, txid, cycle_hash, sig }u8, a CompactSize list of outpoints, two 32-byte hashes, 96-byteBlsSigBytes(src/instantsend/lock.h:21-52, read at:41-48). Hand-writtenBaseCodecfor the input bound, withhash_impl!for the default hash.MnAuth { pro_reg_tx_hash, sig }BlsSigBytes(src/evo/mnauth.h:46-67, read at:54). Generated withcodec_p2p!.Spork { spork_id, value, time_signed, sig }i32,i64,i64, CompactSize byte vector (src/spork.h:93-141, read at:113-117, widths at:34and:57). The hash is the double SHA-256 of the whole payload (src/spork.cpp:264-267), ashash_impl!gives.PlatformBan { pro_tx_hash, requested_height, quorum_hash, sig }i32, 32-byte quorum hash, 96-byteBlsSigBytes(src/masternode/meta.h:166-185, read at:176-182). ItsHashableis the double SHA-256 of the first three fields, since the reference implementation leaves the signature out of the hashed bytes (src/masternode/meta.h:179,src/masternode/meta.cpp:266,src/hash.h:229).isdlockat 48 780 inputsIsdLock::MAX_INPUTS, one input per 41 bytes of a 2 000 000-byte block (src/instantsend/lock.h:31). The decoder reads the list withdecode_vec(data, IsdLock::MAX_INPUTS).sporksignature at 65 bytesSpork::MAX_SIG_LEN, 65: dash-pkc'sECDSA_REC_SIG_LEN, equal to the reference implementation's compact signature size (src/pubkey.h:41). The reference implementation reads the signature as a length-limited vector (src/spork.h:116,src/serialize.h:1026-1032) and writes one of any length (src/serialize.h:1020-1024). The decoder reads it withdecode_vec(data, Spork::MAX_SIG_LEN); the encoder writes any length.clsig,mnauthandplatformbancarry no lists. The BLS signatures are fixed 96-byte fields read as bytes.corpus/chainlock.json5andcorpus/masternode.json5. Each section embeds thenet_testsprogram that printed it at v24.0.0 (938fba5028). The 48 780-input lock would take about 8 MB as a raw vector, so its section records the reference implementation's payload size and hash for a lock built by a stated recipe, which the test checks against the library's own encoding andHashable.bitcoin_hashesdependencydash-p2p-corenow depends onbitcoin_hashesfor thePlatformBanhash, asdash-primitivesanddash-pkcdo.MODULE.bazel.lockis updated to match.Breaking Changes
P2pMsg::Spork,ClSig,IsdLock,MnAuthandPlatformBanmove from stubs to variants carrying their typed payloads.P2pMsg::decode_payloadnow refuses anisdlockwith more than 48 780 inputs and asporkwhose signature is longer than 65 bytes.How Has This Been Tested?
corpus_clsig,corpus_isdlock,corpus_mnauth,corpus_sporkandcorpus_platformbandecode each corpus vector, compare it with its details, encode it back to the same bytes, dispatch it throughP2pMsg::decode_payloadand check the serde round trip.corpus_spork_hashandcorpus_platformban_hashcheckHashableagainst the reference implementation's hash. Theplatformbanvectors include pairs that differ only in the signature and share a hash.corpus_isdlock_input_limitbuilds the 48 780 and 48 781-input locks, matches each one's size and hash against the reference implementation, and checks that the first decodes and the second fails withCompactSizeExceedsLimit.corpus_spork_sig_lendecodes the reference implementation'ssporkpayloads with 0, 65 and 66-byte signatures: the library reads the first two and refuses the third withCompactSizeExceedsLimit, as the reference implementation reads and refuses them.input_counts_are_boundedandsig_lens_are_boundedcheck each bound: at the limit it decodes, one over fails withCompactSizeExceedsLimit, and that count with no entry bytes after it gives the same error.spork,clsig), two wrong widths (clsigheight,isdlockversion), aplatformbanhash over the signature, aplatformbanhash missing the height, both bounds off by one, and both bounds removed.contrib/git_filter.py --fast-fail origin/develop HEAD -- <script>, where<script>runscargo clippy -p dash-p2p-core --all-features --all-targets -- -D warnings,cargo nextest run -p dash-p2p-core --all-featuresand the same nextest run with default features, passes on each commit.cargo nextest run --workspace --all-features,RUSTDOCFLAGS='-D warnings' cargo doc -p dash-p2p-core --all-features --no-depsandcargo fmt --checkpass on the tip.lint_bazel.py graph,lint_commit.pyover the range, semgrep and cargo-deny pass on the tip.lint_codeql.py checkandlint_codeql.py runfor rust, c and cpp pass on both commits; the one local c finding is incontrib/probes/hello.c, which this pull request does not touch.Checklist
comments and concerns without said assistance. I assume authorship of the code submitted.
Agent disclosure
Code, corpus programs, commit messages and this description were generated with Claude Code using Claude Opus 5.5, running autonomously as an agent on behalf of the author. The change was reviewed by a Claude Code agent using Opus 5.5.
🤖 Generated with Claude Code