Skip to content

Reset the accounting record when a receiver session is recreated - #76

Open
chavic wants to merge 3 commits into
ValeraFinebits:masterfrom
chavic:chavic/bridge-session-generation
Open

Reset the accounting record when a receiver session is recreated#76
chavic wants to merge 3 commits into
ValeraFinebits:masterfrom
chavic:chavic/bridge-session-generation

Conversation

@chavic

@chavic chavic commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

When an invoice gets a fresh receiver session (the previous one was removed and checkout re-requested a payment URL), the accounting record kept the previous session's tracking data. The fallback-attach guard (FallbackTransactionId != null) then blocked the new session's fallback from attaching, while the settlement script and expected final transaction did get overwritten — so the record ended up describing two sessions at once, and payment reconciliation could target the wrong outpoint.

Creating a fresh session now resets the record for the new attempt: pending records lose the previous session's fallback/script/expected-transaction data, and an expired record that never learned its final transaction is revived with the new deadline. Reconciled records are final, and failed or expired records that already awaited a final transaction are deliberately left alone so they stay visible for operator review (see #74).

The reset only fires on the fresh-session path — reusing a persisted session leaves the record untouched.

🤖 Generated with Claude Code

@chavic
chavic force-pushed the chavic/bridge-session-generation branch from 8e377e9 to f19eafd Compare July 6, 2026 15:31
@chavic
chavic force-pushed the chavic/bridge-session-generation branch from f19eafd to 0724fde Compare July 16, 2026 10:46
@chavic
chavic marked this pull request as ready for review July 16, 2026 12:48
@ValeraFinebits
ValeraFinebits self-requested a review July 17, 2026 15:53

@ValeraFinebits ValeraFinebits left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks for addressing session recreation. Before merging, I think we should add lifecycle-focused tests covering the interaction with the accounting worker, rather than testing the field reset in isolation only.

The main case I’m concerned about is:

  • a previous session has already reached PendingFinalTransaction;
  • a fresh receiver session is created and resets the bridge;
  • the previous session’s final transaction becomes observable afterwards.

That test would make it explicit whether the old accounting flow is intentionally abandoned or is still expected to reconcile correctly.

Since session creation and the background worker may overlap even in a single-instance deployment, I think two additional cases are also worth covering:

  • ResetForNewSessionAsync racing with ExpirePendingAsync or reconciliation, so a stale worker write cannot overwrite the reset state;
  • a failure or cancellation after session persistence but before the accounting reset completes, to ensure a retry does not reuse a persisted session with stale accounting data.

My main concern is making the ownership and lifecycle boundary between the "old session" and the "new session" explicit in tests.

@ValeraFinebits

Copy link
Copy Markdown
Owner

@chavic could you please resolve the conflicts?

@chavic
chavic force-pushed the chavic/bridge-session-generation branch from 7fff548 to 54b9f4e Compare July 26, 2026 14:32
chavic added 3 commits July 28, 2026 16:06
Move the submodule from f4a6008a to the payjoin-1.0.0-rc.6 release
commit and adapt to the FFI surface changes:

- PjParseException is now UriParseException.
- BuildRecommended throws SenderInputException, and persisted sender
  errors are now SenderPersistedException.Transient/Fatal wrapping a
  SenderException, so the receiver-rejection handling matches
  SenderException.Response through the wrapper.
- IPayjoinProposal drops UtxosToBeLocked and gains
  ProposalTxidIsStable.

rc.5-format closed-session logs recorded scripts and witnesses in
Closed(Success); rc.6 records a txid, so such logs fail replay after
this bump. Every replay site already catches ReceiverReplayException
and removes the session, which is the same terminal outcome a closed
session gets on the next poller tick.
An invoice can get a fresh receiver session while its accounting record
still carries tracking data from a previous session. The fallback-attach
guard would then hold on to the old fallback while later writes replace
the settlement script and expected transaction, leaving one record
describing two different sessions and reconciliation targeting the wrong
outpoint. Creating a fresh session now clears the previous session's
tracking data from a pending record and revives an unexpired-by-review
record for the new attempt; reconciled records are final and failed or
expired records that already awaited a final transaction stay untouched
for operator review.
Resetting the accounting record for a fresh session treated any pending
bridge as replaceable, but a bridge that already knows its expected
final transaction describes a signed proposal the previous session
handed to the sender. That transaction can still confirm, and the
recorded expectation is the only thing that makes its settlement output
creditable, so wiping it would make the payment invisible. Armed
bridges now survive recreation and the new session's own writes take
the record over stage by stage; unarmed pending bridges reset as
before.
@chavic
chavic force-pushed the chavic/bridge-session-generation branch from 54b9f4e to 73a029c Compare July 29, 2026 08:43
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.

2 participants