Repository navigation
Move unconfirmed outgoing restore eligibility semantics from Swift to platform-wallet #4700
Description
Summary
Move the transaction-context and block-height eligibility decision for unconfirmed outgoing transaction replay from the Swift SDK into platform-wallet, exposed through rs-platform-wallet-ffi.
Swift must continue to load PersistentTxo rows, resolve SwiftData relationships, preserve legacy walletId routing, and marshal selected transaction records. Swift must not decide whether a spender context is settled for spend purposes.
Rationale
PlatformWalletPersistenceHandler.buildUnconfirmedOutgoingTxRecordBuffer currently checks spender.context and spender.blockHeight to decide whether a transaction is eligible for replay. These fields encode wallet-state semantics. The current alignment with spendIsInBlock reduces drift, but it does not remove duplicate policy.
A small Rust-owned FFI predicate can make platform-wallet the single owner of this semantic rule without requiring Swift to send every SwiftData relationship to Rust.
The write-side spendIsInBlock behavior and the restore-side eligibility behavior must move together. Otherwise Swift persistence and Rust restoration can disagree about whether an input remains unspent.
Affected areas
packages/swift-sdk/Sources/SwiftDashSDK/PlatformWallet/PlatformWalletPersistenceHandler.swiftspendIsInBlockbuildUnconfirmedOutgoingTxRecordBuffer
packages/rs-platform-wallet- Define the authoritative settled-spend / replay-eligibility rule.
packages/rs-platform-wallet-ffi- Expose the minimal predicate required by Swift.
Required changes
- Add a
platform-walletAPI that owns the context and block-height decision used to determine whether an outgoing spender remains eligible for replay. - Expose that API through
rs-platform-wallet-ffi. - Replace the Swift checks of
spender.contextandspender.blockHeightwith the FFI predicate. - Keep Swift-owned persistence work in Swift:
- Find TXO-linked spenders.
- Check persisted relationship liveness and
isSpent. - Route legacy TXO rows through their owning account when
walletIdis absent. - Exclude asset-lock funding transactions.
- Marshal records into
UnconfirmedOutgoingTxRecordFFI.
- Update the write-side
spendIsInBlockuse so it obtains the same semantic result from the Rust-owned rule. - Add Rust and Swift tests for mempool, InstantSend-locked, in-block, and invalid or inconsistent block-height combinations.
Acceptance criteria
platform-walletis the only owner of transaction-context and block-height eligibility semantics for unconfirmed outgoing replay.- Swift does not contain an independent policy condition over
PersistentTransaction.contextorPersistentTransaction.blockHeightfor this decision. - The write path and restore path use the same Rust-owned rule.
- Swift retains only SwiftData selection, legacy-row routing, asset-lock exclusion, and FFI marshalling responsibilities.
- Tests cover the supported context cases and prevent a future divergence.
Backlinks
- Pull request: fix(platform-wallet): give an unconfirmed outgoing send an owner across a restart #4659
- Review discussion: fix(platform-wallet): give an unconfirmed outgoing send an owner across a restart #4659 (comment)
- Requested by: @romchornyi
Activity