Receiver fallback typestate#1558
Draft
spacebear21 wants to merge 8 commits into
Draft
Conversation
Introduce HasFallback: State with a non-Option fallback_tx() and implement it on the in-protocol receiver states whose contract includes a confirmed broadcastable fallback. - UncheckedOriginalPayload is deliberately excluded; it holds the sender's Original PSBT but has not yet run check_broadcast_suitability, so the PSBT is not yet verified as broadcastable. - HasReplyableError is also excluded; it will gain an optional fallback field in a later commit and continues to model the absent-fallback case at runtime. The existing sealed::State::fallback_tx() returns Option<Transaction> because it lives on the parent trait every state implements, including ones that hold no fallback. It will be removed in a later commit in favor of the HasFallback trait
Fix to pickup local changes in JS bindings.
PendingFallback represents a receiver session that was cancelled or hit a fatal protocol error, and has a fallback transaction available to broadcast. While the session sits in PendingFallback the implementer holds an obligation to broadcast, discard, or otherwise handle the fallback transaction (e.g. save it to wallet DB for later broadcasting). This state is preserved across restarts and session replays until the implemeter calls `close()`, indicating that the handoff of the fallback transaction is complete and no longer a payjoin concern.
HasReplyableError represents a receiver session that hit a replyable error before reaching PendingFallback. The struct must model the runtime fact that some sources can hand it a verified broadcastable fallback and others cannot. Encoding the field as Option<Transaction> keeps that distinction at the type level without weakening the HasFallback trait contract.
Introduce MaybeTerminalTransition for the no-error fork (used by cancel) and MaybeTerminalSuccessTransition for the error-bearing fork (used by process_error_response). Both expose advance and terminate constructors that map to Save and SaveAndClose actions respectively. The success variant returns Option<NextState>; the error variants preserve the caller's distinction between transient, fatal-advance, and fatal-terminate.
The receiver side of v2 had a single blanket cancel implementation that always terminated the session and handed the wallet an Option<Transaction>. Fatal protocol errors emitted Closed(Failure) directly. Both shapes lost the wallet's obligation to broadcast the original transaction across a restart whenever a fallback existed. Replace the blanket cancel with typestate-aware impls: - impl<S: HasFallback> Receiver<S>::cancel advances to PendingFallback - Receiver<Initialized>::cancel and Receiver<UncheckedOriginalPayload> ::cancel terminate with Closed(Cancel); neither holds a verified fallback - Receiver<HasReplyableError>::cancel forks on the optional fallback: Some advances to PendingFallback, None terminates with Closed(Cancel)
The receiver side now lets a session pause in PendingFallback after a cancel or a fatal protocol error. The cli needs a wallet-facing way to enter that state, finish it (by broadcasting or discarding), and pick it up on resume.
Drop dead code.
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.
This draft illustrates the idea described here for the receiver side. Supersedes #1542.
Planned with Claude Opus 4.7, implemented by Codex 5.5
Pull Request Checklist
Please confirm the following before requesting review:
AI
in the body of this PR.