Skip to content

fix: preserve raw nTxType bytes on pre-DIP-0002 transactions#726

Merged
QuantumExplorer merged 1 commit intov0.42-devfrom
claude/recursing-leakey-fd3cad
May 4, 2026
Merged

fix: preserve raw nTxType bytes on pre-DIP-0002 transactions#726
QuantumExplorer merged 1 commit intov0.42-devfrom
claude/recursing-leakey-fd3cad

Conversation

@QuantumExplorer
Copy link
Copy Markdown
Member

Summary

Follow-up to #675. That PR made version == 0 transactions decode as Classic so they wouldn't be rejected on UnknownSpecialTransactionType, but it dropped the original on-wire nTxType u16. After decode, consensus_encode() and txid() re-emitted self.tx_type() as 0, so the malformed mainnet transaction no longer round-tripped and its txid changed after decode.

This PR introduces a new variant pair that preserves the raw u16:

  • TransactionType::ClassicalWithNonStandardVersionTypeBytes(u16)
  • TransactionPayload::ClassicalWithNonStandardVersionTypeBytesPayloadType(u16)

The pseudo-payload variant carries the raw bytes through the existing Option<TransactionPayload> slot on Transaction, so no new struct field is added. tx_type() derives the TransactionType variant from it via the existing from_optional_payload path.

Encoder / txid / sighash

  • (self.tx_type() as u16) is replaced with self.tx_type().to_u16() (had to drop #[repr(u16)] since the enum now has a tuple variant).
  • The encoder, txid(), and the sighash path skip the payload-section block when the payload is the new variant — pre-DIP-0002 transactions have no payload section on the wire (not even a length prefix), so emitting one would change the bytes.

Decoder

For version == 0:

  • type bytes == 0Classic (existing behavior preserved)
  • type bytes != 0ClassicalWithNonStandardVersionTypeBytes(raw) (new path)

Wallet routing

TransactionRouter::classify_transaction treats the new variant as logically Classic — falls through to the standard / coinbase / coinjoin classification.

Test plan

  • New unit test test_pre_dip2_classical_with_non_standard_version_type_bytes_roundtrip:
    • decodes a tx with version=0, nTxType=0x002A and asserts the variant is preserved
    • asserts byte-exact serialize round-trip
    • asserts txid() == Txid::hash(tx_bytes)
    • asserts the txid differs from the same tx with type bytes zeroed (proves we don't collapse different on-chain txs to one id)
  • Existing test_transaction_version still passes (version=0, type=0 still maps to plain Classic)
  • cargo test --workspace --lib — 532 dashcore + 461 key-wallet + others all pass
  • cargo clippy --workspace --all-features --all-targets -- -D warnings clean
  • cargo fmt --check clean

🤖 Generated with Claude Code

PR #675 made version=0 transactions decode as Classic, but it dropped
the original on-wire `nTxType` u16. After decode, `consensus_encode`/
`txid` re-emitted `tx_type()` as 0, breaking byte-exact round-trip and
changing the txid for the malformed mainnet transaction.

Add a `TransactionType::ClassicalWithNonStandardVersionTypeBytes(u16)`
variant (and a matching pseudo-payload `TransactionPayload::
ClassicalWithNonStandardVersionTypeBytesPayloadType(u16)`) that carry
the raw u16 read from the wire. Encoding, txid, and sighash now emit
those bytes verbatim, and the encoder skips the payload section since
pre-DIP-0002 transactions have no payload section on the wire (not even
a length prefix).

`#[repr(u16)]` is dropped from `TransactionType` since the enum is no
longer field-less; replaced `(... as u16)` casts with a new `to_u16()`
method.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 4, 2026

Warning

Rate limit exceeded

@QuantumExplorer has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 33 minutes and 6 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4a57eb1f-a2e6-4173-beac-c3f254b2b93b

📥 Commits

Reviewing files that changed from the base of the PR and between 95377d5 and 68f5593.

📒 Files selected for processing (6)
  • dash/src/blockdata/transaction/mod.rs
  • dash/src/blockdata/transaction/special_transaction/asset_unlock/unqualified_asset_unlock.rs
  • dash/src/blockdata/transaction/special_transaction/mod.rs
  • dash/src/crypto/sighash.rs
  • key-wallet/src/psbt/map/global.rs
  • key-wallet/src/transaction_checking/transaction_router/mod.rs
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/recursing-leakey-fd3cad

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
Review rate limit: 0/1 reviews remaining, refill in 33 minutes and 6 seconds.

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

@codecov
Copy link
Copy Markdown

codecov Bot commented May 4, 2026

Codecov Report

❌ Patch coverage is 78.26087% with 20 lines in your changes missing coverage. Please review.
✅ Project coverage is 70.86%. Comparing base (7dc1225) to head (68f5593).
⚠️ Report is 1 commits behind head on v0.42-dev.

Files with missing lines Patch % Lines
...c/blockdata/transaction/special_transaction/mod.rs 51.42% 17 Missing ⚠️
...src/transaction_checking/transaction_router/mod.rs 78.57% 3 Missing ⚠️
Additional details and impacted files
@@              Coverage Diff              @@
##           v0.42-dev     #726      +/-   ##
=============================================
+ Coverage      70.84%   70.86%   +0.02%     
=============================================
  Files            319      319              
  Lines          68184    68243      +59     
=============================================
+ Hits           48305    48361      +56     
- Misses         19879    19882       +3     
Flag Coverage Δ
core 75.82% <77.33%> (+0.01%) ⬆️
ffi 45.49% <ø> (ø)
rpc 20.00% <ø> (ø)
spv 87.24% <ø> (+0.05%) ⬆️
wallet 69.61% <82.35%> (+<0.01%) ⬆️
Files with missing lines Coverage Δ
dash/src/blockdata/transaction/mod.rs 86.76% <100.00%> (+0.58%) ⬆️
...ansaction/asset_unlock/unqualified_asset_unlock.rs 70.19% <100.00%> (ø)
dash/src/crypto/sighash.rs 65.61% <100.00%> (ø)
key-wallet/src/psbt/map/global.rs 51.26% <100.00%> (+0.62%) ⬆️
...src/transaction_checking/transaction_router/mod.rs 88.80% <78.57%> (-0.46%) ⬇️
...c/blockdata/transaction/special_transaction/mod.rs 38.16% <51.42%> (+2.64%) ⬆️

... and 3 files with indirect coverage changes

Copy link
Copy Markdown
Member Author

@QuantumExplorer QuantumExplorer left a comment

Choose a reason for hiding this comment

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

Self Reviewed

@QuantumExplorer QuantumExplorer merged commit d67cc03 into v0.42-dev May 4, 2026
39 checks passed
@QuantumExplorer QuantumExplorer deleted the claude/recursing-leakey-fd3cad branch May 4, 2026 20:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants