Skip to content

Commit 6a2386d

Browse files
authored
Simply return replay error on expiration (payjoin#1107)
2 parents e8bb1a7 + 0b295e8 commit 6a2386d

3 files changed

Lines changed: 30 additions & 53 deletions

File tree

payjoin/src/core/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ impl<SessionState: Debug, SessionEvent: Debug> std::fmt::Display
5151
Some(session) => write!(f, "Invalid event ({event:?}) for session ({session:?})",),
5252
None => write!(f, "Invalid first event ({event:?}) for session",),
5353
},
54+
Expired(time) => write!(f, "Session expired at {time:?}"),
5455
PersistenceFailure(e) => write!(f, "Persistence failure: {e}"),
5556
}
5657
}
@@ -75,6 +76,8 @@ pub(crate) enum InternalReplayError<SessionState, SessionEvent> {
7576
NoEvents,
7677
/// Invalid initial event
7778
InvalidEvent(Box<SessionEvent>, Option<Box<SessionState>>),
79+
/// Session is expired
80+
Expired(crate::time::Time),
7881
/// Application storage error
7982
PersistenceFailure(ImplementationError),
8083
}

payjoin/src/core/receive/v2/session.rs

Lines changed: 13 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use super::{ReceiveSession, SessionContext};
44
use crate::error::{InternalReplayError, ReplayError};
55
use crate::output_substitution::OutputSubstitution;
66
use crate::persist::SessionPersister;
7-
use crate::receive::v2::{extract_err_req, InternalSessionError, SessionError};
7+
use crate::receive::v2::{extract_err_req, SessionError};
88
use crate::receive::{common, InputPair, JsonReply, OriginalPayload, PsbtContext};
99
use crate::{ImplementationError, IntoUrl, PjUri, Request};
1010

@@ -44,27 +44,15 @@ where
4444
let history = SessionHistory::new(session_events.clone());
4545
let ctx = history.session_context();
4646
if ctx.expiration.elapsed() {
47-
// Session has expired: close the session and persist a fatal error
48-
let err = SessionError(InternalSessionError::Expired(ctx.expiration));
49-
persister
50-
.save_event(SessionEvent::SessionInvalid(err.to_string(), None).into())
51-
.map_err(|e| InternalReplayError::PersistenceFailure(ImplementationError::new(e)))?;
52-
persister
53-
.close()
54-
.map_err(|e| InternalReplayError::PersistenceFailure(ImplementationError::new(e)))?;
55-
56-
session_events.push(SessionEvent::SessionInvalid(err.to_string(), None));
57-
let history = SessionHistory::new(session_events);
58-
59-
return Ok((ReceiveSession::TerminalFailure, history));
47+
return Err(InternalReplayError::Expired(ctx.expiration).into());
6048
}
6149

6250
Ok((receiver, history))
6351
}
6452

