Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
8f14318
feat(kotlin-sdk): split build/broadcast with reservation release for …
bfoss765 Jul 10, 2026
dd04607
fix(kotlin-sdk): bound deferred-payment token lifetime; harden regist…
bfoss765 Jul 11, 2026
f09a7e1
fix(kotlin-sdk): resolve rebase semantic conflicts onto feat/kotlin-s…
bfoss765 Jul 16, 2026
43a2414
fix(kotlin-sdk): assert native code 26 for the stale-reservation-toke…
bfoss765 Jul 17, 2026
f6fbda8
fix(kotlin-sdk): bound the deferred-payment token on the reservation'…
bfoss765 Jul 17, 2026
5c7a461
fix(kotlin-sdk): sweep deferred-payment tokens only when the final wa…
bfoss765 Jul 17, 2026
8dcd345
fix(kotlin-sdk): route deferred builds through the atomic finalize-an…
bfoss765 Jul 17, 2026
35a2335
fix(kotlin-sdk): delete the dead split register→broadcast chain
bfoss765 Jul 21, 2026
cd97c85
fix(kotlin-sdk): baseline the deferred token age on the pre-signing r…
bfoss765 Jul 21, 2026
bfd58fc
fix(kotlin-sdk): give the V2 handle and token paths one wallet-genera…
bfoss765 Jul 21, 2026
b2784e5
fix(kotlin-sdk): validate the deferred token under the lock, consume …
bfoss765 Jul 21, 2026
489e456
fix(kotlin-sdk): release deferred reservations at final-alias destroy…
bfoss765 Jul 21, 2026
9758f3c
fix(kotlin-sdk): split the conflated deferred-token error code into t…
bfoss765 Jul 21, 2026
1803197
docs(kotlin-sdk): correct buildSignedPayment KDoc to the finalize-and…
bfoss765 Jul 21, 2026
ade5cff
fix(kotlin-sdk): make the deferred payment token an owning AutoClosea…
bfoss765 Jul 21, 2026
2f596c6
style(kotlin-sdk): rustfmt wallet_manager.rs after the dead-chain del…
bfoss765 Jul 21, 2026
5eba39a
fix(kotlin-sdk): object-owning broadcast/release overloads for Signed…
bfoss765 Jul 21, 2026
8173cee
fix(kotlin-sdk): retain a releasable account handle for CoinJoin-fund…
bfoss765 Jul 22, 2026
019ec30
fix(kotlin-sdk): bind deferred-payment reservation cleanup to its own…
bfoss765 Jul 22, 2026
48d255f
fix(swift-sdk): surface deferred-token codes 26/27/28 as typed errors
bfoss765 Jul 22, 2026
4b4b7b7
chore(rust-dashcore): pin to canonical dashpay dev rev with owner-tag…
bfoss765 Jul 22, 2026
581947d
fix: renumber deferred-reservation error codes to 27/28/29
bfoss765 Jul 23, 2026
1a25d91
fix(platform-wallet): owner-guard the broadcast-reject reservation re…
bfoss765 Jul 23, 2026
bc33a61
fix(platform-wallet): enforce unique reservation ownership, stop wrap…
bfoss765 Jul 24, 2026
8612a3f
fix(platform-wallet): bind registration to correct wallet generation …
bfoss765 Jul 25, 2026
13ddeec
fix(platform-wallet): linearize wallet removal with deferred broadcas…
bfoss765 Aug 1, 2026
7fc0f24
fix(platform-wallet-ffi)!: move ErrorReservationWalletMismatch 29 -> …
bfoss765 Aug 1, 2026
5b6288d
refactor(platform-wallet-ffi): drop stale HandleStorage::any left by …
bfoss765 Aug 1, 2026
e5c9982
fix(platform-wallet): scope the lifecycle gate to the wallet generati…
bfoss765 Aug 2, 2026
b0ac945
fix(platform-wallet): remove the wallet generation by identity, not b…
bfoss765 Aug 2, 2026
a9008b2
fix(platform-wallet): move the deferred-token trio off the codes #426…
bfoss765 Aug 3, 2026
0e15cc3
fix(kotlin-sdk): stop the stray 98 arm from shadowing PlatformWallet.…
bfoss765 Aug 4, 2026
d653abb
fix(platform-wallet): age-guard the V2 finalized-transaction handle b…
bfoss765 Jul 21, 2026
7a7d629
test+docs(v2-age-guard): boundary test covers both account types; Kot…
bfoss765 Jul 21, 2026
fdcb37f
fix(platform-wallet): age-guard the V2 abandon/free reservation release
bfoss765 Jul 22, 2026
397ba69
fix(platform-wallet): correct stale abandon-aged-reservation docs; dr…
bfoss765 Aug 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 25 additions & 25 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 8 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,14 @@ members = [
]

