Skip to content

Conversation

@arminsabouri
Copy link
Collaborator

@arminsabouri arminsabouri commented Jun 24, 2025

If a MaybeInputsOwned session event is present in the log then we know the session has persisted a "broadcastable" fallback tx by virute of successfully transitioning past UncheckedProposal via check_broadcast_suitability and persisting the result. This commit explicitly does not use the unchecked session event because the fallback at that typestate may fail consensus or policy checks.

I'm not a fan of the double find_map in SessionHistory. We could pub(crate) the v1 psbt on MaybeInputsOwned and duplicate a tiny bit of code (self.psbt.clone().extract_tx_unchecked_fee_rate()).

But something else seems a bit off to me. why is extract_tx_to_schedule_broadcast is on the unchecked typestate?
According to Bip77

At any point, either party may choose to broadcast the fallback transaction described by the Original PSBT instead of proceeding.

Wouldn't it make more sense for extract_tx_to_schedule_broadcast to live on the MaybeInputsOwned type state?

Resolves #798

@arminsabouri arminsabouri requested a review from spacebear21 June 24, 2025 17:47
@coveralls
Copy link
Collaborator

coveralls commented Jun 24, 2025

Pull Request Test Coverage Report for Build 15874355723

Details

  • 38 of 38 (100.0%) changed or added relevant lines in 1 file are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+0.06%) to 86.07%

Totals Coverage Status
Change from base Build 15862294544: 0.06%
Covered Lines: 7853
Relevant Lines: 9124

💛 - Coveralls

@DanGould
Copy link
Contributor

Wouldn't it make more sense for extract_tx_to_schedule_broadcast to live on the MaybeInputsOwned type state?

Let's get extract for broadcast after the typestate that checks broadcast. I would prefer a separate PR because that requires separate review to this.

arminsabouri added a commit to arminsabouri/rust-payjoin that referenced this pull request Jun 24, 2025
Per BIP-77:

> At any point, either party may choose to broadcast the fallback
> transaction described by the Original PSBT instead of proceeding.

However, the fallback transaction available at the `UncheckedProposal`
typestate may not be "broadcastable". Only after transitioning to
`MaybeInputsOwned` via `check_broadcast_suitability` can we determine
with high confidence that the fallback is valid for broadcast.

