Skip to content

Commit 99d7d1f

Browse files
WIP: checking CI
1 parent cb7ae43 commit 99d7d1f

File tree

12 files changed

+424
-786
lines changed

12 files changed

+424
-786
lines changed

lightning-background-processor/src/lib.rs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,7 @@ use futures_util::{dummy_waker, OptionalSelector, Selector, SelectorOutput};
647647
/// # use std::sync::atomic::{AtomicBool, Ordering};
648648
/// # use std::time::SystemTime;
649649
/// # use lightning_background_processor::{process_events_async, GossipSync};
650+
/// # use lightning_liquidity::lsps5::service::TimeProvider;
650651
/// # struct Logger {}
651652
/// # impl lightning::util::logger::Logger for Logger {
652653
/// # fn log(&self, _record: lightning::util::logger::Record) {}
@@ -658,6 +659,16 @@ use futures_util::{dummy_waker, OptionalSelector, Selector, SelectorOutput};
658659
/// # fn remove(&self, primary_namespace: &str, secondary_namespace: &str, key: &str, lazy: bool) -> io::Result<()> { Ok(()) }
659660
/// # fn list(&self, primary_namespace: &str, secondary_namespace: &str) -> io::Result<Vec<String>> { Ok(Vec::new()) }
660661
/// # }
662+
///
663+
/// # use core::time::Duration;
664+
/// # struct DefaultTimeProvider;
665+
///
666+
/// # impl TimeProvider for DefaultTimeProvider {
667+
/// # fn duration_since_epoch(&self) -> Duration {
668+
/// # use std::time::{SystemTime, UNIX_EPOCH};
669+
/// # SystemTime::now().duration_since(UNIX_EPOCH).expect("system time before Unix epoch")
670+
/// # }
671+
/// # }
661672
/// # struct EventHandler {}
662673
/// # impl EventHandler {
663674
/// # async fn handle_event(&self, _: lightning::events::Event) -> Result<(), ReplayEvent> { Ok(()) }
@@ -673,7 +684,7 @@ use futures_util::{dummy_waker, OptionalSelector, Selector, SelectorOutput};
673684
/// # type P2PGossipSync<UL> = lightning::routing::gossip::P2PGossipSync<Arc<NetworkGraph>, Arc<UL>, Arc<Logger>>;
674685
/// # type ChannelManager<B, F, FE> = lightning::ln::channelmanager::SimpleArcChannelManager<ChainMonitor<B, F, FE>, B, FE, Logger>;
675686
/// # type OnionMessenger<B, F, FE> = lightning::onion_message::messenger::OnionMessenger<Arc<lightning::sign::KeysManager>, Arc<lightning::sign::KeysManager>, Arc<Logger>, Arc<ChannelManager<B, F, FE>>, Arc<lightning::onion_message::messenger::DefaultMessageRouter<Arc<NetworkGraph>, Arc<Logger>, Arc<lightning::sign::KeysManager>>>, Arc<ChannelManager<B, F, FE>>, lightning::ln::peer_handler::IgnoringMessageHandler, lightning::ln::peer_handler::IgnoringMessageHandler, lightning::ln::peer_handler::IgnoringMessageHandler>;
676-
/// # type LiquidityManager<B, F, FE> = lightning_liquidity::LiquidityManager<Arc<lightning::sign::KeysManager>, Arc<ChannelManager<B, F, FE>>, Arc<F>>;
687+
/// # type LiquidityManager<B, F, FE> = lightning_liquidity::LiquidityManager<Arc<lightning::sign::KeysManager>, Arc<ChannelManager<B, F, FE>>, Arc<F>, Arc<DefaultTimeProvider>>;
677688
/// # type Scorer = RwLock<lightning::routing::scoring::ProbabilisticScorer<Arc<NetworkGraph>, Arc<Logger>>>;
678689
/// # type PeerManager<B, F, FE, UL> = lightning::ln::peer_handler::SimpleArcPeerManager<SocketDescriptor, ChainMonitor<B, F, FE>, B, FE, Arc<UL>, Logger>;
679690
/// #
@@ -1285,8 +1296,12 @@ mod tests {
12851296
IgnoringMessageHandler,
12861297
>;
12871298

1288-
type LM =
1289-
LiquidityManager<Arc<KeysManager>, Arc<ChannelManager>, Arc<dyn Filter + Sync + Send>>;
1299+
type LM = LiquidityManager<
1300+
Arc<KeysManager>,
1301+
Arc<ChannelManager>,
1302+
Arc<dyn Filter + Sync + Send>,
1303+
Arc<DefaultTimeProvider>,
1304+
>;
12901305

12911306
struct Node {
12921307
node: Arc<ChannelManager>,

lightning-liquidity/src/lsps0/ser.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ use lightning::util::ser::{LengthLimitedRead, LengthReadable, WithoutLength};
3535
use bitcoin::secp256k1::PublicKey;
3636

3737
use core::time::Duration;
38-
#[cfg(feature = "std")]
38+
#[cfg(feature = "time")]
3939
use std::time::{SystemTime, UNIX_EPOCH};
4040

4141
use serde::de::{self, MapAccess, Visitor};
@@ -229,7 +229,7 @@ impl LSPSDateTime {
229229
}
230230

231231
/// Returns if the given time is in the past.
232-
#[cfg(feature = "std")]
232+
#[cfg(feature = "time")]
233233
pub fn is_past(&self) -> bool {
234234
let now_seconds_since_epoch = SystemTime::now()
235235
.duration_since(UNIX_EPOCH)

lightning-liquidity/src/lsps2/utils.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,13 @@ pub fn is_valid_opening_fee_params(
2828
}
2929

3030
/// Determines if the given parameters are expired, or still valid.
31-
#[cfg_attr(not(feature = "std"), allow(unused_variables))]
31+
#[cfg_attr(not(feature = "time"), allow(unused_variables))]
3232
pub fn is_expired_opening_fee_params(fee_params: &LSPS2OpeningFeeParams) -> bool {
33-
#[cfg(feature = "std")]
33+
#[cfg(feature = "time")]
3434
{
3535
fee_params.valid_until.is_past()
3636
}
37-
#[cfg(not(feature = "std"))]
37+
#[cfg(not(feature = "time"))]
3838
{
3939
// TODO: We need to find a way to check expiry times in no-std builds.
4040
false

lightning-liquidity/src/lsps5/client.rs

Lines changed: 58 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -83,18 +83,24 @@ impl Default for LSPS5ClientConfig {
8383
}
8484
}
8585

86-
struct PeerState {
86+
struct PeerState<TP: Deref>
87+
where
88+
TP::Target: TimeProvider,
89+
{
8790
pending_set_webhook_requests:
8891
HashMap<LSPSRequestId, (LSPS5AppName, LSPS5WebhookUrl, LSPSDateTime)>,
8992
pending_list_webhooks_requests: HashMap<LSPSRequestId, LSPSDateTime>,
9093
pending_remove_webhook_requests: HashMap<LSPSRequestId, (LSPS5AppName, LSPSDateTime)>,
9194
last_cleanup: Option<LSPSDateTime>,
9295
max_age_secs: Duration,
93-
time_provider: Arc<dyn TimeProvider>,
96+
time_provider: TP,
9497
}
9598

96-
impl PeerState {
97-
fn new(max_age_secs: Duration, time_provider: Arc<dyn TimeProvider>) -> Self {
99+
impl<TP: Deref> PeerState<TP>
100+
where
101+
TP::Target: TimeProvider,
102+
{
103+
fn new(max_age_secs: Duration, time_provider: TP) -> Self {
98104
Self {
99105
pending_set_webhook_requests: new_hash_map(),
100106
pending_list_webhooks_requests: new_hash_map(),
@@ -109,27 +115,29 @@ impl PeerState {
109115
let now =
110116
LSPSDateTime::new_from_duration_since_epoch(self.time_provider.duration_since_epoch());
111117
// Only run cleanup once per minute to avoid excessive processing
112-
let minute = 60;
118+
const CLEANUP_INTERVAL: Duration = Duration::from_secs(60);
113119
if let Some(last_cleanup) = &self.last_cleanup {
114-
if now.abs_diff(last_cleanup.clone()) < minute {
120+
let time_since_last_cleanup = Duration::from_secs(now.abs_diff(last_cleanup.clone()));
121+
if time_since_last_cleanup < CLEANUP_INTERVAL {
115122
return;
116123
}
117124
}
118125

119126
self.last_cleanup = Some(now.clone());
120127

121128
self.pending_set_webhook_requests.retain(|_, (_, _, timestamp)| {
122-
timestamp.abs_diff(now.clone()) < self.max_age_secs.as_secs()
129+
Duration::from_secs(timestamp.abs_diff(now.clone())) < self.max_age_secs
130+
});
131+
self.pending_list_webhooks_requests.retain(|_, timestamp| {
132+
Duration::from_secs(timestamp.abs_diff(now.clone())) < self.max_age_secs
123133
});
124-
self.pending_list_webhooks_requests
125-
.retain(|_, timestamp| timestamp.abs_diff(now.clone()) < self.max_age_secs.as_secs());
126134
self.pending_remove_webhook_requests.retain(|_, (_, timestamp)| {
127-
timestamp.abs_diff(now.clone()) < self.max_age_secs.as_secs()
135+
Duration::from_secs(timestamp.abs_diff(now.clone())) < self.max_age_secs
128136
});
129137
}
130138
}
131139

132-
/// Clientside handler for the LSPS5 (bLIP-55) webhook registration protocol.
140+
/// Client-side handler for the LSPS5 (bLIP-55) webhook registration protocol.
133141
///
134142
/// `LSPS5ClientHandler` is the primary interface for LSP clients
135143
/// to register, list, and remove webhook endpoints with an LSP, and to parse
@@ -146,27 +154,29 @@ impl PeerState {
146154
/// [`lsps5.set_webhook`]: super::msgs::LSPS5Request::SetWebhook
147155
/// [`lsps5.list_webhooks`]: super::msgs::LSPS5Request::ListWebhooks
148156
/// [`lsps5.remove_webhook`]: super::msgs::LSPS5Request::RemoveWebhook
149-
pub struct LSPS5ClientHandler<ES: Deref>
157+
pub struct LSPS5ClientHandler<ES: Deref, TP: Deref + Clone>
150158
where
151159
ES::Target: EntropySource,
160+
TP::Target: TimeProvider,
152161
{
153162
pending_messages: Arc<MessageQueue>,
154163
pending_events: Arc<EventQueue>,
155164
entropy_source: ES,
156-
per_peer_state: RwLock<HashMap<PublicKey, Mutex<PeerState>>>,
165+
per_peer_state: RwLock<HashMap<PublicKey, Mutex<PeerState<TP>>>>,
157166
config: LSPS5ClientConfig,
158-
time_provider: Arc<dyn TimeProvider>,
167+
time_provider: TP,
159168
recent_signatures: Mutex<VecDeque<(String, LSPSDateTime)>>,
160169
}
161170

162-
impl<ES: Deref> LSPS5ClientHandler<ES>
171+
impl<ES: Deref, TP: Deref + Clone> LSPS5ClientHandler<ES, TP>
163172
where
164173
ES::Target: EntropySource,
174+
TP::Target: TimeProvider,
165175
{
166176
/// Constructs an `LSPS5ClientHandler`.
167177
pub(crate) fn new(
168178
entropy_source: ES, pending_messages: Arc<MessageQueue>, pending_events: Arc<EventQueue>,
169-
config: LSPS5ClientConfig, time_provider: Arc<dyn TimeProvider>,
179+
config: LSPS5ClientConfig, time_provider: TP,
170180
) -> Self {
171181
let max_signatures = config.signature_config.max_signatures.clone();
172182
Self {
@@ -182,11 +192,11 @@ where
182192

183193
fn with_peer_state<F, R>(&self, counterparty_node_id: PublicKey, f: F) -> R
184194
where
185-
F: FnOnce(&mut PeerState) -> R,
195+
F: FnOnce(&mut PeerState<TP>) -> R,
186196
{
187197
let mut outer_state_lock = self.per_peer_state.write().unwrap();
188198
let inner_state_lock = outer_state_lock.entry(counterparty_node_id).or_insert(Mutex::new(
189-
PeerState::new(self.config.response_max_age_secs, Arc::clone(&self.time_provider)),
199+
PeerState::new(self.config.response_max_age_secs, self.time_provider.clone()),
190200
));
191201
let mut peer_state_lock = inner_state_lock.lock().unwrap();
192202

@@ -347,7 +357,7 @@ where
347357
action: ErrorAction::IgnoreAndLog(Level::Error),
348358
});
349359
let event_queue_notifier = self.pending_events.notifier();
350-
let handle_response = |peer_state: &mut PeerState| {
360+
let handle_response = |peer_state: &mut PeerState<TP>| {
351361
if let Some((app_name, webhook_url, _)) =
352362
peer_state.pending_set_webhook_requests.remove(&request_id)
353363
{
@@ -449,13 +459,13 @@ where
449459
fn verify_notification_signature(
450460
&self, counterparty_node_id: PublicKey, signature_timestamp: &LSPSDateTime,
451461
signature: &str, notification: &WebhookNotification,
452-
) -> Result<bool, LSPS5ClientError> {
462+
) -> Result<(), LSPS5ClientError> {
453463
let now =
454464
LSPSDateTime::new_from_duration_since_epoch(self.time_provider.duration_since_epoch());
455465
let diff = signature_timestamp.abs_diff(now);
456-
let ten_minutes = 600;
457-
if diff > ten_minutes {
458-
return Err(LSPS5ClientError::InvalidTimestamp(signature_timestamp.to_rfc3339()));
466+
const MAX_TIMESTAMP_DRIFT_SECS: u64 = 600;
467+
if diff > MAX_TIMESTAMP_DRIFT_SECS {
468+
return Err(LSPS5ClientError::InvalidTimestamp);
459469
}
460470

461471
let message = format!(
@@ -465,7 +475,7 @@ where
465475
);
466476

467477
if message_signing::verify(message.as_bytes(), signature, &counterparty_node_id) {
468-
Ok(true)
478+
Ok(())
469479
} else {
470480
Err(LSPS5ClientError::InvalidSignature)
471481
}
@@ -490,17 +500,10 @@ where
490500

491501
recent_signatures.push_back((signature, now.clone()));
492502

493-
let retention_duration = self.config.signature_config.retention_minutes * 60;
494-
while let Some((_, time)) = recent_signatures.front() {
495-
if now.abs_diff(time.clone()) > retention_duration.as_secs() {
496-
recent_signatures.pop_front();
497-
} else {
498-
break;
499-
}
500-
}
501-
502-
while recent_signatures.len() > self.config.signature_config.max_signatures {
503-
recent_signatures.pop_front();
503+
let retention_secs = self.config.signature_config.retention_minutes.as_secs();
504+
recent_signatures.retain(|(_, ts)| now.abs_diff(ts.clone()) <= retention_secs);
505+
if recent_signatures.len() > self.config.signature_config.max_signatures {
506+
recent_signatures.truncate(self.config.signature_config.max_signatures);
504507
}
505508
}
506509

@@ -513,15 +516,15 @@ where
513516
/// configured retention window.
514517
/// 4. Reconstructs the exact string
515518
/// `"LSPS5: DO NOT SIGN THIS MESSAGE MANUALLY: LSP: At {timestamp} I notify {body}"`
516-
/// and verifies the zbase32 LN-style signature against the LSPs node ID.
519+
/// and verifies the zbase32 LN-style signature against the LSP's node ID.
517520
///
518521
/// # Parameters
519-
/// - `counterparty_node_id`: the LSPs public key, used to verify the signature.
522+
/// - `counterparty_node_id`: the LSP's public key, used to verify the signature.
520523
/// - `timestamp`: ISO8601 time when the LSP created the notification.
521524
/// - `signature`: the zbase32-encoded LN signature over timestamp+body.
522525
/// - `notification`: the [`WebhookNotification`] received from the LSP.
523526
///
524-
/// On success, emits [`LSPS5ClientEvent::WebhookNotificationReceived`].
527+
/// On success, returns the received [`WebhookNotification`].
525528
///
526529
/// Failure reasons include:
527530
/// - Timestamp too old (drift > 10 minutes)
@@ -532,42 +535,31 @@ where
532535
/// event, before taking action on the notification. This guarantees that only authentic,
533536
/// non-replayed notifications reach your application.
534537
///
535-
/// [`LSPS5ClientEvent::WebhookNotificationReceived`]: super::event::LSPS5ClientEvent::WebhookNotificationReceived
536538
/// [`LSPS5ServiceEvent::SendWebhookNotification`]: super::event::LSPS5ServiceEvent::SendWebhookNotification
537539
/// [`WebhookNotification`]: super::msgs::WebhookNotification
538540
pub fn parse_webhook_notification(
539541
&self, counterparty_node_id: PublicKey, timestamp: &LSPSDateTime, signature: &str,
540542
notification: &WebhookNotification,
541-
) -> Result<(), LSPS5ClientError> {
542-
match self.verify_notification_signature(
543+
) -> Result<WebhookNotification, LSPS5ClientError> {
544+
self.verify_notification_signature(
543545
counterparty_node_id,
544546
timestamp,
545547
signature,
546548
&notification,
547-
) {
548-
Ok(signature_valid) => {
549-
let event_queue_notifier = self.pending_events.notifier();
549+
)?;
550550

551-
self.check_signature_exists(signature)?;
551+
self.check_signature_exists(signature)?;
552552

553-
self.store_signature(signature.to_string());
553+
self.store_signature(signature.to_string());
554554

555-
event_queue_notifier.enqueue(LSPS5ClientEvent::WebhookNotificationReceived {
556-
counterparty_node_id,
557-
notification: notification.clone(),
558-
timestamp: timestamp.clone(),
559-
signature_valid,
560-
});
561-
Ok(())
562-
},
563-
Err(e) => Err(e),
564-
}
555+
Ok(notification.clone())
565556
}
566557
}
567558

568-
impl<ES: Deref> LSPSProtocolMessageHandler for LSPS5ClientHandler<ES>
559+
impl<ES: Deref, TP: Deref + Clone> LSPSProtocolMessageHandler for LSPS5ClientHandler<ES, TP>
569560
where
570561
ES::Target: EntropySource,
562+
TP::Target: TimeProvider,
571563
{
572564
type ProtocolMessage = LSPS5Message;
573565
const PROTOCOL_NUMBER: Option<u16> = Some(5);
@@ -592,8 +584,10 @@ mod tests {
592584
};
593585
use bitcoin::{key::Secp256k1, secp256k1::SecretKey};
594586

595-
fn setup_test_client() -> (
596-
LSPS5ClientHandler<Arc<TestEntropy>>,
587+
fn setup_test_client(
588+
time_provider: Arc<dyn TimeProvider>,
589+
) -> (
590+
LSPS5ClientHandler<Arc<TestEntropy>, Arc<dyn TimeProvider>>,
597591
Arc<MessageQueue>,
598592
Arc<EventQueue>,
599593
PublicKey,
@@ -602,7 +596,6 @@ mod tests {
602596
let test_entropy_source = Arc::new(TestEntropy {});
603597
let message_queue = Arc::new(MessageQueue::new());
604598
let event_queue = Arc::new(EventQueue::new());
605-
let time_provider = Arc::new(DefaultTimeProvider);
606599
let client = LSPS5ClientHandler::new(
607600
test_entropy_source,
608601
message_queue.clone(),
@@ -622,7 +615,7 @@ mod tests {
622615

623616
#[test]
624617
fn test_per_peer_state_isolation() {
625-
let (client, _, _, peer_1, peer_2) = setup_test_client();
618+
let (client, _, _, peer_1, peer_2) = setup_test_client(Arc::new(DefaultTimeProvider));
626619

627620
let req_id_1 = client
628621
.set_webhook(peer_1, "test-app-1".to_string(), "https://example.com/hook1".to_string())
@@ -644,7 +637,7 @@ mod tests {
644637

645638
#[test]
646639
fn test_pending_request_tracking() {
647-
let (client, _, _, peer, _) = setup_test_client();
640+
let (client, _, _, peer, _) = setup_test_client(Arc::new(DefaultTimeProvider));
648641
const APP_NAME: &str = "test-app";
649642
const WEBHOOK_URL: &str = "https://example.com/hook";
650643
let lsps5_app_name = LSPS5AppName::from_string(APP_NAME.to_string()).unwrap();
@@ -677,7 +670,7 @@ mod tests {
677670

678671
#[test]
679672
fn test_handle_response_clears_pending_state() {
680-
let (client, _, _, peer, _) = setup_test_client();
673+
let (client, _, _, peer, _) = setup_test_client(Arc::new(DefaultTimeProvider));
681674

682675
let req_id = client
683676
.set_webhook(peer, "test-app".to_string(), "https://example.com/hook".to_string())
@@ -707,7 +700,7 @@ mod tests {
707700

708701
#[test]
709702
fn test_cleanup_expired_responses() {
710-
let (client, _, _, _, _) = setup_test_client();
703+
let (client, _, _, _, _) = setup_test_client(Arc::new(DefaultTimeProvider));
711704
let time_provider = &client.time_provider;
712705
const OLD_APP_NAME: &str = "test-app-old";
713706
const NEW_APP_NAME: &str = "test-app-new";
@@ -764,7 +757,7 @@ mod tests {
764757

765758
#[test]
766759
fn test_unknown_request_id_handling() {
767-
let (client, _message_queue, _, peer, _) = setup_test_client();
760+
let (client, _message_queue, _, peer, _) = setup_test_client(Arc::new(DefaultTimeProvider));
768761

769762
let _valid_req = client
770763
.set_webhook(peer, "test-app".to_string(), "https://example.com/hook".to_string())

0 commit comments

Comments
 (0)