Skip to content

feat(key-wallet-ffi): one-shot transaction decode to (address, amount) structs#870

Merged
QuantumExplorer merged 2 commits into
devfrom
claude/dashcore-issue-869-97f263
Jul 12, 2026
Merged

feat(key-wallet-ffi): one-shot transaction decode to (address, amount) structs#870
QuantumExplorer merged 2 commits into
devfrom
claude/dashcore-issue-869-97f263

Conversation

@QuantumExplorer

@QuantumExplorer QuantumExplorer commented Jul 12, 2026

Copy link
Copy Markdown
Member

Closes #869

Ports the transaction-decode FFI from dashpay/platform#3981 (rs-platform-wallet-ffi/src/tx_decode.rs) into key-wallet-ffi, per the layering rule in the issue: wallet-less Core-chain FFI utilities belong next to the library that owns their types.

What's added

New module key-wallet-ffi/src/tx_decode.rs:

  • DecodedTransactionFFI { txid[32], inputs*, inputs_count, outputs*, outputs_count }
  • DecodedTxInputFFI { prev_txid[32], prev_vout, address* }
  • DecodedTxOutputFFI { address*, value_duffs, script_pubkey*, script_pubkey_len }
  • transaction_decode(tx_bytes, tx_bytes_len, network, out_decoded, error) -> bool — one-shot struct-out decode; rejects trailing bytes after a valid transaction
  • decoded_transaction_free(decoded) — releases the whole tree; safe on null

Semantics carried over unchanged from the platform implementation:

  • Output address from Address::from_script; null for non-standard scripts (OP_RETURN, bare multisig, …).
  • Input address is best-effort recovery from P2PKH-shaped scriptSigs (exactly two pushes: DER-shaped sig then 33/65-byte pubkey) and is unauthenticated — display/matching hint only; the aggressive doc warnings are kept.
  • txid / prev_txid in consensus (internal) byte order; hosts reverse for explorer display.

Adaptations to key-wallet-ffi conventions

  • Error handling: bool return + *mut FFIError out-parameter (this crate's check_ptr! / unwrap_or_return! macros) instead of the platform crate's PlatformWalletFFIResult. Deserialization failures surface as FFIErrorCode::InvalidInput via the existing From<consensus::encode::Error> mapping.
  • Network parameter: dash_network::ffi::FFINetwork.
  • Function names drop the platform_wallet_ prefix (transaction_decode, decoded_transaction_free), matching this crate's transaction_* naming. Struct names are kept verbatim (DecodedTransactionFFI, DecodedTxInputFFI, DecodedTxOutputFFI) so the Swift-wrapper migration is a re-point, not a rewrite.
  • dashcore dev-dependency gains the test-utils feature for the Address::dummy / Transaction::dummy fixtures the ported tests use.

Tests

All 13 tests from the platform PR ported: output address/value/script decoding, OP_RETURN → null address, P2PKH input-address recovery plus its negative cases (coinbase, non-P2PKH scriptSig, P2SH redeem-script collision, non-DER first push), network switching, trailing-garbage and garbage-byte rejection with out-param nulling, raw C-layout pointer walk, null/empty input rejection, and free-on-null.

  • cargo test -p key-wallet-ffi: 226 lib tests + integration tests, all passing
  • cargo clippy -p key-wallet-ffi --all-features --all-targets: clean
  • cbindgen header verified to expose the new structs and both functions

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added transaction decoding through the wallet interface.
    • Decoded transactions now include transaction IDs, inputs, outputs, values, scripts, and available addresses.
    • Added network-specific address formatting and support for identifying standard output types.
    • Added safe memory cleanup for decoded transaction data.
  • Bug Fixes

    • Invalid, empty, null, or trailing transaction data is rejected.
    • Unsupported or unauthenticated address cases are returned without an address.

…) structs

Port the transaction-decode FFI from dashpay/platform#3981
(rs-platform-wallet-ffi/src/tx_decode.rs) into key-wallet-ffi, so
wallet-less Core-chain decoding lives next to the library that owns the
types. `transaction_decode` consensus-decodes raw tx bytes into
`DecodedTransactionFFI` — per-input `(prev_txid, prev_vout, best-effort
sender address)` and per-output `(address, value_duffs, scriptPubKey)` —
in one struct-out call; `decoded_transaction_free` releases it.

Adapted from the platform version to this crate's conventions: bool
return with an `FFIError` out-parameter instead of a result struct, and
`dash_network::ffi::FFINetwork`. Struct names are kept verbatim so the
Swift wrapper migration stays a re-point.

Closes #869

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@QuantumExplorer, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 51 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: dde9e9d6-18c6-4b30-b1e4-13de5cec06d1

