Expose fail() and cancel() on Receiver session#1134
Expose fail() and cancel() on Receiver session#1134spacebear21 wants to merge 2 commits intopayjoin:masterfrom
Conversation
e4008b1 to
c373ec1
Compare
Pull Request Test Coverage Report for Build 18177469540Details
💛 - Coveralls |
This gives implementers the ability to manually fail or cancel a receiver Payjoin session. A user might cancel a session for any reason, or the wallet might fail it if e.g. it detects a double-spend or cannot proceed past a transient error.
c373ec1 to
7c56df0
Compare
| /// | ||
| /// This method allows implementations to terminate the payjoin session when | ||
| /// the user decides to cancel the operation interactively. | ||
| pub fn cancel<P>(self, persister: &P) -> Result<(), P::InternalStorageError> |
There was a problem hiding this comment.
Do you think cancel is simpler than just calling close() on the persister?
There was a problem hiding this comment.
The difference is that cancel() (and fail()) ensures that a Closed event is added to the log before closing.
| /// This method allows implementations to terminate the payjoin session when | ||
| /// they encounter errors that cannot be resolved, such as insufficient | ||
| /// funds or a double-spend detection. | ||
| pub fn fail<P>(self, persister: &P) -> Result<(), P::InternalStorageError> |
There was a problem hiding this comment.
Should we push a generic failure error reply? Otherwise the sender will be hanging until expiration.
There was a problem hiding this comment.
Yes good point, we do need a way to communicate this (and probably cancel() too) back to the sender.
There was a problem hiding this comment.
I started down an approach where they transition to HasReplyableError to enforce responding to the sender. However there is an outstanding issue in that it needs to differentiate between Cancel and Failure outcomes when replaying the event log, so I think GotReplyableError would need to carry this information somehow.
I'm not sure if we actually need to reply in case of Cancel, assuming the receiver would broadcast the fallback tx?
There was a problem hiding this comment.
Cancel sounds like the unavailable wellKnownError to me, even if broadcast is sufficient
There was a problem hiding this comment.
The only reason I can think of for a receiver to do this would be if the receiver wanted to refuse a naive tx.
A better solution for this example would be having the receiver use req-pj
So for now letting it expire is fine.
|
I wanted to pose a question as to why we really need to have these 2 seperate instead of just having application devs use |
| /// Explicitly fail the session. | ||
| /// | ||
| /// This method allows implementations to terminate the payjoin session when | ||
| /// they encounter errors that cannot be resolved, such as insufficient | ||
| /// funds or a double-spend detection. | ||
| pub fn fail(self) -> NextStateTransition<SessionEvent, Receiver<HasReplyableError>> { | ||
| let err = JsonReply::new(crate::error_codes::ErrorCode::Unavailable, "Receiver error"); | ||
| NextStateTransition::success( | ||
| SessionEvent::GotReplyableError(err.clone()), | ||
| Receiver { | ||
| state: HasReplyableError { error_reply: err, outcome: SessionOutcome::Failure }, | ||
| session_context: self.session_context, | ||
| }, | ||
| ) | ||
| } | ||
|
|
There was a problem hiding this comment.
Since failure encapsulates "protocol violations" this method seems unnecessary, everything user-initiated would be a "cancel"
| pub fn cancel(self) -> NextStateTransition<SessionEvent, Receiver<HasReplyableError>> { | ||
| let err = JsonReply::new(crate::error_codes::ErrorCode::Unavailable, "Receiver error"); | ||
| NextStateTransition::success( | ||
| SessionEvent::GotReplyableError(err.clone()), |
There was a problem hiding this comment.
This should transition to "Monitoring" for the fallback transaction
There was a problem hiding this comment.
I think I've convinced myself that transitioning to Receiver<Monitor> on cancel() just for monitoring the fallback tx is overkill.
- It would require refactoring the Monitor typestate to make
PsbtContextoptional, since we may not have a payjoin psbt when calling cancel(). - It would also require a new SessionEvent type like
CancelInitiatedthat transitions to Monitor "early" during replay. - It introduced ambiguity around the role of
Monitortypestate. Currently this state is where we monitor for a set of possible transactions that the sender may broadcast. Monitoring on cancel() would mean we're now also monitoring a transaction broadcast by ourselves. The currentSessionOutcome(FallbackBroadcasted)implies the session was closed by the sender. Now it might actually be the receiver who did? And shouldn't we be closing withSessionOutcome(Cancel)anyway?
The wallet already knows to track a tx that it broadcasted itself, and going through the monitor typestate for confirmation doesn't add any useful additional information that I can think of, especially considering all this additional complexity.
So I think cancel() should simply a) immediately close the session with SessionOutcome(Cancel) and b) return the fallback transaction to broadcast.
|
superseded by #1470 |
This gives implementers the ability to manually fail or cancel a receiver Payjoin session. A user might cancel a session for any reason, or the wallet might fail it if e.g. it detects a double-spend or cannot proceed past a transient error.
Note that the unit tests for closed sessions will need to be reworked once #1132 is merged, since the
expected_receiver_stateshould be an error.I used Claude sonnet 4.5 (it's quite good) to write the generic methods and unit tests.
Pull Request Checklist
Please confirm the following before requesting review:
AI
in the body of this PR.