Skip to content

Commit 38c8436

Browse files
fixups addressing comments. list of changes:
- get rid of dos_protection_enforcer. logic will go inside lsps5/service - improve naming conventions so it's clearer - do not log on level info for possible spam - make the dos protections enabled by default
1 parent 6cebc12 commit 38c8436

File tree

6 files changed

+295
-122
lines changed

6 files changed

+295
-122
lines changed

lightning-liquidity/src/dos_protection_enforcer.rs

Lines changed: 0 additions & 62 deletions
This file was deleted.

lightning-liquidity/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ mod prelude {
5858
pub(crate) use lightning::util::hash_tables::*;
5959
}
6060

61-
pub mod dos_protection_enforcer;
6261
pub mod events;
6362
pub mod lsps0;
6463
pub mod lsps1;

lightning-liquidity/src/lsps2/service.rs

Lines changed: 85 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,8 @@ struct ForwardPaymentAction(ChannelId, FeePayment);
108108
struct ForwardHTLCsAction(ChannelId, Vec<InterceptedHTLC>);
109109

110110
/// The different states a requested JIT channel can be in.
111-
#[derive(Debug)]
112-
enum OutboundJITChannelState {
111+
#[derive(Clone, Debug, PartialEq, Eq)]
112+
pub(crate) enum OutboundJITChannelState {
113113
/// The JIT channel SCID was created after a buy request, and we are awaiting an initial payment
114114
/// of sufficient size to open the channel.
115115
PendingInitialPayment { payment_queue: PaymentQueue },
@@ -134,6 +134,30 @@ enum OutboundJITChannelState {
134134
PaymentForwarded { channel_id: ChannelId },
135135
}
136136

137+
impl OutboundJITChannelState {
138+
fn ord_index(&self) -> u8 {
139+
match self {
140+
OutboundJITChannelState::PendingInitialPayment { .. } => 0,
141+
OutboundJITChannelState::PendingChannelOpen { .. } => 1,
142+
OutboundJITChannelState::PendingPaymentForward { .. } => 2,
143+
OutboundJITChannelState::PendingPayment { .. } => 3,
144+
OutboundJITChannelState::PaymentForwarded { .. } => 4,
145+
}
146+
}
147+
}
148+
149+
impl PartialOrd for OutboundJITChannelState {
150+
fn partial_cmp(&self, other: &Self) -> Option<CmpOrdering> {
151+
Some(self.cmp(other))
152+
}
153+
}
154+
155+
impl Ord for OutboundJITChannelState {
156+
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
157+
self.ord_index().cmp(&other.ord_index())
158+
}
159+
}
160+
137161
impl OutboundJITChannelState {
138162
fn new() -> Self {
139163
OutboundJITChannelState::PendingInitialPayment { payment_queue: PaymentQueue::new() }
@@ -427,10 +451,6 @@ impl OutboundJITChannel {
427451
matches!(self.state, OutboundJITChannelState::PendingInitialPayment { .. })
428452
}
429453

430-
fn is_pending_channel_open(&self) -> bool {
431-
matches!(self.state, OutboundJITChannelState::PendingChannelOpen { .. })
432-
}
433-
434454
fn is_prunable(&self) -> bool {
435455
// We deem an OutboundJITChannel prunable if our offer expired and we haven't intercepted
436456
// any HTLCs initiating the flow yet.
@@ -576,19 +596,15 @@ where
576596
&self.config
577597
}
578598

579-
pub(crate) fn has_pending_channel_open_request(
599+
pub(crate) fn highest_state_for_peer(
580600
&self, counterparty_node_id: &PublicKey,
581-
) -> bool {
582-
let outer_state_lock = self.per_peer_state.read().unwrap();
583-
if let Some(inner_state_lock) = outer_state_lock.get(counterparty_node_id) {
584-
let peer_state = inner_state_lock.lock().unwrap();
585-
peer_state
586-
.outbound_channels_by_intercept_scid
587-
.values()
588-
.any(|c| c.is_pending_channel_open())
589-
} else {
590-
false
591-
}
601+
) -> Option<OutboundJITChannelState> {
602+
let outer = self.per_peer_state.read().unwrap();
603+
let Some(inner) = outer.get(counterparty_node_id) else {
604+
return None;
605+
};
606+
let peer_state = inner.lock().unwrap();
607+
peer_state.outbound_channels_by_intercept_scid.values().map(|c| c.state.clone()).max()
592608
}
593609

594610
/// Used by LSP to inform a client requesting a JIT Channel the token they used is invalid.
@@ -1924,4 +1940,55 @@ mod tests {
19241940
);
19251941
}
19261942
}
1943+
1944+
#[test]
1945+
fn highest_state_for_peer_orders() {
1946+
let opening_fee_params = LSPS2OpeningFeeParams {
1947+
min_fee_msat: 0,
1948+
proportional: 0,
1949+
valid_until: LSPSDateTime::from_str("1970-01-01T00:00:00Z").unwrap(),
1950+
min_lifetime: 0,
1951+
max_client_to_self_delay: 0,
1952+
min_payment_size_msat: 0,
1953+
max_payment_size_msat: 0,
1954+
promise: String::new(),
1955+
};
1956+
1957+
let mut map = new_hash_map();
1958+
map.insert(
1959+
0,
1960+
OutboundJITChannel {
1961+
state: OutboundJITChannelState::PendingInitialPayment {
1962+
payment_queue: PaymentQueue::new(),
1963+
},
1964+
user_channel_id: 0,
1965+
opening_fee_params: opening_fee_params.clone(),
1966+
payment_size_msat: None,
1967+
},
1968+
);
1969+
map.insert(
1970+
1,
1971+
OutboundJITChannel {
1972+
state: OutboundJITChannelState::PendingChannelOpen {
1973+
payment_queue: PaymentQueue::new(),
1974+
opening_fee_msat: 0,
1975+
},
1976+
user_channel_id: 1,
1977+
opening_fee_params: opening_fee_params.clone(),
1978+
payment_size_msat: None,
1979+
},
1980+
);
1981+
map.insert(
1982+
2,
1983+
OutboundJITChannel {
1984+
state: OutboundJITChannelState::PaymentForwarded { channel_id: ChannelId([0; 32]) },
1985+
user_channel_id: 2,
1986+
opening_fee_params,
1987+
payment_size_msat: None,
1988+
},
1989+
);
1990+
1991+
let max_state = map.values().map(|c| c.state.clone()).max().unwrap();
1992+
assert!(matches!(max_state, OutboundJITChannelState::PaymentForwarded { .. }));
1993+
}
19271994
}

lightning-liquidity/src/lsps5/service.rs

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use crate::prelude::*;
2222
use crate::sync::{Arc, Mutex};
2323
use crate::utils::time::TimeProvider;
2424

25+
use crate::lsps2::service::OutboundJITChannelState;
2526
use bitcoin::secp256k1::PublicKey;
2627

2728
use lightning::ln::channelmanager::AChannelManager;
@@ -64,8 +65,6 @@ struct StoredWebhook {
6465
pub struct LSPS5ServiceConfig {
6566
/// Maximum number of webhooks allowed per client.
6667
pub max_webhooks_per_client: u32,
67-
/// Require an existing channel or active LSPS1/LSPS2 flow before accepting requests.
68-
pub enforce_dos_protections: bool,
6968
}
7069

7170
/// Default maximum number of webhooks allowed per client.
@@ -76,10 +75,7 @@ pub const DEFAULT_NOTIFICATION_COOLDOWN_HOURS: Duration = Duration::from_secs(60
7675
// Default configuration for LSPS5 service.
7776
impl Default for LSPS5ServiceConfig {
7877
fn default() -> Self {
79-
Self {
80-
max_webhooks_per_client: DEFAULT_MAX_WEBHOOKS_PER_CLIENT,
81-
enforce_dos_protections: true,
82-
}
78+
Self { max_webhooks_per_client: DEFAULT_MAX_WEBHOOKS_PER_CLIENT }
8379
}
8480
}
8581

@@ -160,6 +156,28 @@ where
160156
&self.config
161157
}
162158

159+
/// Returns whether a request from the given client should be accepted.
160+
///
161+
/// Prior activity includes an existing open channel, an active LSPS1 flow,
162+
/// or an LSPS2 flow that has progressed to at least
163+
/// [`OutboundJITChannelState::PendingChannelOpen`].
164+
pub(crate) fn can_accept_request(
165+
&self, client_id: &PublicKey, lsps2_max_state: Option<OutboundJITChannelState>,
166+
lsps1_has_activity: bool,
167+
) -> bool {
168+
self.client_has_open_channel(client_id)
169+
|| lsps1_has_activity
170+
|| lsps2_max_state.map_or(false, |s| {
171+
matches!(
172+
s,
173+
OutboundJITChannelState::PendingChannelOpen { .. }
174+
| OutboundJITChannelState::PendingPaymentForward { .. }
175+
| OutboundJITChannelState::PendingPayment { .. }
176+
| OutboundJITChannelState::PaymentForwarded { .. }
177+
)
178+
})
179+
}
180+
163181
fn check_prune_stale_webhooks(&self) {
164182
let now =
165183
LSPSDateTime::new_from_duration_since_epoch(self.time_provider.duration_since_epoch());

lightning-liquidity/src/manager.rs

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use alloc::string::ToString;
22
use alloc::vec::Vec;
33

4-
use crate::dos_protection_enforcer::DosProtectionEnforcer;
54
use crate::events::{EventQueue, LiquidityEvent};
65
use crate::lsps0::client::LSPS0ClientHandler;
76
use crate::lsps0::msgs::LSPS0Message;
@@ -484,18 +483,6 @@ where
484483
self.pending_events.get_and_clear_pending_events()
485484
}
486485

487-
fn peer_is_engaged(&self, peer: &PublicKey) -> bool {
488-
let lsps5_engaged =
489-
self.lsps5_service_handler.as_ref().map_or(false, |h| h.is_engaged(peer));
490-
let lsps2_engaged =
491-
self.lsps2_service_handler.as_ref().map_or(false, |h| h.is_engaged(peer));
492-
#[cfg(lsps1_service)]
493-
let lsps1_engaged = self.lsps1_service_handler.as_ref().map_or(false, |h| h.is_engaged(peer));
494-
#[cfg(not(lsps1_service))]
495-
let lsps1_engaged = false;
496-
lsps5_engaged || lsps2_engaged || lsps1_engaged
497-
}
498-
499486
fn handle_lsps_message(
500487
&self, msg: LSPSMessage, sender_node_id: &PublicKey,
501488
) -> Result<(), lightning::ln::msgs::LightningError> {
@@ -572,17 +559,32 @@ where
572559
LSPSMessage::LSPS5(msg @ LSPS5Message::Request(..)) => {
573560
match &self.lsps5_service_handler {
574561
Some(lsps5_service_handler) => {
575-
if lsps5_service_handler.config().enforce_dos_protections {
576-
if !self.peer_is_engaged(sender_node_id) {
577-
return Err(LightningError {
562+
let lsps2_max_state = self
563+
.lsps2_service_handler
564+
.as_ref()
565+
.and_then(|h| h.highest_state_for_peer(sender_node_id));
566+
#[cfg(lsps1_service)]
567+
let lsps1_has_active_requests = self
568+
.lsps1_service_handler
569+
.as_ref()
570+
.map_or(false, |h| h.has_active_requests(sender_node_id));
571+
#[cfg(not(lsps1_service))]
572+
let lsps1_has_active_requests = false;
573+
574+
if !lsps5_service_handler.can_accept_request(
575+
sender_node_id,
576+
lsps2_max_state,
577+
lsps1_has_active_requests,
578+
) {
579+
return Err(LightningError {
578580
err: format!(
579-
"Rejecting LSPS5 request from {:?} without existing engagement",
581+
"Rejecting LSPS5 request from {:?} without prior activity (requires open channel or active LSPS1 or LSPS2 flow)",
580582
sender_node_id
581583
),
582-
action: ErrorAction::IgnoreAndLog(Level::Info),
584+
action: ErrorAction::IgnoreAndLog(Level::Debug),
583585
});
584-
}
585586
}
587+
586588
lsps5_service_handler.handle_message(msg, sender_node_id)?;
587589
},
588590
None => {

0 commit comments

Comments
 (0)