Skip to content

Commit 142842b

Browse files
fixup: make OutboundJITChannelState private, and do bool helper for checking if lsps2 has any pendingChannelOpen or above
1 parent 9a59755 commit 142842b

File tree

4 files changed

+97
-149
lines changed

4 files changed

+97
-149
lines changed

lightning-liquidity/src/lsps2/service.rs

Lines changed: 14 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -107,18 +107,9 @@ struct ForwardPaymentAction(ChannelId, FeePayment);
107107
#[derive(Debug, PartialEq)]
108108
struct ForwardHTLCsAction(ChannelId, Vec<InterceptedHTLC>);
109109

110-
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
111-
pub(crate) enum OutboundJITStage {
112-
PendingInitialPayment,
113-
PendingChannelOpen,
114-
PendingPaymentForward,
115-
PendingPayment,
116-
PaymentForwarded,
117-
}
118-
119110
/// The different states a requested JIT channel can be in.
120-
#[derive(Debug, PartialEq, Eq, Clone)]
121-
pub(crate) enum OutboundJITChannelState {
111+
#[derive(Debug)]
112+
enum OutboundJITChannelState {
122113
/// The JIT channel SCID was created after a buy request, and we are awaiting an initial payment
123114
/// of sufficient size to open the channel.
124115
PendingInitialPayment { payment_queue: PaymentQueue },
@@ -143,36 +134,6 @@ pub(crate) enum OutboundJITChannelState {
143134
PaymentForwarded { channel_id: ChannelId },
144135
}
145136

146-
impl OutboundJITChannelState {
147-
pub(crate) fn stage(&self) -> OutboundJITStage {
148-
match self {
149-
OutboundJITChannelState::PendingInitialPayment { .. } => {
150-
OutboundJITStage::PendingInitialPayment
151-
},
152-
OutboundJITChannelState::PendingChannelOpen { .. } => {
153-
OutboundJITStage::PendingChannelOpen
154-
},
155-
OutboundJITChannelState::PendingPaymentForward { .. } => {
156-
OutboundJITStage::PendingPaymentForward
157-
},
158-
OutboundJITChannelState::PendingPayment { .. } => OutboundJITStage::PendingPayment,
159-
OutboundJITChannelState::PaymentForwarded { .. } => OutboundJITStage::PaymentForwarded,
160-
}
161-
}
162-
}
163-
164-
impl PartialOrd for OutboundJITChannelState {
165-
fn partial_cmp(&self, other: &Self) -> Option<CmpOrdering> {
166-
Some(self.cmp(other))
167-
}
168-
}
169-
170-
impl Ord for OutboundJITChannelState {
171-
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
172-
self.stage().cmp(&other.stage())
173-
}
174-
}
175-
176137
impl OutboundJITChannelState {
177138
fn new() -> Self {
178139
OutboundJITChannelState::PendingInitialPayment { payment_queue: PaymentQueue::new() }
@@ -611,13 +572,20 @@ where
611572
&self.config
612573
}
613574

614-
pub(crate) fn highest_state_for_peer(
615-
&self, counterparty_node_id: &PublicKey,
616-
) -> Option<OutboundJITChannelState> {
575+
/// Returns whether the peer has any opening or open JIT channels.
576+
pub(crate) fn has_opening_or_open_jit_channel(&self, counterparty_node_id: &PublicKey) -> bool {
617577
let outer_state_lock = self.per_peer_state.read().unwrap();
618-
outer_state_lock.get(counterparty_node_id).and_then(|inner| {
578+
outer_state_lock.get(counterparty_node_id).map_or(false, |inner| {
619579
let peer_state = inner.lock().unwrap();
620-
peer_state.outbound_channels_by_intercept_scid.values().map(|c| c.state.clone()).max()
580+
peer_state.outbound_channels_by_intercept_scid.values().any(|chan| {
581+
matches!(
582+
chan.state,
583+
OutboundJITChannelState::PendingChannelOpen { .. }
584+
| OutboundJITChannelState::PendingPaymentForward { .. }
585+
| OutboundJITChannelState::PendingPayment { .. }
586+
| OutboundJITChannelState::PaymentForwarded { .. }
587+
)
588+
})
621589
})
622590
}
623591

@@ -1954,55 +1922,4 @@ mod tests {
19541922
);
19551923
}
19561924
}
1957-
1958-
#[test]
1959-
fn highest_state_for_peer_orders() {
1960-
let opening_fee_params = LSPS2OpeningFeeParams {
1961-
min_fee_msat: 0,
1962-
proportional: 0,
1963-
valid_until: LSPSDateTime::from_str("1970-01-01T00:00:00Z").unwrap(),
1964-
min_lifetime: 0,
1965-
max_client_to_self_delay: 0,
1966-
min_payment_size_msat: 0,
1967-
max_payment_size_msat: 0,
1968-
promise: String::new(),
1969-
};
1970-
1971-
let mut map = new_hash_map();
1972-
map.insert(
1973-
0,
1974-
OutboundJITChannel {
1975-
state: OutboundJITChannelState::PendingInitialPayment {
1976-
payment_queue: PaymentQueue::new(),
1977-
},
1978-
user_channel_id: 0,
1979-
opening_fee_params: opening_fee_params.clone(),
1980-
payment_size_msat: None,
1981-
},
1982-
);
1983-
map.insert(
1984-
1,
1985-
OutboundJITChannel {
1986-
state: OutboundJITChannelState::PendingChannelOpen {
1987-
payment_queue: PaymentQueue::new(),
1988-
opening_fee_msat: 0,
1989-
},
1990-
user_channel_id: 1,
1991-
opening_fee_params: opening_fee_params.clone(),
1992-
payment_size_msat: None,
1993-
},
1994-
);
1995-
map.insert(
1996-
2,
1997-
OutboundJITChannel {
1998-
state: OutboundJITChannelState::PaymentForwarded { channel_id: ChannelId([0; 32]) },
1999-
user_channel_id: 2,
2000-
opening_fee_params,
2001-
payment_size_msat: None,
2002-
},
2003-
);
2004-
2005-
let max_state = map.values().map(|c| c.state.clone()).max().unwrap();
2006-
assert!(matches!(max_state, OutboundJITChannelState::PaymentForwarded { .. }));
2007-
}
20081925
}

lightning-liquidity/src/lsps5/service.rs

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

25-
use crate::lsps2::service::{OutboundJITChannelState, OutboundJITStage};
2625
use bitcoin::secp256k1::PublicKey;
2726

2827
use lightning::ln::channelmanager::AChannelManager;
@@ -157,12 +156,12 @@ where
157156
/// or an LSPS2 flow that has progressed to at least
158157
/// [`OutboundJITChannelState::PendingChannelOpen`].
159158
pub(crate) fn can_accept_request(
160-
&self, client_id: &PublicKey, lsps2_max_state: Option<OutboundJITChannelState>,
159+
&self, client_id: &PublicKey, lsps2_has_opening_or_open_jit_channel: bool,
161160
lsps1_has_activity: bool,
162161
) -> bool {
163162
self.client_has_open_channel(client_id)
163+
|| lsps2_has_opening_or_open_jit_channel
164164
|| lsps1_has_activity
165-
|| lsps2_max_state.map_or(false, |s| s.stage() >= OutboundJITStage::PendingChannelOpen)
166165
}
167166

168167
fn check_prune_stale_webhooks(&self) {
@@ -530,7 +529,7 @@ where
530529
*last_pruning = Some(now);
531530
}
532531

533-
pub(crate) fn client_has_open_channel(&self, client_id: &PublicKey) -> bool {
532+
fn client_has_open_channel(&self, client_id: &PublicKey) -> bool {
534533
self.channel_manager
535534
.get_cm()
536535
.list_channels()

lightning-liquidity/src/manager.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -559,10 +559,10 @@ where
559559
LSPSMessage::LSPS5(msg @ LSPS5Message::Request(..)) => {
560560
match &self.lsps5_service_handler {
561561
Some(lsps5_service_handler) => {
562-
let lsps2_max_state = self
562+
let lsps2_has_opening_or_open_jit_channel = self
563563
.lsps2_service_handler
564564
.as_ref()
565-
.and_then(|h| h.highest_state_for_peer(sender_node_id));
565+
.map_or(false, |h| h.has_opening_or_open_jit_channel(sender_node_id));
566566
#[cfg(lsps1_service)]
567567
let lsps1_has_active_requests = self
568568
.lsps1_service_handler
@@ -573,7 +573,7 @@ where
573573

574574
if !lsps5_service_handler.can_accept_request(
575575
sender_node_id,
576-
lsps2_max_state,
576+
lsps2_has_opening_or_open_jit_channel,
577577
lsps1_has_active_requests,
578578
) {
579579
return Err(LightningError {

lightning-liquidity/tests/lsps5_integration_tests.rs

Lines changed: 77 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1221,64 +1221,82 @@ fn test_send_notifications_and_peer_connected_resets_cooldown() {
12211221
}
12221222
}
12231223

1224-
#[test]
1225-
fn dos_protection() {
1226-
let chanmon_cfgs = create_chanmon_cfgs(2);
1227-
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
1228-
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
1229-
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
1230-
let (lsps_nodes, _) = lsps5_test_setup(nodes, Arc::new(DefaultTimeProvider));
1231-
let LSPSNodes { service_node, client_node } = lsps_nodes;
1232-
let client_node_id = client_node.inner.node.get_our_node_id();
1233-
let service_node_id = service_node.inner.node.get_our_node_id();
1234-
1235-
let client_handler = client_node.liquidity_manager.lsps5_client_handler().unwrap();
1236-
1237-
let assert_reject = || -> () {
1238-
let _ = client_handler
1224+
macro_rules! assert_lsps5_reject {
1225+
($client_handler:expr, $service_node:expr, $client_node:expr, $service_node_id:expr, $client_node_id:expr) => {{
1226+
let _ = $client_handler
12391227
.set_webhook(
1240-
service_node_id,
1228+
$service_node_id,
12411229
"App".to_string(),
12421230
"https://example.org/webhook".to_string(),
12431231
)
12441232
.expect("Request should send");
1245-
let request = get_lsps_message!(client_node, service_node_id);
1233+
let request = get_lsps_message!($client_node, $service_node_id);
12461234

1247-
let result = service_node.liquidity_manager.handle_custom_message(request, client_node_id);
1235+
let result =
1236+
$service_node.liquidity_manager.handle_custom_message(request, $client_node_id);
12481237
assert!(result.is_err(), "Service should reject request without prior interaction");
12491238

1250-
assert!(service_node.liquidity_manager.get_and_clear_pending_msg().is_empty());
1251-
};
1239+
assert!($service_node.liquidity_manager.get_and_clear_pending_msg().is_empty());
1240+
}};
1241+
}
12521242

1253-
let assert_accept = || -> () {
1254-
let _ = client_handler
1243+
macro_rules! assert_lsps5_accept {
1244+
($client_handler:expr, $service_node:expr, $client_node:expr, $service_node_id:expr, $client_node_id:expr) => {{
1245+
let _ = $client_handler
12551246
.set_webhook(
1256-
service_node_id,
1247+
$service_node_id,
12571248
"App".to_string(),
12581249
"https://example.org/webhook".to_string(),
12591250
)
12601251
.expect("Request should send");
1261-
let request = get_lsps_message!(client_node, service_node_id);
1252+
let request = get_lsps_message!($client_node, $service_node_id);
12621253

1263-
let result = service_node.liquidity_manager.handle_custom_message(request, client_node_id);
1254+
let result =
1255+
$service_node.liquidity_manager.handle_custom_message(request, $client_node_id);
12641256
assert!(result.is_ok(), "Service should accept request after prior interaction");
1265-
let _ = service_node.liquidity_manager.next_event().unwrap();
1266-
let response = get_lsps_message!(service_node, client_node_id);
1267-
client_node
1257+
let _ = $service_node.liquidity_manager.next_event().unwrap();
1258+
let response = get_lsps_message!($service_node, $client_node_id);
1259+
$client_node
12681260
.liquidity_manager
1269-
.handle_custom_message(response, service_node_id)
1261+
.handle_custom_message(response, $service_node_id)
12701262
.expect("Client should handle response");
1271-
let _ = client_node.liquidity_manager.next_event().unwrap();
1272-
};
1263+
let _ = $client_node.liquidity_manager.next_event().unwrap();
1264+
}};
1265+
}
1266+
1267+
#[test]
1268+
fn dos_protection() {
1269+
let chanmon_cfgs = create_chanmon_cfgs(2);
1270+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
1271+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
1272+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
1273+
let (lsps_nodes, _) = lsps5_test_setup(nodes, Arc::new(DefaultTimeProvider));
1274+
let LSPSNodes { service_node, client_node } = lsps_nodes;
1275+
let client_node_id = client_node.inner.node.get_our_node_id();
1276+
let service_node_id = service_node.inner.node.get_our_node_id();
1277+
1278+
let client_handler = client_node.liquidity_manager.lsps5_client_handler().unwrap();
12731279

12741280
// no channel is open so far -> should reject
1275-
assert_reject();
1281+
assert_lsps5_reject!(
1282+
client_handler,
1283+
service_node,
1284+
client_node,
1285+
service_node_id,
1286+
client_node_id
1287+
);
12761288

12771289
let (_, _, _, channel_id, funding_tx) =
12781290
create_chan_between_nodes(&service_node.inner, &client_node.inner);
12791291

12801292
// now that a channel is open, should accept
1281-
assert_accept();
1293+
assert_lsps5_accept!(
1294+
client_handler,
1295+
service_node,
1296+
client_node,
1297+
service_node_id,
1298+
client_node_id
1299+
);
12821300

12831301
close_channel(&service_node.inner, &client_node.inner, &channel_id, funding_tx, true);
12841302
let node_a_reason = ClosureReason::CounterpartyInitiatedCooperativeClosure;
@@ -1287,7 +1305,13 @@ fn dos_protection() {
12871305
check_closed_event!(client_node.inner, 1, node_b_reason, [service_node_id], 100000);
12881306

12891307
// channel is now closed again -> should reject
1290-
assert_reject();
1308+
assert_lsps5_reject!(
1309+
client_handler,
1310+
service_node,
1311+
client_node,
1312+
service_node_id,
1313+
client_node_id
1314+
);
12911315
}
12921316

12931317
#[test]
@@ -1298,20 +1322,28 @@ fn lsps2_state_allows_lsps5_request() {
12981322
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
12991323

13001324
let (lsps_nodes, _) = lsps5_lsps2_test_setup(nodes, Arc::new(DefaultTimeProvider));
1301-
establish_lsps2_prior_interaction(&lsps_nodes);
13021325

1303-
let LSPSNodes { service_node, client_node } = lsps_nodes;
1304-
let service_node_id = service_node.inner.node.get_our_node_id();
1305-
let client_node_id = client_node.inner.node.get_our_node_id();
1326+
let client_node_id = lsps_nodes.client_node.inner.node.get_our_node_id();
1327+
let service_node_id = lsps_nodes.service_node.inner.node.get_our_node_id();
1328+
let client_handler = lsps_nodes.client_node.liquidity_manager.lsps5_client_handler().unwrap();
13061329

1307-
let lsps5_client = client_node.liquidity_manager.lsps5_client_handler().unwrap();
1330+
assert_lsps5_reject!(
1331+
client_handler,
1332+
lsps_nodes.service_node,
1333+
lsps_nodes.client_node,
1334+
service_node_id,
1335+
client_node_id
1336+
);
13081337

1309-
let _ = lsps5_client
1310-
.set_webhook(service_node_id, "App".to_string(), "https://example.org/webhook".to_string())
1311-
.expect("Request should send");
1312-
let request = get_lsps_message!(client_node, service_node_id);
1313-
let result = service_node.liquidity_manager.handle_custom_message(request, client_node_id);
1314-
assert!(result.is_ok(), "Service should accept request based on LSPS2 state");
1338+
establish_lsps2_prior_interaction(&lsps_nodes);
1339+
1340+
assert_lsps5_accept!(
1341+
client_handler,
1342+
lsps_nodes.service_node,
1343+
lsps_nodes.client_node,
1344+
service_node_id,
1345+
client_node_id
1346+
);
13151347
}
13161348

13171349
fn establish_lsps2_prior_interaction(lsps_nodes: &LSPSNodes) {

0 commit comments

Comments
 (0)