Skip to content

Commit fc18eb9

Browse files
Use time_provider in LSPS2 Service.
- is_past() on LSPSDateTime is changed so it receives a time_provider to get current time. - is_expired_opening_fee_params() now receives a time_provider to pass to is_past() - is_expired_opening_fee_params now does not depend on std being enabled. - LSPS2 service now passes around the time_provider to be able to use wherever necessary. - LSPS2 service now provides two new() methods: 1. a default new() that uses DefaultTimeProvider 2. a new_with_custom_time_provider() that expects a custom time_provider Devices with no access to std will need to use the latter
1 parent cca6fa0 commit fc18eb9

File tree

5 files changed

+89
-38
lines changed

5 files changed

+89
-38
lines changed

lightning-liquidity/src/lsps0/ser.rs

+3-8
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@ use lightning::util::ser::{LengthLimitedRead, LengthReadable, WithoutLength};
3030

3131
use bitcoin::secp256k1::PublicKey;
3232

33-
#[cfg(feature = "std")]
34-
use std::time::{SystemTime, UNIX_EPOCH};
33+
use crate::sync::Arc;
3534

3635
use serde::de::{self, MapAccess, Visitor};
3736
use serde::ser::SerializeStruct;
@@ -205,12 +204,8 @@ impl LSPSDateTime {
205204
}
206205