[workspace.dependencies]
dashcore = { git = "https://github.com/dashpay/rust-dashcore", rev = "70d4bf8e36057c58e02d56769a6e9760f701dd06" }
dash-network-seeds = { git = "https://github.com/dashpay/rust-dashcore", rev = "70d4bf8e36057c58e02d56769a6e9760f701dd06" }
dash-spv = { git = "https://github.com/dashpay/rust-dashcore", rev = "70d4bf8e36057c58e02d56769a6e9760f701dd06" }
key-wallet = { git = "https://github.com/dashpay/rust-dashcore", rev = "70d4bf8e36057c58e02d56769a6e9760f701dd06" }
key-wallet-ffi = { git = "https://github.com/dashpay/rust-dashcore", rev = "70d4bf8e36057c58e02d56769a6e9760f701dd06" }
key-wallet-manager = { git = "https://github.com/dashpay/rust-dashcore", rev = "70d4bf8e36057c58e02d56769a6e9760f701dd06" }
dash-network = { git = "https://github.com/dashpay/rust-dashcore", rev = "70d4bf8e36057c58e02d56769a6e9760f701dd06" }
dashcore-rpc = { git = "https://github.com/dashpay/rust-dashcore", rev = "70d4bf8e36057c58e02d56769a6e9760f701dd06" }
dashcore = { git = "https://github.com/dashpay/rust-dashcore", rev = "8f78baa6b7979b9bea56501ad75b5a7b7150a711" }
dash-network-seeds = { git = "https://github.com/dashpay/rust-dashcore", rev = "8f78baa6b7979b9bea56501ad75b5a7b7150a711" }
dash-spv = { git = "https://github.com/dashpay/rust-dashcore", rev = "8f78baa6b7979b9bea56501ad75b5a7b7150a711" }
key-wallet = { git = "https://github.com/dashpay/rust-dashcore", rev = "8f78baa6b7979b9bea56501ad75b5a7b7150a711" }
key-wallet-ffi = { git = "https://github.com/dashpay/rust-dashcore", rev = "8f78baa6b7979b9bea56501ad75b5a7b7150a711" }
key-wallet-manager = { git = "https://github.com/dashpay/rust-dashcore", rev = "8f78baa6b7979b9bea56501ad75b5a7b7150a711" }
dash-network = { git = "https://github.com/dashpay/rust-dashcore", rev = "8f78baa6b7979b9bea56501ad75b5a7b7150a711" }
dashcore-rpc = { git = "https://github.com/dashpay/rust-dashcore", rev = "8f78baa6b7979b9bea56501ad75b5a7b7150a711" }

