Motivation
When a wallet's provider owner/voting key matches a ProRegTx during compact-filter sync (works since #863/#864, part of #861), consumers need the transaction's special payload data — most importantly the masternode's service_address (IP:port), but also proTxHash linkage, collateral outpoint, and the owner/voting/operator keys — to display anything useful about the masternode ("this key was used by the masternode at 54.148.58.128:9999"). The legacy DashSync SDK exposed this via DSLocalMasternode (built from DSProviderRegistrationTransaction.ipAddress/port); the Rust SDK currently cannot.
Current state
FFITransactionRecord (key-wallet-ffi/src/managed_account.rs) has no typed payload field. The only way to recover payload data is to consensus-decode the raw tx_data bytes on the consumer side — verified working (the service IP sits in those bytes), but it forces every binding (Swift/Kotlin) to reimplement DIP-2/DIP-3 payload parsing.
Worse, every other FFI conversion path explicitly drops the payload when reconstructing a transaction:
key-wallet-ffi/src/transaction.rs:280 — special_transaction_payload: None
key-wallet-ffi/src/tx_decode.rs:361 — special_transaction_payload: None
key-wallet-ffi/src/managed_account.rs:2350 — special_transaction_payload: None
And with keep-finalized-transactions off (the default in mobile builds), a chainlocked-at-first-sight historical ProRegTx's full record is dropped right after processing (managed_core_keys_account.rs, drop_finalized_transaction), so even the raw-bytes escape hatch disappears for the very transactions masternode-key discovery cares about.
Proposal
Add a typed, FFI-safe specialized-payload representation to FFITransactionRecord:
- A tagged union (e.g.
FFISpecialTransactionPayload with a payload_type discriminant matching the DIP-2 tx types) covering at least:
ProviderRegistration: version, masternode type, collateral outpoint, service address (string form + raw ip/port), owner/voting key hashes, operator pubkey, operator reward, payout script, platform node id/ports
ProviderUpdateService: proTxHash, new service address, operator payout script
ProviderUpdateRegistrar: proTxHash, voting key hash, operator pubkey, payout script
ProviderUpdateRevocation: proTxHash, reason
- (AssetLock/AssetUnlock/Coinbase payloads can follow the same shape later)
- Populate it in
FFITransactionRecord construction from the stored TransactionRecord's special_transaction_payload, and stop hardcoding special_transaction_payload: None in the three conversion paths above where the source payload is available.
- Ensure the payload (or at least the provider-relevant fields) survives
drop_finalized_transaction for transactions that matched a provider key account, so the data remains queryable without keep-finalized-transactions.
Item 3 overlaps with the broader "local masternode surface" idea (a persistent DashSync-DSLocalMasternode-style struct, plus monitoring proTxHash in the filter query for follow-on ProUpServTx IP updates — the pending remainder of #861). This issue is scoped to the FFI record shape; the local-masternode registry can build on it.
Acceptance
FFITransactionRecord for a stored ProRegTx exposes a typed payload including service_address without the consumer decoding tx_data.
- The three
special_transaction_payload: None sites populate the payload when the source has one.
- Round-trip test: record a mainnet-shape
ProRegTx (e.g. owner/voting hash160 70993555a01f7e8d6179d6135b5c56809d2d1d36, service 54.148.58.128:9999) → FFI record exposes those fields.
Motivation
When a wallet's provider owner/voting key matches a
ProRegTxduring compact-filter sync (works since #863/#864, part of #861), consumers need the transaction's special payload data — most importantly the masternode'sservice_address(IP:port), but alsoproTxHashlinkage, collateral outpoint, and the owner/voting/operator keys — to display anything useful about the masternode ("this key was used by the masternode at 54.148.58.128:9999"). The legacy DashSync SDK exposed this viaDSLocalMasternode(built fromDSProviderRegistrationTransaction.ipAddress/port); the Rust SDK currently cannot.Current state
FFITransactionRecord(key-wallet-ffi/src/managed_account.rs) has no typed payload field. The only way to recover payload data is to consensus-decode the rawtx_databytes on the consumer side — verified working (the service IP sits in those bytes), but it forces every binding (Swift/Kotlin) to reimplement DIP-2/DIP-3 payload parsing.Worse, every other FFI conversion path explicitly drops the payload when reconstructing a transaction:
key-wallet-ffi/src/transaction.rs:280—special_transaction_payload: Nonekey-wallet-ffi/src/tx_decode.rs:361—special_transaction_payload: Nonekey-wallet-ffi/src/managed_account.rs:2350—special_transaction_payload: NoneAnd with
keep-finalized-transactionsoff (the default in mobile builds), a chainlocked-at-first-sight historicalProRegTx's full record is dropped right after processing (managed_core_keys_account.rs,drop_finalized_transaction), so even the raw-bytes escape hatch disappears for the very transactions masternode-key discovery cares about.Proposal
Add a typed, FFI-safe specialized-payload representation to
FFITransactionRecord:FFISpecialTransactionPayloadwith apayload_typediscriminant matching the DIP-2 tx types) covering at least:ProviderRegistration: version, masternode type, collateral outpoint, service address (string form + raw ip/port), owner/voting key hashes, operator pubkey, operator reward, payout script, platform node id/portsProviderUpdateService: proTxHash, new service address, operator payout scriptProviderUpdateRegistrar: proTxHash, voting key hash, operator pubkey, payout scriptProviderUpdateRevocation: proTxHash, reasonFFITransactionRecordconstruction from the storedTransactionRecord'sspecial_transaction_payload, and stop hardcodingspecial_transaction_payload: Nonein the three conversion paths above where the source payload is available.drop_finalized_transactionfor transactions that matched a provider key account, so the data remains queryable withoutkeep-finalized-transactions.Item 3 overlaps with the broader "local masternode surface" idea (a persistent DashSync-
DSLocalMasternode-style struct, plus monitoringproTxHashin the filter query for follow-onProUpServTxIP updates — the pending remainder of #861). This issue is scoped to the FFI record shape; the local-masternode registry can build on it.Acceptance
FFITransactionRecordfor a storedProRegTxexposes a typed payload includingservice_addresswithout the consumer decodingtx_data.special_transaction_payload: Nonesites populate the payload when the source has one.ProRegTx(e.g. owner/voting hash16070993555a01f7e8d6179d6135b5c56809d2d1d36, service54.148.58.128:9999) → FFI record exposes those fields.