207206
/// Returns if the given time is in the past.
208-
#[cfg(feature = "std")]
209-
pub fn is_past(&self) -> bool {
210-
let now_seconds_since_epoch = SystemTime::now()
211-
.duration_since(UNIX_EPOCH)
212-
.expect("system clock to be ahead of the unix epoch")
213-
.as_secs();
207+
pub fn is_past(&self, time_provider: Arc<dyn TimeProvider>) -> bool {
208+
let now_seconds_since_epoch = time_provider.duration_since_epoch().as_secs();
214209
let datetime_seconds_since_epoch =
215210
self.0.timestamp().try_into().expect("expiration to be ahead of unix epoch");
216211
now_seconds_since_epoch > datetime_seconds_since_epoch

lightning-liquidity/src/lsps2/msgs.rs

+29-7
Original file line numberDiff line numberDiff line change
@@ -218,12 +218,32 @@ mod tests {
218218
use super::*;
219219

220220
use crate::alloc::string::ToString;
221+
use crate::lsps0::ser::TimeProvider;
221222
use crate::lsps2::utils::is_valid_opening_fee_params;
222-
223+
use crate::sync::Arc;
224+
use core::cell::RefCell;
223225
use core::str::FromStr;
226+
use core::time::Duration;
227+
228+
struct MockTimeProvider {
229+
current_time: RefCell<Duration>,
230+
}
231+
232+
impl MockTimeProvider {
233+
fn new(seconds_since_epoch: u64) -> Self {
234+
Self { current_time: RefCell::new(Duration::from_secs(seconds_since_epoch)) }
235+
}
236+
}
237+
238+
impl TimeProvider for MockTimeProvider {
239+
fn duration_since_epoch(&self) -> Duration {
240+
*self.current_time.borrow()
241+
}
242+
}
224243

225244
#[test]
226245
fn into_opening_fee_params_produces_valid_promise() {
246+
let time_provider = Arc::new(MockTimeProvider::new(1000));
227247
let min_fee_msat = 100;
228248
let proportional = 21;
229249
let valid_until = LSPSDateTime::from_str("2035-05-20T08:30:45Z").unwrap();
@@ -254,11 +274,12 @@ mod tests {
254274
assert_eq!(opening_fee_params.min_payment_size_msat, min_payment_size_msat);
255275
assert_eq!(opening_fee_params.max_payment_size_msat, max_payment_size_msat);
256276

257-
assert!(is_valid_opening_fee_params(&opening_fee_params, &promise_secret));
277+
assert!(is_valid_opening_fee_params(&opening_fee_params, &promise_secret, time_provider));
258278
}
259279

260280
#[test]
261281
fn changing_single_field_produced_invalid_params() {
282+
let time_provider = Arc::new(MockTimeProvider::new(1000));
262283
let min_fee_msat = 100;
263284
let proportional = 21;
264285
let valid_until = LSPSDateTime::from_str("2035-05-20T08:30:45Z").unwrap();
@@ -281,11 +302,12 @@ mod tests {
281302

282303
let mut opening_fee_params = raw.into_opening_fee_params(&promise_secret);
283304
opening_fee_params.min_fee_msat = min_fee_msat + 1;
284-
assert!(!is_valid_opening_fee_params(&opening_fee_params, &promise_secret));
305+
assert!(!is_valid_opening_fee_params(&opening_fee_params, &promise_secret, time_provider));
285306
}
286307

287308
#[test]
288309
fn wrong_secret_produced_invalid_params() {
310+
let time_provider = Arc::new(MockTimeProvider::new(1000));
289311
let min_fee_msat = 100;
290312
let proportional = 21;
291313
let valid_until = LSPSDateTime::from_str("2035-05-20T08:30:45Z").unwrap();
@@ -308,13 +330,13 @@ mod tests {
308330
let other_secret = [2u8; 32];
309331

310332
let opening_fee_params = raw.into_opening_fee_params(&promise_secret);
311-
assert!(!is_valid_opening_fee_params(&opening_fee_params, &other_secret));
333+
assert!(!is_valid_opening_fee_params(&opening_fee_params, &other_secret, time_provider));
312334
}
313335

314336
#[test]
315-
#[cfg(feature = "std")]
316-
// TODO: We need to find a way to check expiry times in no-std builds.
317337
fn expired_params_produces_invalid_params() {
338+
// 70 years since epoch
339+
let time_provider = Arc::new(MockTimeProvider::new(70 * 365 * 24 * 60 * 60)); // 1970 + 70 years
318340
let min_fee_msat = 100;
319341
let proportional = 21;
320342
let valid_until = LSPSDateTime::from_str("2023-05-20T08:30:45Z").unwrap();
@@ -336,7 +358,7 @@ mod tests {
336358
let promise_secret = [1u8; 32];
337359

338360
let opening_fee_params = raw.into_opening_fee_params(&promise_secret);
339-
assert!(!is_valid_opening_fee_params(&opening_fee_params, &promise_secret));
361+
assert!(!is_valid_opening_fee_params(&opening_fee_params, &promise_secret, time_provider));
340362
}
341363

342364
#[test]

lightning-liquidity/src/lsps2/service.rs

+40-8
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,12 @@ use core::ops::Deref;
1616
use core::sync::atomic::{AtomicUsize, Ordering};
1717

1818
use crate::events::EventQueue;
19+
20+
#[cfg(feature = "time")]
21+
use crate::lsps0::ser::DefaultTimeProvider;
22+
1923
use crate::lsps0::ser::{
20-
LSPSMessage, LSPSProtocolMessageHandler, LSPSRequestId, LSPSResponseError,
24+
LSPSMessage, LSPSProtocolMessageHandler, LSPSRequestId, LSPSResponseError, TimeProvider,
2125
JSONRPC_INTERNAL_ERROR_ERROR_CODE, JSONRPC_INTERNAL_ERROR_ERROR_MESSAGE,
2226
LSPS0_CLIENT_REJECTED_ERROR_CODE,
2327
};
@@ -400,18 +404,20 @@ struct OutboundJITChannel {
400404
user_channel_id: u128,
401405
opening_fee_params: LSPS2OpeningFeeParams,
402406
payment_size_msat: Option<u64>,
407+
time_provider: Arc<dyn TimeProvider>,
403408
}
404409

405410
impl OutboundJITChannel {
406411
fn new(
407412
payment_size_msat: Option<u64>, opening_fee_params: LSPS2OpeningFeeParams,
408-
user_channel_id: u128,
413+
user_channel_id: u128, time_provider: Arc<dyn TimeProvider>,
409414
) -> Self {
410415
Self {
411416
user_channel_id,
412417
state: OutboundJITChannelState::new(),
413418
opening_fee_params,
414419
payment_size_msat,
420+
time_provider,
415421
}
416422
}
417423

@@ -451,7 +457,8 @@ impl OutboundJITChannel {
451457
fn is_prunable(&self) -> bool {
452458
// We deem an OutboundJITChannel prunable if our offer expired and we haven't intercepted
453459
// any HTLCs initiating the flow yet.
454-
let is_expired = is_expired_opening_fee_params(&self.opening_fee_params);
460+
let is_expired =
461+
is_expired_opening_fee_params(&self.opening_fee_params, self.time_provider.clone());
455462
self.is_pending_initial_payment() && is_expired
456463
}
457464
}
@@ -481,13 +488,16 @@ impl PeerState {
481488
self.outbound_channels_by_intercept_scid.insert(intercept_scid, channel);
482489
}
483490

484-
fn prune_expired_request_state(&mut self) {
491+
fn prune_expired_request_state(&mut self, time_provider: Arc<dyn TimeProvider>) {
485492
self.pending_requests.retain(|_, entry| {
486493
match entry {
487494
LSPS2Request::GetInfo(_) => false,
488495
LSPS2Request::Buy(request) => {
489496
// Prune any expired buy requests.
490-
!is_expired_opening_fee_params(&request.opening_fee_params)
497+
!is_expired_opening_fee_params(
498+
&request.opening_fee_params,
499+
time_provider.clone(),
500+
)
491501
},
492502
}
493503
});
@@ -566,16 +576,32 @@ where
566576
peer_by_channel_id: RwLock<HashMap<ChannelId, PublicKey>>,
567577
total_pending_requests: AtomicUsize,
568578
config: LSPS2ServiceConfig,
579+
time_provider: Arc<dyn TimeProvider>,
569580
}
570581

571582
impl<CM: Deref> LSPS2ServiceHandler<CM>
572583
where
573584
CM::Target: AChannelManager,
574585
{
586+
#[cfg(feature = "time")]
575587
/// Constructs a `LSPS2ServiceHandler`.
576588
pub(crate) fn new(
577589
pending_messages: Arc<MessageQueue>, pending_events: Arc<EventQueue>, channel_manager: CM,
578590
config: LSPS2ServiceConfig,
591+
) -> Self {
592+
let time_provider = Arc::new(DefaultTimeProvider);
593+
Self::new_with_custom_time_provider(
594+
pending_messages,
595+
pending_events,
596+
channel_manager,
597+
config,
598+
time_provider,
599+
)
600+
}
601+
602+
pub(crate) fn new_with_custom_time_provider(
603+
pending_messages: Arc<MessageQueue>, pending_events: Arc<EventQueue>, channel_manager: CM,
604+
config: LSPS2ServiceConfig, time_provider: Arc<dyn TimeProvider>,
579605
) -> Self {
580606
Self {
581607
pending_messages,
@@ -586,6 +612,7 @@ where
586612
total_pending_requests: AtomicUsize::new(0),
587613
channel_manager,
588614
config,
615+
time_provider,
589616
}
590617
}
591618

@@ -737,6 +764,7 @@ where
737764
buy_request.payment_size_msat,
738765
buy_request.opening_fee_params,
739766
user_channel_id,
767+
self.time_provider.clone(),
740768
);
741769

742770
peer_state_lock
@@ -1192,7 +1220,11 @@ where
11921220
}
11931221

11941222
// TODO: if payment_size_msat is specified, make sure our node has sufficient incoming liquidity from public network to receive it.
1195-
if !is_valid_opening_fee_params(&params.opening_fee_params, &self.config.promise_secret) {
1223+
if !is_valid_opening_fee_params(
1224+
&params.opening_fee_params,
1225+
&self.config.promise_secret,
1226+
self.time_provider.clone(),
1227+
) {
11961228
let response = LSPS2Response::BuyError(LSPSResponseError {
11971229
code: LSPS2_BUY_REQUEST_INVALID_OPENING_FEE_PARAMS_ERROR_CODE,
11981230
message: "valid_until is already past OR the promise did not match the provided parameters".to_string(),
@@ -1334,7 +1366,7 @@ where
13341366
let is_prunable =
13351367
if let Some(inner_state_lock) = outer_state_lock.get(&counterparty_node_id) {
13361368
let mut peer_state_lock = inner_state_lock.lock().unwrap();
1337-
peer_state_lock.prune_expired_request_state();
1369+
peer_state_lock.prune_expired_request_state(self.time_provider.clone());
13381370
peer_state_lock.is_prunable()
13391371
} else {
13401372
return;
@@ -1349,7 +1381,7 @@ where
13491381
let mut outer_state_lock = self.per_peer_state.write().unwrap();
13501382
outer_state_lock.retain(|_, inner_state_lock| {
13511383
let mut peer_state_lock = inner_state_lock.lock().unwrap();
1352-
peer_state_lock.prune_expired_request_state();
1384+
peer_state_lock.prune_expired_request_state(self.time_provider.clone());
13531385
peer_state_lock.is_prunable() == false
13541386
});
13551387
}

lightning-liquidity/src/lsps2/utils.rs

+9-12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
//! Utilities for implementing the bLIP-52 / LSPS2 standard.
22
3+
use crate::sync::Arc;
4+
5+
use crate::lsps0::ser::TimeProvider;
36
use crate::lsps2::msgs::LSPS2OpeningFeeParams;
47
use crate::utils;
58

@@ -10,8 +13,9 @@ use bitcoin::hashes::{Hash, HashEngine};
1013
/// Determines if the given parameters are valid given the secret used to generate the promise.
1114
pub fn is_valid_opening_fee_params(
1215
fee_params: &LSPS2OpeningFeeParams, promise_secret: &[u8; 32],
16+
time_provider: Arc<dyn TimeProvider>,
1317
) -> bool {
14-
if is_expired_opening_fee_params(fee_params) {
18+
if is_expired_opening_fee_params(fee_params, time_provider) {
1519
return false;
1620
}
1721
let mut hmac = HmacEngine::<Sha256>::new(promise_secret);
@@ -28,17 +32,10 @@ pub fn is_valid_opening_fee_params(
2832
}
2933

3034
/// Determines if the given parameters are expired, or still valid.
31-
#[cfg_attr(not(feature = "std"), allow(unused_variables))]
32-
pub fn is_expired_opening_fee_params(fee_params: &LSPS2OpeningFeeParams) -> bool {
33-
#[cfg(feature = "std")]
34-
{
35-
fee_params.valid_until.is_past()
36-
}
37-
#[cfg(not(feature = "std"))]
38-
{
39-
// TODO: We need to find a way to check expiry times in no-std builds.
40-
false
41-
}
35+
pub fn is_expired_opening_fee_params(
36+
fee_params: &LSPS2OpeningFeeParams, time_provider: Arc<dyn TimeProvider>,
37+
) -> bool {
38+
fee_params.valid_until.is_past(time_provider)
4239
}
4340

4441
/// Computes the opening fee given a payment size and the fee parameters.

lightning-liquidity/tests/lsps2_integration_tests.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ mod common;
55
use common::{create_service_and_client_nodes, get_lsps_message, Node};
66

77
use lightning_liquidity::events::LiquidityEvent;
8-
use lightning_liquidity::lsps0::ser::LSPSDateTime;
8+
use lightning_liquidity::lsps0::ser::{DefaultTimeProvider, LSPSDateTime, TimeProvider};
99
use lightning_liquidity::lsps2::client::LSPS2ClientConfig;
1010
use lightning_liquidity::lsps2::event::{LSPS2ClientEvent, LSPS2ServiceEvent};
1111
use lightning_liquidity::lsps2::msgs::LSPS2RawOpeningFeeParams;
@@ -26,6 +26,7 @@ use bitcoin::secp256k1::{PublicKey, Secp256k1};
2626
use bitcoin::Network;
2727

2828
use std::str::FromStr;
29+
use std::sync::Arc;
2930
use std::time::Duration;
3031

3132
fn create_jit_invoice(
@@ -148,7 +149,7 @@ fn invoice_generation_flow() {
148149
.liquidity_manager
149150
.handle_custom_message(get_info_response, service_node_id)
150151
.unwrap();
151-
152+
let time_provider: Arc<(dyn TimeProvider + 'static)> = Arc::new(DefaultTimeProvider);
152153
let opening_params_event = client_node.liquidity_manager.next_event().unwrap();
153154
let opening_fee_params = match opening_params_event {
154155
LiquidityEvent::LSPS2Client(LSPS2ClientEvent::OpeningParametersReady {
@@ -159,7 +160,11 @@ fn invoice_generation_flow() {
159160
assert_eq!(request_id, get_info_request_id);
160161
assert_eq!(counterparty_node_id, service_node_id);
161162
let opening_fee_params = opening_fee_params_menu.first().unwrap().clone();
162-
assert!(is_valid_opening_fee_params(&opening_fee_params, &promise_secret));
163+
assert!(is_valid_opening_fee_params(
164+
&opening_fee_params,
165+
&promise_secret,
166+
time_provider
167+
));
163168
opening_fee_params
164169
},
165170
_ => panic!("Unexpected event"),

0 commit comments

Comments
 (0)