Skip to content

Sender Pending Fallback state#1557

Open
arminsabouri wants to merge 3 commits into
payjoin:masterfrom
arminsabouri:sender-cancel-state
Open

Sender Pending Fallback state#1557
arminsabouri wants to merge 3 commits into
payjoin:masterfrom
arminsabouri:sender-cancel-state

Conversation

@arminsabouri
Copy link
Copy Markdown
Collaborator

@arminsabouri arminsabouri commented May 14, 2026

Implementing @spacebear21 's idea from #1542 (comment)
for just the sender.

Recevier side is implemented here: #1558

Pull Request Checklist

Please confirm the following before requesting review:

Claude made the updates to the payjoin-cli

@arminsabouri arminsabouri force-pushed the sender-cancel-state branch 2 times, most recently from 7cf2e7c to d3dac67 Compare May 15, 2026 15:31
@coveralls
Copy link
Copy Markdown
Collaborator

coveralls commented May 15, 2026

Coverage Report for CI Build 26115328632

Coverage decreased (-0.09%) to 85.211%

Details

  • Coverage decreased (-0.09%) from the base build.
  • Patch coverage: 22 uncovered changes across 4 files (34 of 56 lines covered, 60.71%).
  • No coverage regressions found.

Uncovered Changes

File Changed Covered %
payjoin/src/core/send/v2/mod.rs 42 27 64.29%
payjoin-cli/src/app/v2/mod.rs 6 2 33.33%
payjoin-cli/src/app/v1.rs 2 0 0.0%
payjoin/src/core/send/v2/session.rs 3 2 66.67%

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 13720
Covered Lines: 11691
Line Coverage: 85.21%
Coverage Strength: 394.42 hits per line

💛 - Coveralls

@arminsabouri arminsabouri force-pushed the sender-cancel-state branch from d3dac67 to cefae94 Compare May 15, 2026 16:21
@arminsabouri arminsabouri force-pushed the sender-cancel-state branch 4 times, most recently from 38ef7b3 to 402848a Compare May 18, 2026 15:58
@DanGould
Copy link
Copy Markdown
Contributor

concept ACK just like #1558

@arminsabouri arminsabouri changed the title Sender cancel state Sender Pending Fallback state May 19, 2026
@arminsabouri arminsabouri force-pushed the sender-cancel-state branch from 402848a to b6040c7 Compare May 19, 2026 17:36
@arminsabouri arminsabouri marked this pull request as ready for review May 19, 2026 17:37
Comment thread payjoin/src/core/send/v2/mod.rs Outdated
/// Indicates that the fallback transaction has been broadcast and the session is complete.
pub fn broadcasted(
&self,
) -> MaybeSuccessTransition<SessionEvent, (), std::convert::Infallible> {
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::convert::Infallible bc this method can never fail due to a API error.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TerminalTransition is more appropriate

If application manually cancels the assumption is they will not attempt
to resume that session again. The session is marked closed. The cancel
type leaves the session open and
the session can only be closed once the user confirms they broadcasted
the fallback tx -- or took an equivalent action.
Introduce a `cancel` command for v2 sender sessions. The command
transitions the session to `PendingFallback`.
`cancel` only records the cancellation event and prints a message
directing the user to run `fallback` to broadcast. `fallback` handles
the broadcast. The e2e test is updated to exercise both steps in
sequence, asserting the fallback is absent from the mempool after
`cancel` and present after `fallback`.
@arminsabouri arminsabouri force-pushed the sender-cancel-state branch from b6040c7 to c57d338 Compare May 19, 2026 17:55
Copy link
Copy Markdown
Collaborator

@xstoicunicornx xstoicunicornx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK c57d338

Somewhat strongly held nit: changing from SessionOutcome::Cancel to SessionOutcome::Aborted makes it more confusing/less intuitive. It gives two different terms that refer to the same state transition path. Was there a reason you felt that SessionOutcome::Aborted was better? I do think Cancelled is better than Cancel though.

Other than my nits and the comment that seems like it can be removed, implementation looks good and all tests pass.

Comment on lines +204 to +215

// impl<S> From<payjoin::persist::PersistedError<std::convert::Infallible, S>> for SenderPersistedError
// where
// S: std::error::Error + Send + Sync + 'static,
// {
// fn from(err: payjoin::persist::PersistedError<std::convert::Infallible, S>) -> Self {
// if let Some(storage_err) = err.storage_error() {
// return SenderPersistedError::from(ImplementationError::new(storage_err));
// }
// SenderPersistedError::Unexpected
// }
// }
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// impl<S> From<payjoin::persist::PersistedError<std::convert::Infallible, S>> for SenderPersistedError
// where
// S: std::error::Error + Send + Sync + 'static,
// {
// fn from(err: payjoin::persist::PersistedError<std::convert::Infallible, S>) -> Self {
// if let Some(storage_err) = err.storage_error() {
// return SenderPersistedError::from(ImplementationError::new(storage_err));
// }
// SenderPersistedError::Unexpected
// }
// }

Can this be removed?

Comment on lines +197 to +198
SendSession::PendingFallback(inner) =>
Self::Cancelled { inner: Arc::new(inner.into()) },
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
SendSession::PendingFallback(inner) =>
Self::Cancelled { inner: Arc::new(inner.into()) },
SendSession::PendingFallback(inner) =>
Self::PendingFallback { inner: Arc::new(inner.into()) },

Nit: Why not follow the base code naming conventions rather than creating anti-patterns?

"State should be Closed after cancel",
"Cancelled",
"State should be Cancelled after cancel",
);
Copy link
Copy Markdown
Collaborator

@xstoicunicornx xstoicunicornx May 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

            const cancelled = pendingFallback
                .broadcasted()
                .save(senderPersister);

            const result = payjoin.replaySenderEventLog(senderPersister);
            const state = result.state();
            assert.strictEqual(
                state.tag,
                "Closed",
                "State should be Closed after cancel",
            );

Nit: Maybe keep Closed as the final state so that it tests broadcasted() and continues to test that send session is closed out? Would apply to rest of unit tests as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants