`LSPS1ServiceHandler`: Use `TimeProvider` when creating new orders
What changed, and why it matters
This commit is a routine internal refactoring of the Lightning Dev Kit's LSPS1 service handler. It replaces a manual user-provided timestamp parameter with an internal TimeProvider trait, so the code automatically fills in the creation time when a new order is created. There is no security bug being fixed here; it is purely a cleaner API design change.
No security action required. Treat as a normal API refactor during dependency update review.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch changes LSPS1ServiceHandler to take a generic TimeProvider (TP) and use time_provider.duration_since_epoch() to generate the created_at LSPSDateTime inside send_payment_details, removing the created_at argument from that method’s public API. The LiquidityManager propagates its existing TimeProvider into the handler, and tests are updated to stop passing a hard-coded timestamp. No cryptographic, memory-safety, or protocol correctness changes are present.
Changed components
lightning-liquidity/src/lsps1/service.rslightning-liquidity/src/manager.rslightning-liquidity/tests/lsps1_integration_tests.rsInspect captured patch +30 / −18
diff --git a/lightning-liquidity/src/lsps1/service.rs b/lightning-liquidity/src/lsps1/service.rs
index 52d9715..0b0adc0 100644
--- a/lightning-liquidity/src/lsps1/service.rs
+++ b/lightning-liquidity/src/lsps1/service.rs
@@ -30,6 +30,7 @@ use crate::lsps0::ser::{
use crate::prelude::{new_hash_map, HashMap};
use crate::sync::{Arc, Mutex, RwLock};
use crate::utils;
+use crate::utils::time::TimeProvider;
use lightning::ln::channelmanager::AChannelManager;
use lightning::ln::msgs::{ErrorAction, LightningError};
@@ -50,26 +51,35 @@ pub struct LSPS1ServiceConfig {
}
/// The main object allowing to send and receive bLIP-51 / LSPS1 messages.
-pub struct LSPS1ServiceHandler<ES: EntropySource, CM: Deref + Clone, K: KVStore + Clone>
-where
+pub struct LSPS1ServiceHandler<
+ ES: EntropySource,
+ CM: Deref + Clone,
+ K: KVStore + Clone,
+ TP: Deref + Clone,
+> where
CM::Target: AChannelManager,
+ TP::Target: TimeProvider,
{
entropy_source: ES,
_channel_manager: CM,
pending_messages: Arc<MessageQueue>,
pending_events: Arc<EventQueue<K>>,
per_peer_state: RwLock<HashMap<PublicKey, Mutex<PeerState>>>,
+ time_provider: TP,
config: LSPS1ServiceConfig,
}
-impl<ES: EntropySource, CM: Deref + Clone, K: KVStore + Clone> LSPS1ServiceHandler<ES, CM, K>
+impl<ES: EntropySource, CM: Deref + Clone, K: KVStore + Clone, TP: Deref + Clone>
+ LSPS1ServiceHandler<ES, CM, K, TP>
where
CM::Target: AChannelManager,
+ TP::Target: TimeProvider,
{
/// Constructs a `LSPS1ServiceHandler`.
pub(crate) fn new(
entropy_source: ES, pending_messages: Arc<MessageQueue>,
- pending_events: Arc<EventQueue<K>>, channel_manager: CM, config: LSPS1ServiceConfig,
+ pending_events: Arc<EventQueue<K>>, channel_manager: CM, time_provider: TP,
+ config: LSPS1ServiceConfig,
) -> Self {
Self {
entropy_source,
@@ -77,6 +87,7 @@ where
pending_messages,
pending_events,
per_peer_state: RwLock::new(new_hash_map()),
+ time_provider,
config,
}
}
@@ -181,7 +192,7 @@ where
/// [`LSPS1ServiceEvent::RequestForPaymentDetails`]: crate::lsps1::event::LSPS1ServiceEvent::RequestForPaymentDetails
pub fn send_payment_details(
&self, request_id: LSPSRequestId, counterparty_node_id: &PublicKey,
- payment_details: LSPS1PaymentInfo, created_at: LSPSDateTime,
+ payment_details: LSPS1PaymentInfo,
) -> Result<(), APIError> {
let mut message_queue_notifier = self.pending_messages.notifier();
@@ -198,6 +209,9 @@ where
match request {
LSPS1Request::CreateOrder(params) => {
let order_id = self.generate_order_id();
+ let created_at = LSPSDateTime::new_from_duration_since_epoch(
+ self.time_provider.duration_since_epoch(),
+ );
let order = peer_state_lock.new_order(
order_id.clone(),
params.order,
@@ -321,10 +335,11 @@ where
}
}
-impl<ES: EntropySource, CM: Deref + Clone, K: KVStore + Clone> LSPSProtocolMessageHandler
- for LSPS1ServiceHandler<ES, CM, K>
+impl<ES: EntropySource, CM: Deref + Clone, K: KVStore + Clone, TP: Deref + Clone>
+ LSPSProtocolMessageHandler for LSPS1ServiceHandler<ES, CM, K, TP>
where
CM::Target: AChannelManager,
+ TP::Target: TimeProvider,
{
type ProtocolMessage = LSPS1Message;
const PROTOCOL_NUMBER: Option<u16> = Some(1);
diff --git a/lightning-liquidity/src/manager.rs b/lightning-liquidity/src/manager.rs
index db05d71..85c8ba3 100644
--- a/lightning-liquidity/src/manager.rs
+++ b/lightning-liquidity/src/manager.rs
@@ -283,7 +283,7 @@ pub struct LiquidityManager<
lsps0_client_handler: LSPS0ClientHandler<ES, K>,
lsps0_service_handler: Option<LSPS0ServiceHandler>,
#[cfg(lsps1_service)]
- lsps1_service_handler: Option<LSPS1ServiceHandler<ES, CM, K>>,
+ lsps1_service_handler: Option<LSPS1ServiceHandler<ES, CM, K, TP>>,
lsps1_client_handler: Option<LSPS1ClientHandler<ES, K>>,
lsps2_service_handler: Option<LSPS2ServiceHandler<CM, K, T>>,
lsps2_client_handler: Option<LSPS2ClientHandler<ES, K>>,
@@ -429,7 +429,7 @@ where
kv_store.clone(),
node_signer,
lsps5_service_config.clone(),
- time_provider,
+ time_provider.clone(),
))
} else {
None
@@ -452,7 +452,7 @@ where
#[cfg(lsps1_service)]
let lsps1_service_handler = service_config.as_ref().and_then(|config| {
if let Some(number) =
- <LSPS1ServiceHandler<ES, CM, K> as LSPSProtocolMessageHandler>::PROTOCOL_NUMBER
+ <LSPS1ServiceHandler<ES, CM, K, TP> as LSPSProtocolMessageHandler>::PROTOCOL_NUMBER
{
supported_protocols.push(number);
}
@@ -462,6 +462,7 @@ where
Arc::clone(&pending_messages),
Arc::clone(&pending_events),
channel_manager.clone(),
+ time_provider,
config.clone(),
)
})
@@ -519,7 +520,7 @@ where
/// Returns a reference to the LSPS1 server-side handler.
#[cfg(lsps1_service)]
- pub fn lsps1_service_handler(&self) -> Option<&LSPS1ServiceHandler<ES, CM, K>> {
+ pub fn lsps1_service_handler(&self) -> Option<&LSPS1ServiceHandler<ES, CM, K, TP>> {
self.lsps1_service_handler.as_ref()
}
@@ -1032,7 +1033,7 @@ where
#[cfg(lsps1_service)]
pub fn lsps1_service_handler(
&self,
- ) -> Option<&LSPS1ServiceHandler<ES, CM, KVStoreSyncWrapper<KS>>> {
+ ) -> Option<&LSPS1ServiceHandler<ES, CM, KVStoreSyncWrapper<KS>, TP>> {
self.inner.lsps1_service_handler()
}
diff --git a/lightning-liquidity/tests/lsps1_integration_tests.rs b/lightning-liquidity/tests/lsps1_integration_tests.rs
index 5e842c6..0db96f5 100644
--- a/lightning-liquidity/tests/lsps1_integration_tests.rs
+++ b/lightning-liquidity/tests/lsps1_integration_tests.rs
@@ -7,7 +7,6 @@ use common::{get_lsps_message, LSPSNodes};
use lightning::ln::peer_handler::CustomMessageHandler;
use lightning_liquidity::events::LiquidityEvent;
-use lightning_liquidity::lsps0::ser::LSPSDateTime;
use lightning_liquidity::lsps1::client::LSPS1ClientConfig;
use lightning_liquidity::lsps1::event::LSPS1ClientEvent;
use lightning_liquidity::lsps1::event::LSPS1ServiceEvent;
@@ -24,7 +23,6 @@ use lightning::ln::functional_test_utils::{
};
use lightning::util::test_utils::TestStore;
-use std::str::FromStr;
use std::sync::Arc;
use lightning::ln::functional_test_utils::{create_network, Node};
@@ -177,10 +175,8 @@ fn lsps1_happy_path() {
let onchain: LSPS1OnchainPaymentInfo =
serde_json::from_str(json_str).expect("Failed to parse JSON");
let payment_info = LSPS1PaymentInfo { bolt11: None, bolt12: None, onchain: Some(onchain) };
- let _now = LSPSDateTime::from_str("2024-01-01T00:00:00Z").expect("Failed to parse date");
-
- let _ = service_handler
- .send_payment_details(_create_order_id.clone(), &client_node_id, payment_info.clone(), _now)
+ service_handler
+ .send_payment_details(_create_order_id.clone(), &client_node_id, payment_info.clone())
.unwrap();
let create_order_response = get_lsps_message!(service_node, client_node_id);
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.