Skip to content

Decapsulate v1 provisional and payjoin typestate from v2 typestate#938

Merged
spacebear21 merged 2 commits intopayjoin:masterfrom
arminsabouri:psbt-ctx-recv
Aug 13, 2025
Merged

Decapsulate v1 provisional and payjoin typestate from v2 typestate#938
spacebear21 merged 2 commits intopayjoin:masterfrom
arminsabouri:psbt-ctx-recv

Conversation

@arminsabouri
Copy link
Collaborator

We currently represent a failure to finalize a Payjoin proposal using
the ReplayableError(ImplementationError) variant. While this is
appropriate for a v1 Payjoin receiver, it misrepresents the expected
behavior for a v2 receiver. In v2, encountering an implementation error
is considered a transient failure. i.e the receiver should retry the
session rather than respond to the sender and terminate it.

This misclassification stems from the current v2 -> v1 type hierarchy,
which forces the v2 receiver to adopt ReplayableError, when a
more suitable variant exists. In reality, the v2 receiver only requires
a shared abstraction (PsbtContext) to finalize the PSBT and construct a
payjoin proposal. It does not need to embed or depend on the v1
receiver’s typestates.

This PR introduces PsbtContext as a common abstraction for
proposal finalization and decouples the ProvisionalProposal and
PayjoinProposal typestates from the v1 typestate. Additionally we map
the finalize proposal error to a new SessionError integration error variant.

@coveralls
Copy link
Collaborator

coveralls commented Aug 7, 2025

Pull Request Test Coverage Report for Build 16918472197

Details

  • 114 of 121 (94.21%) changed or added relevant lines in 5 files are covered.
  • 2 unchanged lines in 1 file lost coverage.
  • Overall coverage increased (+0.03%) to 85.901%

Changes Missing Coverage Covered Lines Changed/Added Lines %
payjoin/src/core/receive/mod.rs 68 70 97.14%
payjoin/src/core/receive/v2/error.rs 0 2 0.0%
payjoin/src/core/receive/v2/mod.rs 16 19 84.21%
Files with Coverage Reduction New Missed Lines %
payjoin/src/core/receive/v2/mod.rs 2 91.52%
Totals Coverage Status
Change from base Build 16901789693: 0.03%
Covered Lines: 7500
Relevant Lines: 8731

💛 - Coveralls

Copy link
Collaborator

@spacebear21 spacebear21 left a comment

Choose a reason for hiding this comment

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

Concept ACK, this is a nice decoupling.

pub struct ProvisionalProposal {
v1: v1::ProvisionalProposal,
psbt_context: PsbtContext,
context: SessionContext,
Copy link
Collaborator

Choose a reason for hiding this comment

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

As a follow up I'm thinking this other context should be renamed to session_context across all the receiver states.

We currently represent a failure to finalize a Payjoin proposal using
the `ReplayableError(ImplementationError)` variant. While this is
appropriate for a v1 Payjoin receiver, it misrepresents the expected
behavior for a v2 receiver. In v2, encountering an implementation error
is considered a transient failure. i.e the receiver should retry the
session rather than respond to the sender and terminate it.

This misclassification stems from the current v2 -> v1 type hierarchy,
which forces the v2 receiver to adopt `ReplayableError`, when a
more suitable variant exists. In reality, the v2 receiver only requires
a shared abstraction (`PsbtContext`) to finalize the PSBT and construct a
payjoin proposal. It does not need to embed or depend on the v1
receiver’s typestates.

This commit introduces `PsbtContext` as a common abstraction for
proposal finalization and decouples the `ProvisionalProposal` and
`PayjoinProposal` typestates from the v1 typestate.
In the async. payjoin context, we need a mechanism to represent
integration errors that should not be relayed to the
counterparty. Currently, these errors are incorrectly represented as
`ReplayableError::ImplementationError`.

This commit corrects the error handling by updating `finalize_proposal`
to return `SessionError::IntegrationError`, which provides a more
accurate and semantically correct representation for integration
failures. Moreover, `SessionError`s cannot be accidentally converted and
relayed to the sender as a json reply as they do not impl the
conversion.
Copy link
Collaborator

@spacebear21 spacebear21 left a comment

Choose a reason for hiding this comment

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

ACK 08ae332

The failing CI check is due to server issues with Coveralls, bypassing this for merge.

@spacebear21 spacebear21 merged commit e0e6dff into payjoin:master Aug 13, 2025
15 of 16 checks passed
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.

3 participants