Skip to content

Commit 33cb0eb

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 33cb0eb

File tree

5 files changed

+87
-39
lines changed

5 files changed

+87
-39
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

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

1818
use crate::events::EventQueue;
19+
1920
use crate::lsps0::ser::{
20-
LSPSMessage, LSPSProtocolMessageHandler, LSPSRequestId, LSPSResponseError,
21-
JSONRPC_INTERNAL_ERROR_ERROR_CODE, JSONRPC_INTERNAL_ERROR_ERROR_MESSAGE,
21+
DefaultTimeProvider, LSPSMessage, LSPSProtocolMessageHandler, LSPSRequestId, LSPSResponseError,
22+
TimeProvider, JSONRPC_INTERNAL_ERROR_ERROR_CODE, JSONRPC_INTERNAL_ERROR_ERROR_MESSAGE,
2223
LSPS0_CLIENT_REJECTED_ERROR_CODE,
2324
};
2425
use crate::lsps2::event::LSPS2ServiceEvent;
@@ -400,18 +401,20 @@ struct OutboundJITChannel {
400401
user_channel_id: u128,
401402
opening_fee_params: LSPS2OpeningFeeParams,
402403
payment_size_msat: Option<u64>,
404+
time_provider: Arc<dyn TimeProvider>,
403405
}
404406

405407
impl OutboundJITChannel {
406408
fn new(
407409
payment_size_msat: Option<u64>, opening_fee_params: LSPS2OpeningFeeParams,
408-
user_channel_id: u128,
410+
user_channel_id: u128, time_provider: Arc<dyn TimeProvider>,
409411
) -> Self {
410412
Self {
411413
user_channel_id,
412414
state: OutboundJITChannelState::new(),
413415
opening_fee_params,
414416
payment_size_msat,
417+
time_provider,
415418
}
416419
}
417420

@@ -451,7 +454,8 @@ impl OutboundJITChannel {
451454
fn is_prunable(&self) -> bool {
452455
// We deem an OutboundJITChannel prunable if our offer expired and we haven't intercepted
453456
// any HTLCs initiating the flow yet.
454-
let is_expired = is_expired_opening_fee_params(&self.opening_fee_params);
457+
let is_expired =
458+
is_expired_opening_fee_params(&self.opening_fee_params, self.time_provider.clone());
455459
self.is_pending_initial_payment() && is_expired
456460
}
457461
}
@@ -481,13 +485,16 @@ impl PeerState {
481485
self.outbound_channels_by_intercept_scid.insert(intercept_scid, channel);
482486
}
483487

484-
fn prune_expired_request_state(&mut self) {
488+
fn prune_expired_request_state(&mut self, time_provider: Arc<dyn TimeProvider>) {
485489
self.pending_requests.retain(|_, entry| {
486490
match entry {
487491
LSPS2Request::GetInfo(_) => false,
488492
LSPS2Request::Buy(request) => {
489493
// Prune any expired buy requests.
490-
!is_expired_opening_fee_params(&request.opening_fee_params)
494+
!is_expired_opening_fee_params(
495+
&request.opening_fee_params,
496+
time_provider.clone(),
497+
)
491498
},
492499
}
493500
});
@@ -566,16 +573,32 @@ where
566573
peer_by_channel_id: RwLock<HashMap<ChannelId, PublicKey>>,
567574
total_pending_requests: AtomicUsize,
568575
config: LSPS2ServiceConfig,
576+
time_provider: Arc<dyn TimeProvider>,
569577
}
570578

571579
impl<CM: Deref> LSPS2ServiceHandler<CM>
572580
where
573581
CM::Target: AChannelManager,
574582
{
583+
#[cfg(feature = "time")]
575584
/// Constructs a `LSPS2ServiceHandler`.
576585
pub(crate) fn new(
577586
pending_messages: Arc<MessageQueue>, pending_events: Arc<EventQueue>, channel_manager: CM,
578587
config: LSPS2ServiceConfig,
588+
) -> Self {
589+
let time_provider = Arc::new(DefaultTimeProvider);
590+
Self::new_with_custom_time_provider(
591+
pending_messages,
592+
pending_events,
593+
channel_manager,
594+
config,
595+
time_provider,
596+
)
597+
}
598+
599+
pub(crate) fn new_with_custom_time_provider(
600+
pending_messages: Arc<MessageQueue>, pending_events: Arc<EventQueue>, channel_manager: CM,
601+
config: LSPS2ServiceConfig, time_provider: Arc<dyn TimeProvider>,
579602
) -> Self {
580603
Self {
581604
pending_messages,
@@ -586,6 +609,7 @@ where
586609
total_pending_requests: AtomicUsize::new(0),
587610
channel_manager,
588611
config,
612+
time_provider,
589613
}
590614
}
591615

@@ -737,6 +761,7 @@ where
737761
buy_request.payment_size_msat,
738762
buy_request.opening_fee_params,
739763
user_channel_id,
764+
self.time_provider.clone(),
740765
);
741766

742767
peer_state_lock
@@ -1192,7 +1217,11 @@ where
11921217
}
11931218

11941219
// 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) {
1220+
if !is_valid_opening_fee_params(
1221+
&params.opening_fee_params,
1222+
&self.config.promise_secret,
1223+
self.time_provider.clone(),
1224+
) {
11961225
let response = LSPS2Response::BuyError(LSPSResponseError {
11971226
code: LSPS2_BUY_REQUEST_INVALID_OPENING_FEE_PARAMS_ERROR_CODE,
11981227
message: "valid_until is already past OR the promise did not match the provided parameters".to_string(),
@@ -1334,7 +1363,7 @@ where
13341363
let is_prunable =
13351364
if let Some(inner_state_lock) = outer_state_lock.get(&counterparty_node_id) {
13361365
let mut peer_state_lock = inner_state_lock.lock().unwrap();
1337-
peer_state_lock.prune_expired_request_state();
1366+
peer_state_lock.prune_expired_request_state(self.time_provider.clone());
13381367
peer_state_lock.is_prunable()
13391368
} else {
13401369
return;
@@ -1349,7 +1378,7 @@ where
13491378
let mut outer_state_lock = self.per_peer_state.write().unwrap();
13501379
outer_state_lock.retain(|_, inner_state_lock| {
13511380
let mut peer_state_lock = inner_state_lock.lock().unwrap();
1352-
peer_state_lock.prune_expired_request_state();
1381+
peer_state_lock.prune_expired_request_state(self.time_provider.clone());
13531382
peer_state_lock.is_prunable() == false
13541383
});
13551384
}

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)