-
Notifications
You must be signed in to change notification settings - Fork 56
feat(kotlin-sdk): split build/broadcast with reservation release for BIP70-style deferred submission #4185
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: v4.2-dev
Are you sure you want to change the base?
feat(kotlin-sdk): split build/broadcast with reservation release for BIP70-style deferred submission #4185
Changes from all commits
78a4b0b
558455c
308479d
ddc8872
7f7b020
ade3999
36a6afe
0f5ac19
9eb53b3
1678804
1458d5b
fba8c48
6f5edea
1d4b3fa
3ef6e89
c5677bf
e219883
47b4d53
6d6bf61
74f9ae4
90f144d
a020d7d
d5f16a6
3f719d4
440897c
7d85953
0b0d5c7
d854deb
6c37e86
d1bed4d
6be6748
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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/bfoss765/rust-dashcore", rev = "e99959ced0062159d629930f488374e29f63c42b" } | ||
| dash-network-seeds = { git = "https://github.com/bfoss765/rust-dashcore", rev = "e99959ced0062159d629930f488374e29f63c42b" } | ||
| dash-spv = { git = "https://github.com/bfoss765/rust-dashcore", rev = "e99959ced0062159d629930f488374e29f63c42b" } | ||
| key-wallet = { git = "https://github.com/bfoss765/rust-dashcore", rev = "e99959ced0062159d629930f488374e29f63c42b" } | ||
| key-wallet-ffi = { git = "https://github.com/bfoss765/rust-dashcore", rev = "e99959ced0062159d629930f488374e29f63c42b" } | ||
| key-wallet-manager = { git = "https://github.com/bfoss765/rust-dashcore", rev = "e99959ced0062159d629930f488374e29f63c42b" } | ||
| dash-network = { git = "https://github.com/bfoss765/rust-dashcore", rev = "e99959ced0062159d629930f488374e29f63c42b" } | ||
| dashcore-rpc = { git = "https://github.com/bfoss765/rust-dashcore", rev = "e99959ced0062159d629930f488374e29f63c42b" } | ||
|
Comment on lines
+55
to
+62
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔴 Blocking: Do not merge workspace-wide dependencies from a personal fork All eight rust-dashcore workspace dependencies are redirected from source: ['codex'] |
||
|
|
||
| tokio-metrics = "0.5" | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -176,6 +176,49 @@ sealed class DashSdkError( | |
| cause, | ||
| ) | ||
|
|
||
| /** | ||
| * `ErrorStaleReservationToken` (native code 27). A deferred | ||
| * (BIP70/BIP270) [broadcastSigned][org.dashfoundation.dashsdk.wallet.ManagedPlatformWallet.broadcastSigned] | ||
| * token has outlived its funding reservation's 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 with | ||
| * [buildSignedPayment][org.dashfoundation.dashsdk.wallet.ManagedPlatformWallet.buildSignedPayment]. | ||
| * | ||
| * 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 28). 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 30). 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 | ||
|
|
@@ -235,7 +278,20 @@ sealed class DashSdkError( | |
| 6 -> PlatformWallet.WalletOperation(message, cause) // ErrorWalletOperation | ||
| 7, // ErrorIdentityNotFound | ||
| 8, // ErrorContactNotFound | ||
| 98, // NotFound (Option returned as an error) | ||
| // NotFound. Handle/Option lookup failures, plus the | ||
| // wallet-was-REMOVED case 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 (30) | ||
| // no other live generation holds the payment either — so it is not | ||
| // retryable. See dashpay/platform#4185. | ||
| 98, | ||
| -> NotFound(message, cause) | ||
| 16 -> PlatformWallet.ShieldedBroadcastFailed(message, cause) // ErrorShieldedBroadcastFailed | ||
| 18 -> PlatformWallet.ShieldedSpendUnconfirmed(message, cause) // ErrorShieldedSpendUnconfirmed | ||
|
|
@@ -245,6 +301,11 @@ sealed class DashSdkError( | |
| 23 -> PlatformWallet.AssetLockNotTracked(message, cause) // ErrorAssetLockNotTracked | ||
| 24 -> PlatformWallet.AssetLockAlreadyConsumed(message, cause) // ErrorAssetLockAlreadyConsumed | ||
| 25 -> PlatformWallet.AssetLockFundingMismatch(message, cause) // ErrorAssetLockFundingMismatch | ||
| 27 -> PlatformWallet.StaleReservationToken(message, cause) // ErrorStaleReservationToken | ||
| 28 -> PlatformWallet.ReservationTokenConsumed(message, cause) // ErrorReservationTokenConsumed | ||
| // 29 is ErrorAssetLockInsufficientFunds (dashpay/platform#4184); this | ||
| // code is 30. See packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md. | ||
| 30 -> PlatformWallet.ReservationWalletMismatch(message, cause) // ErrorReservationWalletMismatch | ||
| else -> PlatformWallet.Generic(code, message, cause) | ||
|
Comment on lines
296
to
309
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Suggestion: Preserve definitive broadcast rejection as a typed Kotlin error A definitive deferred-payment broadcast rejection is preserved as native code 26 through the C ABI and JNI, but this switch falls through to source: ['codex'] |
||
| } | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift
Do not merge workspace dependencies pinned to a personal fork.
All eight
rust-dashcorecrates now resolve fromhttps://github.com/bfoss765/rust-dashcoreat reve99959ced0062159d629930f488374e29f63c42b. A personal fork is outside the organization's control: the owner can force-push, rename, or delete the repository, and the build then fails or silently resolves unaudited code. This also makes the whole wallet, SPV, and key-derivation stack unreviewed relative todashpay/rust-dashcore.This change is also unrelated to the deferred signed-payment work in this PR. If the fork carries a required upstream change, land that change in
dashpay/rust-dashcorefirst, then pin this workspace to the resulting upstream revision before merge.🔒 Proposed change once the upstream revision exists
Run the following script to check whether the pinned revision exists upstream:
🤖 Prompt for AI Agents
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed — this is a genuine merge blocker, and it can't be fixed inside this PR.
The eight crates are pinned to
bfoss765/rust-dashcore@e99959cebecause thisPR depends on an unreleased key-wallet change: owner-tagged reservations, which
close the broadcast/release TOCTOU that the deferred build → broadcast lifecycle
in this PR is built on. Without it the reservation set can't tell which owner a
release belongs to, and the split-build feature has no safe primitive to sit on.
That change is upstream as dashpay/rust-dashcore#916
("feat(key-wallet): owner-tagged reservations to close the broadcast-release
TOCTOU (platform#4185)"). It is open and currently conflicting; we're working it now. This PR is blocked on #916 merging and should not go in before it.
On the "does the rev exist upstream" check in your script — it needs one
correction, because the naive form gives a false pass.
gh api repos/dashpay/rust-dashcore/commits/e99959cedoes return the commit, but onlybecause forks share object storage with the parent; resolvability is not
reachability. The real check is against a branch:
So the pin is fork-only, exactly as you say: one commit ahead of a point on
devand six behind, on no upstream branch.Plan of record: once #916 lands, this workspace repins all eight crates to
https://github.com/dashpay/rust-dashcoreat the resulting upstream revision —no
bfoss765remotes in the merged tree. I'll leave this thread open as themerge gate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bfoss765, acknowledged. The explanation confirms that the personal-fork pin is a required temporary dependency for owner-tagged reservations in#916.You are correct about the validation detail. A successful
commits/<sha>response only proves object availability. It does not prove that an upstream branch reaches the revision. The branch reachability checks are the correct validation.I will keep this thread open. Before this PR merges, it must repin all eight workspace dependencies to
https://github.com/dashpay/rust-dashcoreat an upstream revision that contains#916.You are interacting with an AI system.