fix: classify a stale funding reservation as NotBroadcast on the atomic send path - #1548
Open
HashEngineering wants to merge 1 commit into
Open
Conversation
…ic send path dashpay/platform#4309 age-guards the atomic finalize -> broadcast path against the same reservation bound the deferred surface already used, and reuses native code 34 (ErrorStaleReservationToken) for it. That puts the error on classifyCoreSendFailure's paths for the first time -- the plain send (sendToAddresses) and the drain (CoreSendAllNative) -- where there was no arm for it. Without an arm it fell through to classifyBroadcastFailure and came back Ambiguous, so a refusal the SDK contract guarantees never reached the broadcaster would surface as "may be on the network" and block the safe dashj fallback. The guard also releases the still-owned reservation owner-guarded on the way out, so the freed inputs are immediately reselectable by a rebuild. classifyDeferredBroadcastFailure keeps its own arm ahead of this one purely to name the deferred surface in the reason string. Test fails without the production arm and passes with it; the ambiguous sibling (code 20) is asserted to keep its Ambiguous classification so the pair cannot collapse into one rule. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Contributor
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds a typed
StaleReservationTokenarm toclassifyCoreSendFailure, so a stale funding reservation on the atomic send path classifiesNotBroadcastinstead ofAmbiguous.Why
dashpay/platform#4309 age-guards the finalized-transaction handle path (
finalize→broadcastTransaction) against the same reservation bound the deferred BIP70 surface already used, and deliberately reuses native code 34 (ErrorStaleReservationToken) rather than minting a new one.Until then, code 34 could only arrive from the deferred surface, so the arm lived only in
classifyDeferredBroadcastFailure. After #4309 it also reaches:SdkL1SendService.sendToAddress→ManagedPlatformWallet.sendToAddresses(the plain send)CoreSendAllNative→ManagedCoreWallet.broadcastTransaction(send-all / CoinJoin drain)Neither is covered by the deferred classifier, so the error fell through
classifyCoreSendFailure's message-prefix rules toclassifyBroadcastFailureand came backAmbiguous.That is wrong in the direction that costs the user most. The SDK contract guarantees this refusal happens before the broadcaster is touched, and the guard releases the still-owned reservation owner-guarded on the way out, so the inputs are immediately reselectable. Classifying it
Ambiguousshows a payment that provably never left the device as "may be on the network" and blocks the safe dashj fallback — the exact failure mode the existing typed arms (CoreInsufficientFunds,SigningKeyUnavailable,TransactionBroadcastRejected) were added to prevent.Changes
SdkL1SendService.kt— typedStaleReservationTokenarm inclassifyCoreSendFailure, placed with the other definitively-pre-network arms, plus KDoc explaining why both classifiers keep an arm (the deferred one stays ahead only to name the deferred surface in the reason string).SdkL1SendServiceTest.kt—classify_staleReservationToken_isNotBroadcast_onTheAtomicSendPath, and code 34 added to the deferred-table parity list.Testing
:wallet:testProdDebugUnitTest --tests SdkL1SendServiceTest— 71 tests, 0 failures.Verified non-vacuous: with the production arm reverted, the new test fails (
AssertionError) and it is the only failure. The existing deferred-path test still passes without it, confirming the two classifiers are independently covered.The new test also asserts the ambiguous sibling
TransactionBroadcastUnconfirmed(code 20) keeps itsAmbiguousclassification, so the pair cannot collapse into one rule.Notes
PlatformWalletError::InputMidBroadcast(the new build refusal when a coin selection picks an input held by an in-flight dispatch) maps toErrorUnknown(99) and so also classifiesAmbiguous. Fixing it by message-prefix matching would be fragile — the message embeds an outpoint — so the right fix is claiming a real FFI code upstream and adding a typed arm here. Not yet raised upstream.