Skip to content

Commit d0bd8af

Browse files
Handle interrupts across the whole receive session
Wrap process_receiver_session in a select against the interrupt channel in receive_payjoin, mirroring how send_payjoin guards the sender driver. This is safe at any phase: transitions persist to the event log with no await between transition and save, so an interrupted session resumes from its last saved state via the resume command. The interrupt message now includes the session id, and read_from_directory becomes a plain single-step function like the other state handlers.
1 parent 0c3dd5d commit d0bd8af

1 file changed

Lines changed: 29 additions & 33 deletions

File tree

payjoin-cli/src/app/v2/mod.rs

Lines changed: 29 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -293,9 +293,17 @@ impl AppTrait for App {
293293
println!("Request Payjoin by sharing this Payjoin Uri:");
294294
println!("{pj_uri}");
295295

296-
self.process_receiver_session(ReceiveSession::Initialized(session.clone()), &persister)
297-
.await?;
298-
Ok(())
296+
let mut interrupt = self.interrupt.clone();
297+
tokio::select! {
298+
res = self.process_receiver_session(ReceiveSession::Initialized(session.clone()), &persister) => res,
299+
_ = interrupt.changed() => {
300+
let id = persister.session_id();
301+
println!(
302+
"Session {id} interrupted. Call the `resume` command to resume all sessions, or `payjoin-cli cancel {id}` to cancel the session."
303+
);
304+
Err(anyhow!("Interrupted"))
305+
}
306+
}
299307
}
300308

301309
async fn resume_payjoins(&self, session_id: Option<SessionId>) -> Result<()> {
@@ -806,43 +814,31 @@ impl App {
806814
}
807815
}
808816

809-
#[allow(clippy::incompatible_msrv)]
810817
async fn read_from_directory(
811818
&self,
812819
session: Receiver<Initialized>,
813820
persister: &ReceiverPersister,
814821
) -> Result<ReceiveSession> {
815-
let poll_once = async {
816-
println!("Polling receive request...");
817-
let (ohttp_response, context) =
818-
self.post_via_relay(|relay| session.create_poll_request(relay)).await?;
819-
let state_transition = session
820-
.process_response(ohttp_response.bytes().await?.to_vec().as_slice(), context)
821-
.save(persister);
822-
match state_transition {
823-
Ok(OptionalTransitionOutcome::Progress(next_state)) => {
824-
println!("Got a request from the sender. Responding with a Payjoin proposal.");
825-
Ok(ReceiveSession::UncheckedOriginalPayload(next_state))
826-
}
827-
Ok(OptionalTransitionOutcome::Stasis(current_state)) =>
828-
Ok(ReceiveSession::Initialized(current_state)),
829-
Err(e) if e.is_transient() => {
830-
tracing::debug!("Transient error polling for request, retrying: {e:?}");
831-
let session =
832-
e.transient_state().expect("transient error carries current state");
833-
tokio::time::sleep(TRANSIENT_RETRY_DELAY).await;
834-
Ok(ReceiveSession::Initialized(session))
835-
}
836-
Err(e) => Err(e.into()),
822+
println!("Polling receive request...");
823+
let (ohttp_response, context) =
824+
self.post_via_relay(|relay| session.create_poll_request(relay)).await?;
825+
let state_transition = session
826+
.process_response(ohttp_response.bytes().await?.to_vec().as_slice(), context)
827+
.save(persister);
828+
match state_transition {
829+
Ok(OptionalTransitionOutcome::Progress(next_state)) => {
830+
println!("Got a request from the sender. Responding with a Payjoin proposal.");
831+
Ok(ReceiveSession::UncheckedOriginalPayload(next_state))
837832
}
838-
};
839-
let mut interrupt = self.interrupt.clone();
840-
tokio::select! {
841-
res = poll_once => res,
842-
_ = interrupt.changed() => {
843-
println!("Interrupted. Call the `resume` command to resume all sessions.");
844-
Err(anyhow!("Interrupted"))
833+
Ok(OptionalTransitionOutcome::Stasis(current_state)) =>
834+
Ok(ReceiveSession::Initialized(current_state)),
835+
Err(e) if e.is_transient() => {
836+
tracing::debug!("Transient error polling for request, retrying: {e:?}");
837+
let session = e.transient_state().expect("transient error carries current state");
838+
tokio::time::sleep(TRANSIENT_RETRY_DELAY).await;
839+
Ok(ReceiveSession::Initialized(session))
845840
}
841+
Err(e) => Err(e.into()),
846842
}
847843
}
848844

0 commit comments

Comments
 (0)