Skip to content

feat(dash)!: decode and encode CbTx v4 merkleRootAssetUnlocks - #1137

Merged
xdustinface merged 1 commit into
devfrom
feat/cbtx-v4
Oct 8, 2026
Merged

xdustinface merged 1 commit into
devfrom
feat/cbtx-v4

Conversation

@ZocoLini

@ZocoLini ZocoLini commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #1129. Part of #1047.

CbTx version 4 (dashpay/dash#7639) adds merkleRootAssetUnlocks after creditPoolBalance, and every coinbase must be v4 once v24 activates. We stopped reading after asset_locked_amount, so a v4 coinbase left 32 bytes unread and misaligned the rest of the block, and re-encoding dropped the field (wrong txid and block merkle root).

Changes

  • New MerkleRootAssetUnlocks hash type.
  • CoinbasePayload::merkle_root_asset_unlocks: Option<MerkleRootAssetUnlocks>, read and written from version 4 and counted in size(). Encoding a v4 payload without it errors, like the v3 fields.
  • CURRENT_VERSION stays 3: Core rejects v4 until v24 activates.
  • Hand-written bincode impl with the derived layout, writing the new field only from version 4. The derive would add a trailing Option tag to every payload and break decoding of data persisted before (the dash-spv QRInfo fixtures failed with it).

Breaking: new public field on CoinbasePayload.

Modelling note

I'd prefer the field to be mandatory, as in Core: a plain uint256 in CCbTx that stays null unless version >= 4, where it is always serialized (all zeros when the block has no v2 unlocks). That would rule out a v4 payload without the root. It is an Option here to follow the existing pattern of the v3 fields (best_cl_height, best_cl_signature, asset_locked_amount). Changing that is better done for all of them in a separate PR.

Testing

  • Unit: size for v1-v4, v4 consensus round trip (root at the end, exact bytes consumed), v4 bincode round trip, v4 without the root fails to encode.
  • cargo test -p dashcore -p key-wallet -p key-wallet-ffi, cargo test -p dash-spv with dashd integration tests, clippy -D warnings.
  • No real v4 coinbase vector yet: our regtest dashd doesn't run v24.

🤖 Generated with Claude Code

PR Hygiene · 095cfc6

  • Bots — coderabbitai skipped after its own rate limit
  • Self-review — post /self-reviewed
  • Build green
  • Approvals
    • files with no dedicated owner (dash/src/blockdata/transaction/special_transaction/coinbase.rs, dash/src/hash_types.rs, key-wallet-ffi/src/special_payload.rs) — QuantumExplorer or xdustinface
    • key-wallet (key-wallet/src/transaction_checking/transaction_router/tests/classification.rs, key-wallet/src/transaction_checking/transaction_router/tests/coinbase.rs, key-wallet/src/wallet/managed_wallet_info/transaction_builder.rs) — QuantumExplorer or xdustinface

When every merge requirement is met, the PR Hygiene check passes. Reviewer limits do not block merging; other required GitHub checks and protections still apply.

CbTx version 4 adds `merkleRootAssetUnlocks` after `creditPoolBalance`.
The decoder stopped after `asset_locked_amount`, so a v4 coinbase left 32
bytes unread and misaligned the rest of the block, and re-encoding it
dropped the field.

`CoinbasePayload` gains `merkle_root_asset_unlocks`, read and written from
version 4 and counted in `size()`. It is an `Option` like the version 3
fields.

The bincode impl is written by hand with the derived layout, writing the
new field only from version 4, so payloads persisted before it existed
keep decoding.

Closes #1129

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 36 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Repository: dashpay/rust-dashcore/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: fcb31f12-04fb-4d82-aec1-ce9b48704ec1
📥 Commits

Reviewing files that changed from the base of the PR and between 67b307c and 095cfc6.

📒 Files selected for processing (6)
  • dash/src/blockdata/transaction/special_transaction/coinbase.rs
  • dash/src/hash_types.rs
  • key-wallet-ffi/src/special_payload.rs
  • key-wallet/src/transaction_checking/transaction_router/tests/classification.rs
  • key-wallet/src/transaction_checking/transaction_router/tests/coinbase.rs
  • key-wallet/src/wallet/managed_wallet_info/transaction_builder.rs
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@github-actions

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Waiting for bot review — coderabbitai not yet. Wait for the missing reviews, or a writer can post /skip-bots to proceed without them; blocking findings still need addressing.
Full checklist in the description.

@github-actions github-actions Bot added the waiting-bots Waiting for the review bots to report on this head label Oct 7, 2026
@codecov

codecov Bot commented Oct 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.08738% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.93%. Comparing base (67b307c) to head (095cfc6).

Files with missing lines Patch % Lines
...ckdata/transaction/special_transaction/coinbase.rs 97.02% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##              dev    #1137      +/-   ##
==========================================
+ Coverage   77.92%   77.93%   +0.01%     
==========================================
  Files         302      302              
  Lines       79108    79209     +101     
==========================================
+ Hits        61644    61733      +89     
- Misses      17464    17476      +12     
Flag Coverage Δ
core 75.10% <97.02%> (+0.13%) ⬆️
ffi 51.14% <100.00%> (-0.01%) ⬇️
rpc 48.95% <ø> (ø)
spv 92.34% <ø> (-0.06%) ⬇️
wallet 81.99% <100.00%> (+<0.01%) ⬆️
Files with missing lines Coverage Δ
dash/src/hash_types.rs 55.43% <ø> (ø)
key-wallet-ffi/src/special_payload.rs 92.25% <100.00%> (+0.02%) ⬆️
.../wallet/managed_wallet_info/transaction_builder.rs 92.73% <100.00%> (+<0.01%) ⬆️
...ckdata/transaction/special_transaction/coinbase.rs 89.88% <97.02%> (+4.76%) ⬆️

... and 7 files with indirect coverage changes

@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Bots are done — your move: post /self-reviewed.
Full checklist in the description.

@github-actions github-actions Bot added waiting-self-review Waiting for the author to post /self-reviewed bot-review-skipped A required review bot did not report; it was skipped by the window or by a person. and removed waiting-bots Waiting for the review bots to report on this head labels Oct 7, 2026
@xdustinface
xdustinface merged commit 8c85aba into dev Oct 8, 2026
38 of 39 checks passed
@xdustinface
xdustinface deleted the feat/cbtx-v4 branch October 8, 2026 04:10
ZocoLini added a commit that referenced this pull request Oct 12, 2026
…Key` and pre-v4 `CoinbasePayload` (#1149)

* fix(dash)!: restore `PlatformNodeId` and its canonical-order serialization

#1056 replaced `PlatformNodeId` with an alias of `EddsaPkHash`, which
holds the wire order. Its binary serde writes the in-memory bytes, so
`bincode::serde` started storing node IDs byte-reversed, silently
misreading records persisted before (Platform stores wallet transaction
records this way).

Restore the earlier `PlatformNodeId`, which holds the canonical order and
reverses only on the consensus wire, so JSON, binary serde, native
bincode and consensus encodings match the pre-#1056 ones again, as do
`from_byte_array`/`to_byte_array`. `EddsaPkHash` stays as the Ed25519
public key hash and converts into a `PlatformNodeId` in canonical order.

A test pins the binary serde bytes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(dash): keep the pre-v4 serde shape of `CoinbasePayload`

#1137 added `merkle_root_asset_unlocks` to the derived serde impl, which
writes it for every version, so coinbase payloads persisted through
binary serde before (Platform stores wallet transaction records this
way) no longer decode.

Write the serde impls by hand with the earlier shape, adding the field
only from version 4, as the bincode impl already does. Tests check that
a version 3 payload serializes to the same bytes and JSON as the earlier
derived impl and decodes from them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(dash)!: restore `BLSPublicKey` as a type serialized as a hex string

#1036 made `BLSPublicKey` an alias of `BlsPkBytes`, whose serde impl
writes raw bytes in binary formats, while the earlier type wrote its hex
string in every format. Records persisted through binary serde before
(Platform stores wallet transaction records this way, with operator
keys in ProRegTx and ProUpRegTx payloads) no longer decode.

`BLSPublicKey` is its own 48-byte type again, serialized as a hex string
in every format as before. Native bincode and the consensus encoding are
unchanged. It converts to and from `BlsPkBytes` for cryptographic
operations, which the quorum aggregate verification and the key-wallet
BLS derivation now do explicitly.

A test pins the binary serde bytes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(dash): reject the CbTx asset unlocks root before version 4 in serde maps

The hand-written `visit_map` accepted `merkle_root_asset_unlocks` at any
version, so a version 3 map carrying it decoded into a payload whose root
was dropped on the next encode. Read the version first and reject the
field before version 4, as `visit_seq` already ignores it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(dash): use the compact license header in platform_node_id

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot-review-skipped A required review bot did not report; it was skipped by the window or by a person. waiting-self-review Waiting for the author to post /self-reviewed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

dash: decode and encode CbTx v4 (merkleRootAssetUnlocks)

2 participants