Skip to content

Remove Completed at history column#1683

Open
benalleng wants to merge 1 commit into
payjoin:masterfrom
benalleng:cli-expiry-history
Open

Remove Completed at history column#1683
benalleng wants to merge 1 commit into
payjoin:masterfrom
benalleng:cli-expiry-history

Conversation

@benalleng

@benalleng benalleng commented Jun 25, 2026

Copy link
Copy Markdown
Collaborator

This PR removes the completed at column and db entry to instead rely more heavily on SessionStatus::Active as the source of truth for what can be resumed.

This additionally adds 2 minor print statements to more easily track your session_id when trying to keep track or cancel it later on. As well as a Fallback Tx available suffix in the history for better understanding of what happened in an aborted session.

Here are the primary screenshots demonstrating what this does.

Receiver
image

Sender
image

Coded with help from GLM-5.2

Pull Request Checklist

Please confirm the following before requesting review:

@benalleng benalleng requested a review from spacebear21 June 25, 2026 16:40
@benalleng benalleng force-pushed the cli-expiry-history branch 2 times, most recently from d5e0f58 to 48013e2 Compare June 25, 2026 16:54
@coveralls

coveralls commented Jun 25, 2026

Copy link
Copy Markdown
Collaborator

Coverage Report for CI Build 28291062969

Coverage increased (+0.5%) to 86.016%

Details

  • Coverage increased (+0.5%) from the base build.
  • Patch coverage: 7 uncovered changes across 2 files (40 of 47 lines covered, 85.11%).
  • 1 coverage regression across 1 file.

Uncovered Changes

File Changed Covered %
payjoin-cli/src/app/v2/mod.rs 11 5 45.45%
payjoin-cli/src/db/v2.rs 34 33 97.06%
Total (3 files) 47 40 85.11%

Coverage Regressions

1 previously-covered line in 1 file lost coverage.

File Lines Losing Coverage Coverage
payjoin-cli/src/app/v2/mod.rs 1 56.91%

Coverage Stats

Coverage Status
Relevant Lines: 15060
Covered Lines: 12954
Line Coverage: 86.02%
Coverage Strength: 363.43 hits per line

💛 - Coveralls

@zealsham zealsham left a comment

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.

tACK

@caarloshenriq caarloshenriq left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

tACK 48013e2

Reviewed and tested the changes locally, works as expected.

@spacebear21 spacebear21 left a comment

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.

cACK however there seems to be an unrelated behavior change to the resume command that snuck in here, I think because of the move the SessionStatus as a source of truth.

