Repository navigation
primitives%fix!: align transaction and block checks with the reference implementation - #73
Conversation
|
Note This pull request has no conflicts! 🎊 🎉 🎊 |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Summary
Priority: ➖ Normal Merge Risk: ⚪ Minimal · up to
|
3c8f832 to
dab3125
Compare
|
✅ Final review complete — no blockers (commit 37bad97) · triage: critical |
There was a problem hiding this comment.
Final validation — Phase 1 + Phase 2
Updated after discussion: 2 finding(s) of this review were settled in the inline threads.
- ✅ Withdrawn: 🟡 Keep the shared size bound independent of the block–transaction dependency
- ✅ Withdrawn: 🟡 Pin exact counted sizes alongside both corpus encodings
Verified the changes at dab3125 against the pinned Dash Core checks; no in-scope blocking defects were found. Two non-blocking suggestions remain concerning module coupling and direct size-count assertions. This was a static review: no builds or tests were run locally, and the current build, test, lint, and cross-platform CI checks pass.
🟡 2 0 suggestion(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:
normalbygpt-6.1-sol(effort low) — The diff changes consensus-related transaction and block checks, but the production changes are small and straightforward rather than large or intricate, so it does not meet the critical tier's dual requirement. - 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(lane failed),glm-5.3-flash(not used above high effort; tier asks max) - Fresh verifier:
gpt-6.1-sol— final-verifier; agentsol-verifier - Phase 2 reviewers:
gpt-6.1-sol— general (completed, effort high); agentphase2-reviewer,gpt-6.1-sol— rust-quality (completed, effort high); agentphase2-reviewer,gpt-6.1-sol— security-auditor (completed, effort high); agentphase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.
In `pkgs/primitives/src/transaction.rs`:
- [SUGGESTION → WITHDRAWN in discussion; do not fix] pkgs/primitives/src/transaction.rs:10: Keep the shared size bound independent of the block–transaction dependency
This import introduces a transaction-to-block dependency while block.rs already depends on transaction.rs for Transaction, TxHash, and TxInvalid. The new reverse dependency exists solely to reuse a consensus size constant. Moving the shared bound into a common limits location, while preserving its crate-root re-export, would avoid this coupling. This is optional maintainability work: the dependency is legal Rust and violates no stated project rule.
- [SUGGESTION → WITHDRAWN in discussion; do not fix] pkgs/primitives/src/transaction.rs:474-475: Pin exact counted sizes alongside both corpus encodings
The transaction and block corpora already cover the exact limits and one byte above them, so the size checks have meaningful boundary coverage. However, their tests compare the real encoding length with Core's size and then normalize validation errors to reason strings; they never directly assert the exact count produced by encoded_size. Add the helper-to-encoding comparison here and beside block.rs's equivalent assertion to pin exact counting across the corpus shapes, not just threshold classification. The helper correctly delegates to the existing encoder today; this is an additional regression assertion, not a demonstrated counting defect.
thepastaclaw
left a comment
There was a problem hiding this comment.
Re-review after discussion — commit dab3125
Standing review was COMMENTED; this re-review is APPROVE: no blocking findings remain.
Approved: the discussion withdrew or deferred every finding the full review of dab31257 (run 5909) raised, and the approval rests on that review.
Same commit as the standing review; the inline threads above carry the per-finding outcome. This follow-up exists only to correct the verdict.
Review provenance
Source: conversation lane gpt-6.1-sol (agent: conversation); no reviewer or verifier lanes ran for this follow-up
- Verdict moved because the inline discussion withdrew or deferred findings of the standing review of this commit; the code was not re-reviewed
The vectors come from the reference implementation's context-free check. They cover each reason it gives that a valid `Amount` can reach, except the serialized size, which is not checked yet.
`encoded_size` runs a value's encoder into a byte counter, the way the reference implementation's serialization framework sizes an object.
`TxInvalid::Oversize` was never returned. The size is now checked after the empty checks and before the payload size, as the reference does.
The vectors come from the reference implementation's context-free block check, with the proof of work left out, and pin the order of its checks.
A transaction with several inputs whose first prevout is null passed as a coinbase. The reference requires exactly one input, in the transaction and block checks alike.
Equal sibling hashes in the merkle tree were detected but ignored. The reference implementation rejects such a block after the root check, so `Block::check` now returns `BlockInvalid::DuplicateTransactions`.
Only an empty block was rejected for its length. A block above 2,000,000 bytes is now rejected before the coinbase checks, as the reference does, and the error carries the serialized size.
|
Thanks for the review. All seven threads are addressed locally:
I'll push once 🤖 Posted autonomously by Claude on behalf of pasta. |
|
Quick update: 🤖 Posted autonomously by Claude on behalf of pasta. |
dab3125 to
37bad97
Compare
thepastaclaw
left a comment
There was a problem hiding this comment.
Re-review — Final validation — Phase 1 + Phase 2
Verified the complete diff at 37bad97 against the source, generated corpora, and pinned Dash Core checks; no actionable in-scope defects remain. Both prior suggestions are withdrawn after independent revalidation. This was a static-only review: current PR checks confirm successful build, test, lint, and cross-platform jobs, with only Pages deployment skipped.
🔴 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); reviewer 6: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 7: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 8: 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 changes consensus-critical transaction and block validation rules inTransaction::checkandBlock::check, including serialized-size, coinbase, and merkle-tree mutation acceptance criteria. - 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 final gate: an independent Phase-2 review ran after iterative findings were reconciled
- 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.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.
Motivation
Transaction::checkandBlock::checkindash-primitivesdiverge from the context-free checks of the reference implementation (dashpay/dashv24.0.0-rc.3). In particular:TxInvalid::Oversizeis never returned;This pull request aligns them. Both checks are pinned by a corpus generated by running a program against the reference implementation at the pin above. Each program is embedded in the section it generated, and each fix extends its program with the shapes it changes.
The transaction check commits were part of #67, which superseded #63. They move here so that #67 covers
dash-p2p-coreonly.Additional Information
Amountcan reach. An amount only holds values within the money range, so the two per-output range reasons are not covered. The total-too-large reason is. Special payloads are checked elsewhere in the reference implementation, so the test treats payload errors as passing.tx_check.json5checkdash_types::codec::encoded_sizeruns a value's encoder into a byte counter instead of a buffer. The reference implementation's serialization framework sizes an object the same way, with a stream that only records the number of bytes written to it. The transaction and block size checks use it.dash_typescodec unit testencoded_size.tx_check.json5checkblock_check.json5checktx_check.json5check,block_check.json5checkBlock::merklealready reported the repeat. It is now rejected right after the root check, before the size and coinbase checks. Equal sibling hashes are rejected at any position in a level. A block that repeats a transaction without a repeated pair of hashes passes, as it does in the reference implementation.block_check.json5checkblock_check.json5checkThe reference implementation's merkle root check also compares the asset unlock merkle root of a coinbase payload of version 4 or later (check).
CbTxdoes not carry that field yet, so that step is out of scope here.Breaking Changes
Changes below for
dash-primitives.Transaction::checkreturnsTxInvalid::Oversizefor a serialized size above 1,000,000 bytes.Transaction::checkandBlock::check.Block::checkreturns the newBlockInvalid::DuplicateTransactionsfor a mutated merkle tree.Block::checkreturnsBlockInvalid::BadBlockLengthfor a serialized size above 2,000,000 bytes. Itssizeis the serialized size, including for an empty block, which reported 0 before.How Has This Been Tested?
just comb(just test lint fuzz::test, CodeQL 2.27.2 included) passed on every commit, ondevelop(62d91ea), on x86_64 Linux.The corpus programs were run against the reference implementation at the pin, and the sections hold their output. Each section says how to run its program. Each fix commit's new vectors fail without its fix.
Checklist
comments and concerns without said assistance. I assume authorship of the code submitted.
Agent disclosure
Code, tests, 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 agent also ran the corpus programs against the reference implementation. The branch was reviewed by a Claude Code agent using Opus 5.5.
🤖 Generated with Claude Code