tokio-metrics = "0.5"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,74 @@ sealed class DashSdkError(
class NotFound(message: String, cause: Throwable? = null) :
PlatformWallet(message, cause)

/**
* `ErrorTransactionBroadcastRejected` (native code 26). Core
* DEFINITIVELY rejected the core transaction: it is not on the network
* and will not get there. The build's UTXO reservation was released and,
* on the deferred (BIP70/BIP270) path, the token was consumed at the
* same time — so the inputs are spendable again and the token is gone.
*
* The definitive counterpart to [TransactionBroadcastUnconfirmed] (20),
* whose outcome is AMBIGUOUS and which therefore keeps its inputs
* reserved. Because the reservation and token are already gone, this is
* NOT retryable in place: address the rejection reason carried in the
* message, then rebuild with
* [buildSignedPayment][org.dashfoundation.dashsdk.wallet.ManagedPlatformWallet.buildSignedPayment]
* (deferred) or re-issue the send.
*/
class TransactionBroadcastRejected(message: String, cause: Throwable? = null) :
PlatformWallet(message, cause)

/**
* `ErrorStaleReservationToken` (native code 34). A payment's funding
* reservation has outlived its lifetime: key-wallet's TTL may already
* have swept and re-selected the inputs, so acting on it could touch a
* newer, unrelated reservation. The call did NOT touch the network.
* NOT retryable in place — rebuild the payment.
*
* The code is shared by BOTH deferred-payment surfaces (the messages
* distinguish them): a deferred (BIP70/BIP270)
* [broadcastSigned][org.dashfoundation.dashsdk.wallet.ManagedPlatformWallet.broadcastSigned]
* token, rebuilt with
* [buildSignedPayment][org.dashfoundation.dashsdk.wallet.ManagedPlatformWallet.buildSignedPayment];
* and a token-less V2 finalized handle whose
* [broadcastTransaction][org.dashfoundation.dashsdk.wallet.ManagedCoreWallet.broadcastTransaction]
* aged past the same reservation bound (abandon still works at any age).
*
* Sibling of the other two deferred-token failures this code used to
* conflate: [ReservationTokenConsumed] (unknown / already broadcast /
* already released) and [ReservationWalletMismatch] (minted against a
* different wallet generation).
*/
class StaleReservationToken(message: String, cause: Throwable? = null) :
PlatformWallet(message, cause)

/**
* `ErrorReservationTokenConsumed` (native code 35). A deferred
* (BIP70/BIP270) [broadcastSigned][org.dashfoundation.dashsdk.wallet.ManagedPlatformWallet.broadcastSigned]
* token is unknown, already broadcast, or already released — the guard
* that turns a double-broadcast (or a broadcast after release) into a
* typed error instead of a second send. The call did NOT touch the
* network. NOT retryable: rebuild the payment with
* [buildSignedPayment][org.dashfoundation.dashsdk.wallet.ManagedPlatformWallet.buildSignedPayment].
* (Release is idempotent and never raises this.)
*/
class ReservationTokenConsumed(message: String, cause: Throwable? = null) :
PlatformWallet(message, cause)

/**
* `ErrorReservationWalletMismatch` (native code 36). A deferred
* (BIP70/BIP270) [broadcastSigned][org.dashfoundation.dashsdk.wallet.ManagedPlatformWallet.broadcastSigned]
* token was minted against a different wallet *generation* than the one
* broadcasting it (e.g. a wallet re-created under the same id); its
* reservation lives in that other generation's reservation set. The call
* did NOT touch the network and did NOT consume the rightful owner's
* token. NOT retryable through this handle: rebuild the payment with
* [buildSignedPayment][org.dashfoundation.dashsdk.wallet.ManagedPlatformWallet.buildSignedPayment].
*/
class ReservationWalletMismatch(message: String, cause: Throwable? = null) :
PlatformWallet(message, cause)

/**
* Any other `PlatformWalletFFIResultCode` without a dedicated type.
* Carries the platform-wallet [nativeCode] (already de-offset) and
Expand Down Expand Up @@ -335,6 +403,20 @@ sealed class DashSdkError(
// RAW code via translateManagedIdentityNotFoundToZero (#4051)
// before this mapping ever runs. BREAKING for Kotlin hosts that
// caught DashSdkError.NotFound from platform-wallet operations.
//
// 98 is also what the wallet-was-REMOVED case returns on BOTH
// deferred-send paths:
// * deferred (BIP70/BIP270) TOKEN path — a signed-payment broadcast
// whose wallet is no longer registered in the manager, or a
// signed-payment finalize whose wallet was removed while it was
// being signed;
// * finalized-transaction HANDLE (V2) path — a tx-builder finalize
// whose wallet was removed or re-created during signing (no handle
// is published), or a V2 broadcast whose generation is gone.
// Every one reconciles the build's UTXO reservation before returning.
// Nothing was broadcast, and unlike ReservationWalletMismatch (36)
// no other live generation holds the payment either — so it is not
// retryable. See dashpay/platform#4185.
PLATFORM_WALLET_NOT_FOUND_CODE ->
PlatformWallet.NotFound(message, cause)
16 -> PlatformWallet.ShieldedBroadcastFailed(message, cause) // ErrorShieldedBroadcastFailed
Expand All @@ -345,6 +427,16 @@ sealed class DashSdkError(
23 -> PlatformWallet.AssetLockNotTracked(message, cause) // ErrorAssetLockNotTracked
24 -> PlatformWallet.AssetLockAlreadyConsumed(message, cause) // ErrorAssetLockAlreadyConsumed
25 -> PlatformWallet.AssetLockFundingMismatch(message, cause) // ErrorAssetLockFundingMismatch
26 -> PlatformWallet.TransactionBroadcastRejected(message, cause) // ErrorTransactionBroadcastRejected
// The deferred-token trio sits at the contiguous block 34-36 because
// 27-33 are claimed elsewhere: 27 ErrorShutdownIncomplete
// (dashpay/platform#4268, merged), 29 ErrorAssetLockInsufficientFunds
// (#4184), 31 ErrorSigningKeyUnavailable (#4183/#4259), 32
// ErrorTransactionBuild (#4247/#4256), 33 ErrorTransactionSigning
// (#4256). See packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md.
34 -> PlatformWallet.StaleReservationToken(message, cause) // ErrorStaleReservationToken
35 -> PlatformWallet.ReservationTokenConsumed(message, cause) // ErrorReservationTokenConsumed
36 -> PlatformWallet.ReservationWalletMismatch(message, cause) // ErrorReservationWalletMismatch
// ErrorSigningKeyUnavailable — the STRUCTURED signer
// discriminator (dashpay/platform#4060 finding 7): the typed
// completion code rides the whole Rust round-trip, no message
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,45 @@ internal object WalletManagerNative {
*/
external fun coreTransactionFree(tx: Long)

/**
* `core_wallet_signed_payment_finalize` — atomically fund, reserve, sign,
* AND register a builder for deferred (BIP70/BIP270) submission in one
* native call. Selection and reservation commit as a single unit under the
* wallet-manager lock, closing the double-selection window. CONSUMES
* [builder]. [accountType]/[accountIndex] identify the funding account
* (0 BIP44, 1 BIP32, 2 CoinJoin); [coreSignerHandle] is a
* `MnemonicResolverHandle`.
*
* Returns a big-endian BLOB decoded into a `SignedCoreTransaction`:
* `u64 token, u64 feeDuffs, u32 txidLen, txid utf8, u32 txBytesLen, txBytes`.
*/
external fun coreWalletFinalizeSignedPayment(
builder: Long,
walletHandle: Long,
accountType: Int,
accountIndex: Int,
coreSignerHandle: Long,
): ByteArray

/**
* `core_wallet_signed_payment_broadcast` — broadcast the payment behind
* [token], reconciling its reservation on failure and consuming the token.
* Rather than double-broadcasting, an unusable token throws one of three
* sibling codes — `ErrorStaleReservationToken` (34, aged out),
* `ErrorReservationTokenConsumed` (35, already consumed/unknown), or
* `ErrorReservationWalletMismatch` (36, different wallet generation).
* [coreHandle] must resolve to the wallet the token was minted against.
* Returns the txid as a lowercase hex string.
*/
external fun coreWalletBroadcastSignedPayment(coreHandle: Long, token: Long): String

/**
* `core_wallet_signed_payment_release` — release the funding reservation
* behind [token] and drop it. Idempotent: releasing an unknown /
* already-consumed token is a silent no-op.
*/
external fun coreWalletReleaseSignedPayment(token: Long)

/**
* Enumerate the wallet's Platform-payment addresses with cached credit
* balances, as a big-endian blob: `u32 rowCount` then per row
Expand Down
Loading
Loading