Comment thread payjoin-cli/src/app/v2/mod.rs Outdated
ReceiverSessionOutcome::Success(_) => "Session success, Payjoin proposal was broadcasted",
ReceiverSessionOutcome::FallbackBroadcasted => "Fallback broadcasted",
ReceiverSessionOutcome::Aborted => if has_fallback_tx {
"Session aborted, Fallback Tx available".to_string()

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.

Is this a call to action? How do I obtain the fallback tx to broadcast?

Also, nit:

Suggested change
"Session aborted, Fallback Tx available".to_string()
"Session aborted, fallback transaction available".to_string()

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.

running cancel on this session again will print the raw_tx to be broadcast again. from #1673

Comment on lines 293 to +302

if recv_session_ids.is_empty() && send_session_ids.is_empty() {
println!("No sessions to resume.");
return Ok(());
}

let mut tasks: Vec<(String, tokio::task::JoinHandle<Result<()>>)> = Vec::new();

// Process receiver sessions
for session_id in recv_session_ids {
let self_clone = self.clone();
let recv_persister = ReceiverPersister::from_id(self.db.clone(), session_id.clone());
match replay_receiver_event_log(&recv_persister) {
Ok((receiver_state, _)) => {
tasks.push((
session_id.to_string(),
tokio::spawn(async move {
self_clone
.process_receiver_session(receiver_state, &recv_persister)
.await
}),
));
Ok((receiver_state, history)) => {
if history.status() == ReceiverSessionStatus::Active {

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.

I'm not sure I understand the rationale for this behavior change in resume. It seems unrelated to the history display changes and may have snuck in here unintentionally?

This was the result of resume on master:

❯ cargo run -- resume
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.12s
     Running `/Users/spacebear/Projects/rust-payjoin/target/debug/payjoin-cli resume`                                                                                                                                 
Polling receive request...                                                                                                                                                                                           
Session 26 error: No valid relays available

❯ cargo run -- history
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.12s
     Running `/Users/spacebear/Projects/rust-payjoin/target/debug/payjoin-cli history`
Session ID   Sender/Receiver           Completed At    Status
26           Receiver                  Not Completed   Waiting for original proposal
1            Receiver                  1778275755      Session expired at Time(Time(1761155560))
2            Receiver                  1778275755      Session expired at Time(Time(1761234352))
3            Receiver                  1778275755      Session expired at Time(Time(1761334774))
4            Receiver                  1778275755      Session expired at Time(Time(1761335340))
5            Receiver                  1778275755      Session expired at Time(Time(1761335391))
6            Receiver                  1778275755      Session expired at Time(Time(1761336435))
7            Receiver                  1778275755      Session expired at Time(Time(1761336442))
8            Receiver                  1778275755      Session expired at Time(Time(1761336448))
9            Receiver                  1778275755      Session expired at Time(Time(1761336794))
10           Receiver                  1778275755      Session expired at Time(Time(1761336825))
11           Receiver                  1778275755      Session expired at Time(Time(1761337402))
12           Receiver                  1761318523      Session success, Payjoin proposal was broadcasted
13           Receiver                  1778275755      Session expired at Time(Time(1772828046))
14           Receiver                  1778275755      Session expired at Time(Time(1772993300))
15           Receiver                  1772907232      Session success, Payjoin proposal was broadcasted
16           Receiver                  1778275755      Session expired at Time(Time(1772993673))
17           Receiver                  1772907521      Session success, Payjoin proposal was broadcasted
18           Receiver                  1773716774      Session success, Payjoin proposal was broadcasted
19           Receiver                  1778275755      Session expired at Time(Time(1773803253))
20           Receiver                  1778275755      Session expired at Time(Time(1773803383))
21           Receiver                  1774571740      Session success, Payjoin proposal was broadcasted
22           Receiver                  1778275755      Session expired at Time(Time(1774658178))
23           Receiver                  1776873519      Session success, Payjoin proposal was broadcasted
24           Receiver                  1778275755      Session expired at Time(Time(1776960197))
25           Receiver                  1782328670      Session expired at Time(Time(1778362082))
27           Receiver                  1782334039      Session success, Payjoin proposal was broadcasted
28           Receiver                  1782334213      Session success, Payjoin proposal was broadcasted
29           Receiver                  1782334476      Session success, Payjoin proposal was broadcasted

On this branch:

❯ cargo run -- resume
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.11s
     Running `/Users/spacebear/Projects/rust-payjoin/target/debug/payjoin-cli resume`
Session 1 receiver expired.
2026-06-25T20:46:57.479810Z ERROR payjoin_cli::app::v2: Closed failed receiver session: 1
Session 2 receiver expired.
2026-06-25T20:46:57.480654Z ERROR payjoin_cli::app::v2: Closed failed receiver session: 2
Session 3 receiver expired.
2026-06-25T20:46:57.480962Z ERROR payjoin_cli::app::v2: Closed failed receiver session: 3
Session 4 receiver expired.
2026-06-25T20:46:57.481245Z ERROR payjoin_cli::app::v2: Closed failed receiver session: 4
Session 5 receiver expired.
2026-06-25T20:46:57.481532Z ERROR payjoin_cli::app::v2: Closed failed receiver session: 5
Session 6 receiver expired.
2026-06-25T20:46:57.481799Z ERROR payjoin_cli::app::v2: Closed failed receiver session: 6
Session 7 receiver expired.
2026-06-25T20:46:57.482092Z ERROR payjoin_cli::app::v2: Closed failed receiver session: 7
Session 8 receiver expired.
2026-06-25T20:46:57.482354Z ERROR payjoin_cli::app::v2: Closed failed receiver session: 8
Session 9 receiver expired.
2026-06-25T20:46:57.483102Z ERROR payjoin_cli::app::v2: Closed failed receiver session: 9
Session 10 receiver expired.
2026-06-25T20:46:57.483344Z ERROR payjoin_cli::app::v2: Closed failed receiver session: 10
Session 11 receiver expired.
2026-06-25T20:46:57.483586Z ERROR payjoin_cli::app::v2: Closed failed receiver session: 11
Session 13 receiver expired.
2026-06-25T20:46:57.484437Z ERROR payjoin_cli::app::v2: Closed failed receiver session: 13
Session 14 receiver expired.
2026-06-25T20:46:57.484670Z ERROR payjoin_cli::app::v2: Closed failed receiver session: 14
Session 16 receiver expired.
2026-06-25T20:46:57.485433Z ERROR payjoin_cli::app::v2: Closed failed receiver session: 16
Session 19 receiver expired.
2026-06-25T20:46:57.486784Z ERROR payjoin_cli::app::v2: Closed failed receiver session: 19
Session 20 receiver expired.
2026-06-25T20:46:57.487024Z ERROR payjoin_cli::app::v2: Closed failed receiver session: 20
Session 22 receiver expired.
2026-06-25T20:46:57.487895Z ERROR payjoin_cli::app::v2: Closed failed receiver session: 22
Session 24 receiver expired.
2026-06-25T20:46:57.488668Z ERROR payjoin_cli::app::v2: Closed failed receiver session: 24
Session 25 receiver expired.
2026-06-25T20:46:57.489350Z ERROR payjoin_cli::app::v2: Closed failed receiver session: 25
Session 26 receiver expired.
2026-06-25T20:46:57.489604Z ERROR payjoin_cli::app::v2: Closed failed receiver session: 26
No sessions to resume.

❯ cargo run -- history
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.11s
     Running `/Users/spacebear/Projects/rust-payjoin/target/debug/payjoin-cli history`
Session ID   Sender/Receiver           Status
1            Receiver                  Session aborted, Fallback Tx available
2            Receiver                  Session aborted, Fallback Tx available
3            Receiver                  Session aborted
4            Receiver                  Session aborted
5            Receiver                  Session aborted
6            Receiver                  Session aborted
7            Receiver                  Session aborted
8            Receiver                  Session aborted
9            Receiver                  Session aborted, Fallback Tx available
10           Receiver                  Session aborted
11           Receiver                  Session aborted
12           Receiver                  Session success, Payjoin proposal was broadcasted
13           Receiver                  Session aborted
14           Receiver                  Session aborted
15           Receiver                  Session success, Payjoin proposal was broadcasted
16           Receiver                  Session aborted
17           Receiver                  Session success, Payjoin proposal was broadcasted
18           Receiver                  Session success, Payjoin proposal was broadcasted
19           Receiver                  Session aborted
20           Receiver                  Session aborted
21           Receiver                  Session success, Payjoin proposal was broadcasted
22           Receiver                  Session aborted
23           Receiver                  Session success, Payjoin proposal was broadcasted
24           Receiver                  Session aborted
25           Receiver                  Session aborted, Fallback Tx available
26           Receiver                  Session aborted
27           Receiver                  Session success, Payjoin proposal was broadcasted
28           Receiver                  Session success, Payjoin proposal was broadcasted
29           Receiver                  Session success, Payjoin proposal was broadcasted
30           Receiver                  Session success, Payjoin proposal was broadcasted

@benalleng benalleng Jun 26, 2026

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.

Taking a closer look at this the expired session status only lives before that session is resumed. 3df5eea forces the expired status to be inside the same match block with other outcomes that are checked with status(). However because we automatically error on an expired session and immediately close it this then changes the outcome to closed which now always fires before expired in the status() match block.

The screenshot below shows the history before and after a resume.
image

As an aside I think that now close_failed_session() is working properly though the tracing::error!() is jarring on an expired session, should this be info or debug. I went with debug for now

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.

Here is an updated screenshot
image

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.

I also wonder if close_failed_session shouldn't be using ReceiveSession::cancel() instead of SessionPersister::close() directly, forcing the PendingFallback path if a fallback transaction exists.

Removes the completed_at DB column and the "Completed At" history display column. Session status is now derived from the event log via SessionHistory::status(); resume only acts on SessionStatus::Active. SenderPersister and ReceiverPersister close() append a Closed(Aborted) event so expired/failed sessions read as terminal on subsequent replays rather than re-erroring.
@benalleng benalleng force-pushed the cli-expiry-history branch 2 times, most recently from 9107045 to 416e949 Compare June 27, 2026 13:44
@benalleng

Copy link
Copy Markdown
Collaborator Author

This PR is beginning to grow out of scope I am going to break out the cli UX seperately from the pure remove completed_at change

@benalleng benalleng force-pushed the cli-expiry-history branch from 416e949 to 6e55225 Compare June 27, 2026 13:47
@benalleng benalleng mentioned this pull request Jun 27, 2026
2 tasks
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.

Replaying the event log results in "expired" error for successfully completed sessions

5 participants