6553
/// A collection of events that have occurred during a receiver's session.
6654
/// It is obtained by calling [replay_event_log].
67-
#[derive(Clone)]
55+
#[derive(Debug, Clone)]
6856
pub struct SessionHistory {
6957
events: Vec<SessionEvent>,
7058
}
@@ -372,21 +360,16 @@ mod tests {
372360

373361
#[test]
374362
fn test_replaying_session_creation_with_expired_session() -> Result<(), BoxError> {
375-
let session_context = SessionContext {
376-
expiration: (SystemTime::now() - Duration::from_secs(1)).try_into().unwrap(),
377-
..SHARED_CONTEXT.clone()
378-
};
379-
let test = SessionHistoryTest {
380-
events: vec![SessionEvent::Created(session_context.clone())],
381-
expected_session_history: SessionHistoryExpectedOutcome {
382-
psbt_with_fee_contributions: None,
383-
fallback_tx: None,
384-
expected_status: SessionStatus::Expired,
385-
},
386-
expected_receiver_state: ReceiveSession::TerminalFailure,
387-
};
388-
// TODO: should check for the expired error message off the session history
389-
run_session_history_test(test)
363+
let expiration = (SystemTime::now() - Duration::from_secs(1)).try_into().unwrap();
364+
let session_context = SessionContext { expiration, ..SHARED_CONTEXT.clone() };
365+
let persister = InMemoryTestPersister::<SessionEvent>::default();
366+
persister.save_event(SessionEvent::Created(session_context))?;
367+
368+
let err = replay_event_log(&persister).expect_err("session should be expired");
369+
let expected_err: ReplayError<ReceiveSession, SessionEvent> =
370+
InternalReplayError::Expired(expiration).into();
371+
assert_eq!(err.to_string(), expected_err.to_string());
372+
Ok(())
390373
}
391374

392375
#[test]

payjoin/src/core/send/v2/session.rs

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -40,24 +40,12 @@ where
4040
let history = SessionHistory::new(session_events.clone());
4141
let pj_param = history.pj_param();
4242
if pj_param.expiration().elapsed() {
43-
// Session has expired: close the session and persist a fatal error
44-
let session_event = SessionEvent::SessionInvalid("Session expired".to_string());
45-
persister
46-
.save_event(session_event.clone().into())
47-
.map_err(|e| InternalReplayError::PersistenceFailure(ImplementationError::new(e)))?;
48-
persister
49-
.close()
50-
.map_err(|e| InternalReplayError::PersistenceFailure(ImplementationError::new(e)))?;
51-
52-
session_events.push(session_event);
53-
let history = SessionHistory::new(session_events);
54-
55-
return Ok((SendSession::TerminalFailure, history));
43+
return Err(InternalReplayError::Expired(pj_param.expiration()).into());
5644
}
5745
Ok((sender, history))
5846
}
5947

60-
#[derive(Clone)]
48+
#[derive(Debug, Clone)]
6149
pub struct SessionHistory {
6250
events: Vec<SessionEvent>,
6351
}
@@ -225,12 +213,13 @@ mod tests {
225213
.unwrap();
226214
let reply_key = HpkeKeyPair::gen_keypair();
227215
let endpoint = sender.endpoint().clone();
228-
let fallback_tx = sender.psbt_ctx.original_psbt.clone().extract_tx_unchecked_fee_rate();
229216
let id = crate::uri::ShortId::try_from(&b"12345670"[..]).expect("valid short id");
217+
let expiration =
218+
(std::time::SystemTime::now() - std::time::Duration::from_secs(1)).try_into().unwrap();
230219
let pj_param = crate::uri::v2::PjParam::new(
231220
endpoint,
232221
id,
233-
(std::time::SystemTime::now() - std::time::Duration::from_secs(1)).try_into().unwrap(),
222+
expiration,
234223
crate::OhttpKeys(
235224
ohttp::KeyConfig::new(KEY_ID, KEM, Vec::from(SYMMETRIC)).expect("valid key config"),
236225
),
@@ -241,13 +230,15 @@ mod tests {
241230
psbt_ctx: sender.psbt_ctx.clone(),
242231
reply_key: reply_key.0,
243232
};
244-
let test = SessionHistoryTest {
245-
events: vec![SessionEvent::CreatedReplyKey(with_reply_key)],
246-
expected_session_history: SessionHistoryExpectedOutcome { fallback_tx, pj_param },
247-
expected_sender_state: SendSession::TerminalFailure,
248-
expected_error: Some("Session expired".to_string()),
249-
};
250-
run_session_history_test(test);
233+
let persister = InMemoryTestPersister::<SessionEvent>::default();
234+
persister
235+
.save_event(SessionEvent::CreatedReplyKey(with_reply_key))
236+
.expect("save_event should succeed");
237+
238+
let err = replay_event_log(&persister).expect_err("session should be expired");
239+
let expected_err: ReplayError<SendSession, SessionEvent> =
240+
InternalReplayError::Expired(expiration).into();
241+
assert_eq!(err.to_string(), expected_err.to_string());
251242
}
252243

253244
#[test]

0 commit comments

Comments
 (0)