Skip to content

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

Description

@QuantumExplorer

Context

dashpay/platform#3981 adds a one-shot transaction-decode FFI (platform_wallet_decode_transaction) to rs-platform-wallet-ffi: consensus-decode raw tx bytes into per-input/per-output C structs with derived addresses, so host apps (Swift today) can reconstruct (address, amount) pairs from stored raw bytes — e.g. to match arbitrary transactions against the CrowdNode on-chain API ("did this tx pay amount X to address Y?").

That function is pure Core-chain functionality: its entire vocabulary is consensus::deserialize, Address::from_script, scriptPubKey classification, and txid byte order. It takes no wallet handle and touches no Platform state. It only lives in the platform repo because that was the crate the feature shipped from. The right home is rust-dashcore, next to the library that owns the types.

Current gap in key-wallet-ffi

key-wallet-ffi already exposes transaction_deserialize(bytes) -> FFITransaction*, but FFITransaction is an opaque handle whose full API is builder/signing-oriented (transaction_create, add_input, add_output, get_txid, serialize, sighash, sign_input, destroy). There are no read accessors and no script→address helper, so an FFI consumer can deserialize a transaction and then cannot look inside it.

Proposal

Port the API from dashpay/platform#3981 (see packages/rs-platform-wallet-ffi/src/tx_decode.rs) into key-wallet-ffi, essentially verbatim:

  • 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 }
  • tx_decode(tx_bytes, len, network, out_decoded) -> result — one-shot struct-out call; rejects trailing bytes
  • decoded_transaction_free(...)

Deliberately not piecemeal accessors on FFITransaction (get_output_count, get_output_at, …): the one-shot struct-out shape is a less chatty, less error-prone FFI surface, and it is what the platform PR already validated.

Notes carried over from the platform implementation:

  • Output address comes from Address::from_script; null for non-standard scripts (OP_RETURN, bare multisig, …).
  • Input address is a 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, never authorization. Keep the aggressive doc warnings; centralizing this heuristic with its warning label beats having every host app reimplement it.
  • txid / prev_txid in consensus (internal) byte order; hosts reverse for explorer display.

Migration plan

Once this lands and dashpay/platform bumps its rust-dashcore pin (routine), delete tx_decode.rs from rs-platform-wallet-ffi and re-point the thin Swift wrapper — everything ships in the same xcframework header namespace, so the host-side change is minimal.

The layering rule this establishes: FFI functions live next to the library that owns their types — wallet-less Core-chain utilities in rust-dashcore FFI crates; anything touching a platform-wallet handle or Platform state in rs-platform-wallet-ffi.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions