Cli cancel UX#1700
Conversation
|
@spacebear21 I am most curious of your thoughts on 9b718ac and whether you think this is appropriate for both the |
Coverage Report for CI Build 29342380627Coverage decreased (-0.2%) to 85.777%Details
Uncovered Changes
Coverage Regressions1 previously-covered line in 1 file lost coverage.
Coverage Stats
💛 - Coveralls |
bc1cindy
left a comment
There was a problem hiding this comment.
looks solid
two notes inline on the expiry paths.
the new mid-loop cancel/broadcast and the fallback_available display aren't covered by tests
adding a test driving an expired session through the sender path would lock in the behavior, wdyt?
c91653e to
597ffe4
Compare
b241374 to
8d063a0
Compare
02f8cb5 to
7f0c249
Compare
| println!("Error: Sender session expired: {}", persister.session_id()); | ||
| Ok(()) |
There was a problem hiding this comment.
"Error: Sender session expired" printed here but the fn returns Ok(())
|
The issue with automating fallback is perhaps that another session could spend the same coin between sessions. For example if bitcoind was restarted and another payjoin session spent the same coin. Then you might have a fallback automatically broadcast that doesn't pay attention to wallet state? Just seems like some complexity that this would need to take responsibility for. Not completely confident in the motivation and my tendency is to prefer leaving things manual unless the automation is a no brainer default. |
9de5aeb to
a4e6f34
Compare
xstoicunicornx
left a comment
There was a problem hiding this comment.
General nit: a lot of the sender/receiver verbiage has been removed from tracing and print statements, was there a reason for this? Maybe I can take as a followup - as I have been wanting to standardize the printing more in the CLI so that every printed line starts with the session role and ID.
Rather than creating repetitive relay selection/expiration code (and in some places loosing the loop it seems) why not adapt post_via_relay to still be usable in all contexts? For example (code created with Claude Opus 4.8):
/// A request-construction error that can report whether it was caused by
/// session expiry. Implemented by the sender/receiver request-building error
/// types so `post_via_relay` can hand expiry back to the caller (which owns the
/// typestate needed to react) instead of flattening it into `anyhow::Error`.
trait RequestExpiry {
fn expired(&self) -> bool;
}
impl RequestExpiry for payjoin::send::v2::CreateRequestError {
fn expired(&self) -> bool { self.is_expired() }
}
impl RequestExpiry for payjoin::receive::v2::CreateRequestError {
fn expired(&self) -> bool { self.is_expired() }
}
impl RequestExpiry for payjoin::receive::v2::SessionError {
fn expired(&self) -> bool { self.is_expired() }
}
/// Outcome of building and posting a request via `post_via_relay`. HTTP
/// failures are retried against other relays inside the helper; only session
/// expiry and fatal build errors escape to the caller.
enum RelayPost<T> {
Posted(reqwest::Response, T),
Expired,
}and then:
async fn post_via_relay<F, T, E>(&self, mut build: F) -> Result<RelayPost<T>>
where
F: FnMut(&str) -> std::result::Result<(payjoin::Request, T), E>,
E: RequestExpiry + Into<anyhow::Error>,
{
loop {
let relay = self.mailroom_manager.choose_relay()?;
let (req, ctx) = match build(relay.as_str()) {
Ok(r) => r,
Err(e) if e.expired() => return Ok(RelayPost::Expired),
Err(e) => return Err(e.into()),
};
match self.post_request(req).await {
Ok(resp) => return Ok(RelayPost::Posted(resp, ctx)),
Err(e) => {
tracing::debug!("Request to relay {relay} failed: {e:?}");
self.mailroom_manager.add_failed_relay(relay);
}
}
}
}
}See full (half-reviewed) implementation here in last commit https://github.com/xstoicunicornx/rust-payjoin/tree/pr1700-mod
I'm fine dropping the last commit if you want to take this up. I've gotten a bit lost in the sauce having redesigned this PR and I'm not sure what I think is best for the readability |
28f611c to
84bea22
Compare
84bea22 to
2161742
Compare
xstoicunicornx
left a comment
There was a problem hiding this comment.
Few more questions but overall looking good.
Before this change there was no reasonable way of knowing which session had a fallback transaction available meaning that if the session was closed with `payjoin-cli cancel # --no-broadcast` a user would have to enumerate through all the sessions trying to look for which session had the fallback tx calling back the sessions with `payjoin-cli history`.
This ensures that the session id is printed immediately after session id creation ensuring that the session id is displayed at the earliest moment it is readable.
eaa357a to
2792915
Compare
|
Got caught up a bit in rebase hell on this one, thanks for being diligent in your reveiws @xstoicunicornx I think this is good for a re-review |
xstoicunicornx
left a comment
There was a problem hiding this comment.
A couple more things I noticed. The comment in finalize_expired_receiver is just a suggestion but there are other ways to implement that don't require as much of a refactor.
| RelayPost::Posted(resp, ctx) => (resp, ctx), | ||
| RelayPost::Expired => { | ||
| let session_id = persister.session_id(); | ||
| Self::close_failed_session(persister, &session_id, "receiver"); |
There was a problem hiding this comment.
I think this needs to handle sessions that have a fallback tx.
| "Session {session_id} failed to deliver error reply. Broadcast the fallback transaction manually:\n{}", | ||
| serialize_hex(pending.fallback_tx()) | ||
| ); | ||
| pending.close().save(persister)?; |
There was a problem hiding this comment.
Shouldn't we be avoiding closing automatically?
Previously the cli only checked for expiry at program start during long polling loops there was not an expiry check leading to the possiblity of an indefinite poll past expiry where the counterparty had already given up. This will automatically close the session now whenever the expiry time is crossed. This also adds an `--expire-in` flag for the receiver to both test this functionality and demonstrate that the receiver is not actually fixed to a 24hr expiration and is in controll of this expiry time.
This moves all session_id prints to be formatted the same way for easier readability and copying. Also fixes some stale print regarding resuming using `resume --session-id` instead of `send`. Lastly fixes the history role column to be only as wide as it needs to be.
268bf10 to
2201bdc
Compare
xstoicunicornx
left a comment
There was a problem hiding this comment.
utACK 2201bdc
Reviewed and tested the code. This implements expiry handling at every point where expiry could get hit rather than just upon startup.
Will be taking on followup to standardize the messages printed to the user.
This PR builds on #1688 but reverts the
completed_atDB column removal.payjoin-cli historyfor rebroadcasting.example output. custom timeout of 20s to demonstrate automatic cancel logic

Coded with help from GLM-5.2
Pull Request Checklist
Please confirm the following before requesting review:
AI
in the body of this PR.