📥 Commits

Reviewing files that changed from the base of the PR and between bed7dba and ce8641b.

📒 Files selected for processing (2)
  • key-wallet-ffi/FFI_API.md
  • key-wallet-ffi/src/tx_decode.rs
📝 Walkthrough

Walkthrough

Adds a public FFI transaction decoder that deserializes raw Dash transactions, exposes inputs and outputs with derived addresses and scripts, and provides cleanup for allocated memory. Tests cover decoding, error handling, network rendering, and ownership behavior.

Changes

Transaction decode FFI

Layer / File(s) Summary
FFI contracts and address helpers
key-wallet-ffi/src/lib.rs, key-wallet-ffi/src/tx_decode.rs
Adds the public module, decoded transaction/input/output structs, C-string and boxed-buffer helpers, and best-effort P2PKH input address recovery.
Decode and memory lifecycle
key-wallet-ffi/src/tx_decode.rs
Adds consensus deserialization with trailing-byte rejection, transaction and script marshalling, error handling, and nested allocation cleanup.
Decode path validation
key-wallet-ffi/Cargo.toml, key-wallet-ffi/src/tx_decode.rs
Adds the Dashcore test utility dependency and tests decoding, address rendering, invalid inputs, deserialization failures, and freeing behavior.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant transaction_decode
  participant DashcoreTransaction
  participant decoded_transaction_free
  Caller->>transaction_decode: raw transaction bytes and network
  transaction_decode->>DashcoreTransaction: consensus deserialize
  DashcoreTransaction-->>transaction_decode: decoded transaction fields
  transaction_decode-->>Caller: DecodedTransactionFFI
  Caller->>decoded_transaction_free: decoded transaction pointer
  decoded_transaction_free-->>Caller: allocations released
Loading
🚥 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 clearly matches the main change: adding one-shot transaction decoding FFI to key-wallet-ffi.
Linked Issues check ✅ Passed The changes implement the requested one-shot tx_decode API, structs, null handling, byte-ordering, and free function from issue #869.
Out of Scope Changes check ✅ Passed No obvious out-of-scope changes are shown beyond the requested FFI port and its test-utils dependency.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/dashcore-issue-869-97f263

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.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 12, 2026
@codecov

codecov Bot commented Jul 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 78.59238% with 73 lines in your changes missing coverage. Please review.
✅ Project coverage is 73.91%. Comparing base (1ee1c94) to head (ce8641b).

Files with missing lines Patch % Lines
key-wallet-ffi/src/tx_decode.rs 78.59% 73 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##              dev     #870      +/-   ##
==========================================
+ Coverage   73.69%   73.91%   +0.22%     
==========================================
  Files         324      325       +1     
  Lines       73434    73775     +341     
==========================================
+ Hits        54117    54534     +417     
+ Misses      19317    19241      -76     
Flag Coverage Δ
core 77.08% <ø> (ø)
ffi 49.14% <78.59%> (+2.70%) ⬆️
rpc 20.00% <ø> (ø)
spv 90.87% <ø> (-0.04%) ⬇️
wallet 73.25% <ø> (ø)
Files with missing lines Coverage Δ
key-wallet-ffi/src/lib.rs 100.00% <ø> (+50.00%) ⬆️
key-wallet-ffi/src/tx_decode.rs 78.59% <78.59%> (ø)

... and 16 files with indirect coverage changes

Pre-commit fixes: regenerate FFI_API.md for the new transaction_decode /
decoded_transaction_free entries, and "unparseable" -> "unparsable" per
the typos hook.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@QuantumExplorer

Copy link
Copy Markdown
Member Author

Reviewed

@QuantumExplorer QuantumExplorer merged commit d4692bb into dev Jul 12, 2026
36 checks passed
@QuantumExplorer QuantumExplorer deleted the claude/dashcore-issue-869-97f263 branch July 12, 2026 08:04
QuantumExplorer added a commit to dashpay/platform that referenced this pull request Jul 12, 2026
…let-ffi

The transaction-decode FFI added by this PR was upstreamed into
rust-dashcore's key-wallet-ffi (dashpay/rust-dashcore#870), where it
belongs: it is pure Core-chain functionality with no platform-wallet
involvement. Bump the rust-dashcore pin to that merge commit (one
commit, additive only), delete the platform-side copy from
rs-platform-wallet-ffi, and re-point the thin Swift wrapper at
key-wallet-ffi's transaction_decode / decoded_transaction_free using
the FFIError + bool house convention.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

key-wallet-ffi: one-shot transaction decode to (address, amount) structs — upstream tx_decode from platform#3981

1 participant