Reset the accounting record when a receiver session is recreated - #76
Reset the accounting record when a receiver session is recreated#76chavic wants to merge 3 commits into
Conversation
8e377e9 to
f19eafd
Compare
f19eafd to
0724fde
Compare
ValeraFinebits
left a comment
There was a problem hiding this comment.
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:
ResetForNewSessionAsyncracing withExpirePendingAsyncor 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.
|
@chavic could you please resolve the conflicts? |
7fff548 to
54b9f4e
Compare
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.
54b9f4e to
73a029c
Compare
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