Related PR: [payjoin#799](payjoin#799)
@arminsabouri
Copy link
Collaborator Author

Drafting till we resolve #799 (comment)

@arminsabouri arminsabouri marked this pull request as draft June 24, 2025 19:57
arminsabouri added a commit to arminsabouri/rust-payjoin that referenced this pull request Jun 24, 2025
Per BIP-77:

> At any point, either party may choose to broadcast the fallback
> transaction described by the Original PSBT instead of proceeding.

However, the fallback transaction available at the `UncheckedProposal`
typestate may not be "broadcastable". Only after transitioning to
`MaybeInputsOwned` via `check_broadcast_suitability` can we determine
with high confidence that the fallback is valid for broadcast.

Related PR: [payjoin#799](payjoin#799)
spacebear21 added a commit that referenced this pull request Jun 24, 2025
Per BIP-77:

> At any point, either party may choose to broadcast the fallback
> transaction described by the Original PSBT instead of proceeding.

However, the fallback transaction available at the `UncheckedProposal`
typestate may not be "broadcastable". Only after transitioning to
`MaybeInputsOwned` via `check_broadcast_suitability` can we determine
with high confidence that the fallback is valid for broadcast.

Related PR: [#799](#799)
@nothingmuch
Copy link
Collaborator

I'm not a fan of the double find_map in SessionHistory. We could pub(crate) the v1 psbt on MaybeInputsOwned and duplicate a tiny bit of code (self.psbt.clone().extract_tx_unchecked_fee_rate()).

SGTM, but I also don't really think the find_map is that terrible.

At any point, either party may choose to broadcast the fallback transaction described by the Original PSBT instead of proceeding.

Hmm, I think that's a bug in the spec honestly.

Wouldn't it make more sense for extract_tx_to_schedule_broadcast to live on the MaybeInputsOwned type state?

Yes, and...

Given the purpose of MaybeInputsOwned, in this state the fallback transaction can technically be broadcast as testmempoolaccept succeeded, but it may be malicious, causing the receiver to spend their own coins, so I think it should actually be moved to MaybeInputsSeen, which if that fails then broadcasting the fallback txn can serve as a deterrence.

But perhaps we want a way of getting the unchecked PSBT that has "unsafe" in the name, and still make that available in UncheckedProposal because even if it's rejected by the local mempool it might serve some purpose?

@DanGould
Copy link
Contributor

the fallback transaction can technically be broadcast as testmempoolaccept succeeded, but it may be malicious, causing the receiver to spend their own coins

How would this be possible? The sender provides all sigs. They wouldn't have sigs for any receiver inputs.

@arminsabouri arminsabouri force-pushed the 798-expose-fallback-tx-using-receiver-session-history-object branch from 1c9bbb9 to feb8abf Compare June 25, 2025 10:49
If a `MaybeInputsOwned` session event is present in the log then we know
the session has persisted a "broadcastable" fallback tx by virute of
successfully transitioning past `UncheckedProposal` via calling
`check_broadcast_suitability` and persisting the result. This commit
explicitly does not use the unchecked session event because the fallback
at that typestate may fail consensus or policy checks.
@arminsabouri arminsabouri force-pushed the 798-expose-fallback-tx-using-receiver-session-history-object branch from feb8abf to 979ca78 Compare June 25, 2025 10:50
@arminsabouri arminsabouri marked this pull request as ready for review June 25, 2025 10:51
@arminsabouri
Copy link
Collaborator Author

Rebased and pushed

@arminsabouri
Copy link
Collaborator Author

Given the purpose of MaybeInputsOwned, in this state the fallback transaction can technically be broadcast as testmempoolaccept succeeded, but it may be malicious, causing the receiver to spend their own coins, so I think it should actually be moved to MaybeInputsSeen, which if that fails then broadcasting the fallback txn can serve as a deterrence.

Wouldnt the receiver have to sign? I suppose if its not clear what the fallback is they could mistakenly build a flow where the users sign it.

On a similar note, perhaps it should be move to OutputsUnknown. What good is a fallback tx that doesnt pay you.

@nothingmuch
Copy link
Collaborator

How would this be possible? The sender provides all sigs. They wouldn't have sigs for any receiver inputs.

The only thing I can think of is two concurrent sessions with cross talk which seems like a tagging attack... After one session succeeds the sender sends the payjoin tx as if it was a proposal tx, which from a loss of funds perspective is safe.

But honestly I didn't think about it too much when posting this, just took the docs for that typestate at face value. The check is from BIP 78's receiver checklist but I don't see any example

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.

utACK 979ca78

@spacebear21 spacebear21 merged commit a838c95 into payjoin:master Jun 25, 2025
13 checks passed
@arminsabouri arminsabouri deleted the 798-expose-fallback-tx-using-receiver-session-history-object branch June 25, 2025 17:31
shinghim pushed a commit to shinghim/rust-payjoin that referenced this pull request Jul 2, 2025
Per BIP-77:

> At any point, either party may choose to broadcast the fallback
> transaction described by the Original PSBT instead of proceeding.

However, the fallback transaction available at the `UncheckedProposal`
typestate may not be "broadcastable". Only after transitioning to
`MaybeInputsOwned` via `check_broadcast_suitability` can we determine
with high confidence that the fallback is valid for broadcast.

Related PR: [payjoin#799](payjoin#799)
node-smithxby72w added a commit to node-smithxby72w/rust-payjoin that referenced this pull request Sep 28, 2025
Per BIP-77:

> At any point, either party may choose to broadcast the fallback
> transaction described by the Original PSBT instead of proceeding.

However, the fallback transaction available at the `UncheckedProposal`
typestate may not be "broadcastable". Only after transitioning to
`MaybeInputsOwned` via `check_broadcast_suitability` can we determine
with high confidence that the fallback is valid for broadcast.

Related PR: [#799](payjoin/rust-payjoin#799)
node-smithxby72w added a commit to node-smithxby72w/rust-payjoin that referenced this pull request Sep 28, 2025
Per BIP-77:

> At any point, either party may choose to broadcast the fallback
> transaction described by the Original PSBT instead of proceeding.

However, the fallback transaction available at the `UncheckedProposal`
typestate may not be "broadcastable". Only after transitioning to
`MaybeInputsOwned` via `check_broadcast_suitability` can we determine
with high confidence that the fallback is valid for broadcast.

Related PR: [#799](payjoin/rust-payjoin#799)
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.

expose fallback tx using receiver session history object

5 participants