Skip to content

chore(kotlin-sdk): tx-decode follow-up — blob hardening, net_from_ord hoist, prevVout docs (#4187 review)#4192

Merged
shumkov merged 4 commits into
dashpay:v4.1-devfrom
bfoss765:followup/v4.1/tx-decode-polish
Jul 22, 2026
Merged

chore(kotlin-sdk): tx-decode follow-up — blob hardening, net_from_ord hoist, prevVout docs (#4187 review)#4192
shumkov merged 4 commits into
dashpay:v4.1-devfrom
bfoss765:followup/v4.1/tx-decode-polish

Conversation

@bfoss765

@bfoss765 bfoss765 commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Small follow-up promised on #4187, addressing @shumkov's three post-approval review notes on the Kotlin tx-decode JNI binding:

  1. Blob hardening (rs-unified-sdk-jni/src/tx_decode.rs) — the output encoder wrote script_pubkey_len unconditionally but the script bytes only when the pointer was non-null, so a defensively-null pointer with a nonzero length field could shift every subsequent field in the blob. Length and bytes are now written together: a null (or empty) script encodes as length 0, never a nonzero length without its bytes. Pinned by a new unit test with a hand-built null-script fixture whose length field deliberately lies. The cross-pinned Kotlin/Rust fixture blob is unchanged.
  2. net_from_ord deduplication — tx_decode.rs had added the third private copy (after transactions.rs and identity.rs). There is now a single shared support::net_from_ord; rs_sdk_ffi::FFINetwork and platform_wallet_ffi::FFINetwork are both re-exports of dash_network::ffi::FFINetwork, so the one helper serves every module. A fourth identical copy in persistence.rs was consolidated too. The exhaustive ordinal test moved to support.rs.
  3. prevVout signedness — documented rather than retyped: DecodedTransaction.Input.prevVout stays a signed Int carrying the raw u32 bits (the coinbase sentinel 0xFFFFFFFF reads as -1; Swift's UInt32 shows 4294967295), with KDoc spelling that out and a new prevVoutUnsigned: Long convenience view for the numeric u32 / Swift-parity rendering. The existing pinned -1 test is untouched; assertions for the unsigned view were added alongside it.

Verified: cargo test -p rs-unified-sdk-jni (37 pass), cargo clippy -p rs-unified-sdk-jni --all-targets clean, and kotlin-sdk :sdk:compileDebugKotlin + :sdk:testDebugUnitTest green (TransactionDecoderTest 4/4). Each commit builds and tests green standalone.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added an unsigned view of transaction input output indexes, supporting the full 32-bit range and clearly representing coinbase inputs.
  • Bug Fixes

    • Improved transaction encoding when input or output data is unavailable or contains inconsistent lengths, preventing malformed data from causing decoding issues.
    • Standardized network handling across SDK operations, including safe fallback behavior for unknown network values.
  • Documentation

    • Clarified how signed and unsigned transaction output indexes are represented.

bfoss765 and others added 4 commits July 21, 2026 16:10
Follow-up to dashpay#4187 review: tx_decode.rs added the third private copy of
the FFINetwork ordinal mapping (after transactions.rs and identity.rs).
Hoist a single shared copy into support.rs and update all callers —
including a fourth copy in persistence.rs with identical semantics.
rs_sdk_ffi::FFINetwork and platform_wallet_ffi::FFINetwork are both
re-exports of dash_network::ffi::FFINetwork, so the one helper serves
every module. The exhaustive ordinal test moves to support.rs with it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Follow-up to dashpay#4187 review: the output encoder always wrote
script_pubkey_len but only appended the bytes when the pointer was
non-null, so a defensively-null script pointer paired with a nonzero
length field would shift every subsequent field in the blob. Write the
length and bytes together — a null (or empty) script encodes as length
0 — and pin the hazard with a hand-built null-script fixture whose
length field deliberately lies.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…gned view

Follow-up to dashpay#4187 review: Kotlin exposes prevVout as a signed Int, so
the coinbase sentinel 0xFFFFFFFF reads as -1 while the Swift wrapper's
UInt32 shows 4294967295. Keep the field type and the pinned -1 test
unchanged (raw u32 bits preserved bit-for-bit) and say so in KDoc, and
add a prevVoutUnsigned: Long convenience view (0..4294967295) for
callers who want the numeric u32 / Swift-parity rendering.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review round 2: the array-level writes had the same half-defensive shape
the script fix closed — counts written unconditionally, records only if
the pointer was non-null, so a lying (null array, nonzero count) would
desync every field parseBlob reads after. Counts and records now write
together, pinned by a lying-fixture test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions github-actions Bot added this to the v4.1.0 milestone Jul 21, 2026
@thepastaclaw

thepastaclaw commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

🕓 Ready for review — 30 ahead in queue (commit d6d0313)
Queue position: 31/72 · 2 reviews active
ETA: start ~02:55 UTC · complete ~03:23 UTC (median 28m across 30 recent reviews; 2 slots)
Queued 22h 42m ago · Last checked: 2026-07-22 19:40 UTC

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: dd9b927a-50c7-47a5-b870-947ae49a8e90

📥 Commits

Reviewing files that changed from the base of the PR and between 5b8dbf4 and d6d0313.

📒 Files selected for processing (7)
  • packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/keywallet/TransactionDecoder.kt
  • packages/kotlin-sdk/sdk/src/test/kotlin/org/dashfoundation/dashsdk/keywallet/TransactionDecoderTest.kt
  • packages/rs-unified-sdk-jni/src/identity.rs
  • packages/rs-unified-sdk-jni/src/persistence.rs
  • packages/rs-unified-sdk-jni/src/support.rs
  • packages/rs-unified-sdk-jni/src/transactions.rs
  • packages/rs-unified-sdk-jni/src/tx_decode.rs

📝 Walkthrough

Walkthrough

The JNI SDK centralizes network ordinal conversion and hardens packed transaction encoding for null pointers. The Kotlin SDK documents signed prevVout preservation and adds an unsigned 32-bit accessor with fixture and coinbase coverage.

Changes

JNI transaction updates

Layer / File(s) Summary
Shared JNI network mapping
packages/rs-unified-sdk-jni/src/support.rs, packages/rs-unified-sdk-jni/src/{identity,persistence,transactions,tx_decode}.rs
Adds shared ordinal-to-FFINetwork conversion with a Testnet fallback, updates JNI imports and removes duplicated local helpers, and validates the mappings.
Defensive transaction encoding
packages/rs-unified-sdk-jni/src/tx_decode.rs
Encodes zero counts or script lengths when decoded pointers or script data are absent, updates the packed-layout documentation, and adds null-pointer encoding tests.

Kotlin unsigned transaction index

Layer / File(s) Summary
Unsigned previous-output index contract
packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/keywallet/TransactionDecoder.kt, packages/kotlin-sdk/sdk/src/test/kotlin/org/dashfoundation/dashsdk/keywallet/TransactionDecoderTest.kt
Documents bit-preserving signed prevVout behavior, adds prevVoutUnsigned, and tests regular and coinbase representations.

Estimated code review effort: 2 (Simple) | ~15 minutes

Suggested labels: Client Only

Suggested reviewers: quantumexplorer, bezibalazs, shumkov

🚥 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 accurately summarizes the main follow-up changes: blob hardening, shared net_from_ord, and prevVout documentation/unsigned view.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@shumkov

shumkov commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

Reviewed (two independent passes) — all three #4187 follow-up items are done completely and correctly: the null-pointer/length-lie hazards are guarded at both the script and array levels with tests that hand-build lying FFI structs, the net_from_ord hoist actually removed a fourth private copy (persistence.rs) beyond the three flagged, with ordinal semantics preserved and test-pinned, and prevVoutUnsigned documents and widens the signed-bits contract properly. The fixture cross-pin is untouched, which is correct — real decode output can never produce the null-with-nonzero-length state. Nothing to add; ready.

@shumkov
shumkov merged commit 267e1cb into dashpay:v4.1-dev Jul 22, 2026
20 of 22 checks passed
QuantumExplorer added a commit that referenced this pull request Jul 22, 2026
The tx-decode follow-up (#4192) merged rustfmt-drifted code: the crate is
absent from the rs package filter, so its own push run never executed the
workspace formatting check, while every unrelated PR that does trigger the
Rust workspace job now fails cargo fmt --check --all on its merge ref.
Formatting-only, no semantic change.

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.

3 participants