Skip to content
This repository was archived by the owner on Jan 6, 2025. It is now read-only.

Commit dffa067

Browse files
committed
Add initial LSPS2 Integration test
.. we test the invoice generation flow up until creating a JIT invoice.
1 parent 427bd6e commit dffa067

File tree

1 file changed

+245
-0
lines changed

1 file changed

+245
-0
lines changed

tests/lsps2_integration_tests.rs

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
#![cfg(all(test, feature = "std"))]
2+
3+
mod common;
4+
5+
use common::{create_service_and_client_nodes, get_lsps_message, Node};
6+
7+
use lightning_liquidity::events::Event;
8+
use lightning_liquidity::lsps2::client::LSPS2ClientConfig;
9+
use lightning_liquidity::lsps2::event::{LSPS2ClientEvent, LSPS2ServiceEvent};
10+
use lightning_liquidity::lsps2::msgs::RawOpeningFeeParams;
11+
use lightning_liquidity::lsps2::service::LSPS2ServiceConfig;
12+
use lightning_liquidity::lsps2::utils::is_valid_opening_fee_params;
13+
use lightning_liquidity::{LiquidityClientConfig, LiquidityServiceConfig};
14+
15+
use lightning::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY_DELTA;
16+
use lightning::ln::peer_handler::CustomMessageHandler;
17+
use lightning::log_error;
18+
use lightning::routing::router::{RouteHint, RouteHintHop};
19+
use lightning::util::logger::Logger;
20+
21+
use lightning_invoice::{Bolt11Invoice, InvoiceBuilder, RoutingFees};
22+
23+
use bitcoin::hashes::{sha256, Hash};
24+
use bitcoin::secp256k1::{PublicKey, Secp256k1};
25+
use bitcoin::Network;
26+
27+
use chrono::DateTime;
28+
29+
use std::time::Duration;
30+
31+
fn create_jit_invoice(
32+
node: &Node, service_node_id: PublicKey, intercept_scid: u64, cltv_expiry_delta: u32,
33+
payment_size_msat: Option<u64>, description: &str, expiry_secs: u32,
34+
) -> Result<Bolt11Invoice, ()> {
35+
// LSPS2 requires min_final_cltv_expiry_delta to be at least 2 more than usual.
36+
let min_final_cltv_expiry_delta = MIN_FINAL_CLTV_EXPIRY_DELTA + 2;
37+
let (payment_hash, payment_secret) = node
38+
.channel_manager
39+
.create_inbound_payment(None, expiry_secs, Some(min_final_cltv_expiry_delta))
40+
.map_err(|e| {
41+
log_error!(node.logger, "Failed to register inbound payment: {:?}", e);
42+
()
43+
})?;
44+
45+
let route_hint = RouteHint(vec![RouteHintHop {
46+
src_node_id: service_node_id,
47+
short_channel_id: intercept_scid,
48+
fees: RoutingFees { base_msat: 0, proportional_millionths: 0 },
49+
cltv_expiry_delta: cltv_expiry_delta as u16,
50+
htlc_minimum_msat: None,
51+
htlc_maximum_msat: None,
52+
}]);
53+
54+
let payment_hash = sha256::Hash::from_slice(&payment_hash.0).map_err(|e| {
55+
log_error!(node.logger, "Invalid payment hash: {:?}", e);
56+
()
57+
})?;
58+
59+
let currency = Network::Bitcoin.into();
60+
let mut invoice_builder = InvoiceBuilder::new(currency)
61+
.description(description.to_string())
62+
.payment_hash(payment_hash)
63+
.payment_secret(payment_secret)
64+
.current_timestamp()
65+
.min_final_cltv_expiry_delta(min_final_cltv_expiry_delta.into())
66+
.expiry_time(Duration::from_secs(expiry_secs.into()))
67+
.private_route(route_hint);
68+
69+
if let Some(amount_msat) = payment_size_msat {
70+
invoice_builder = invoice_builder.amount_milli_satoshis(amount_msat).basic_mpp();
71+
}
72+
73+
invoice_builder
74+
.build_signed(|hash| {
75+
Secp256k1::new().sign_ecdsa_recoverable(hash, &node.keys_manager.get_node_secret_key())
76+
})
77+
.map_err(|e| {
78+
log_error!(node.logger, "Failed to build and sign invoice: {}", e);
79+
()
80+
})
81+
}
82+
83+
#[test]
84+
fn invoice_generation_flow() {
85+
let promise_secret = [42; 32];
86+
let lsps2_service_config = LSPS2ServiceConfig { promise_secret };
87+
let service_config = LiquidityServiceConfig {
88+
#[cfg(lsps1)]
89+
lsps1_service_config: None,
90+
lsps2_service_config: Some(lsps2_service_config),
91+
advertise_service: true,
92+
};
93+
94+
let lsps2_client_config = LSPS2ClientConfig::default();
95+
let client_config = LiquidityClientConfig {
96+
#[cfg(lsps1)]
97+
lsps1_client_config: None,
98+
lsps2_client_config: Some(lsps2_client_config),
99+
};
100+
101+
let (service_node, client_node) =
102+
create_service_and_client_nodes("invoice_generation_flow", service_config, client_config);
103+
104+
let service_handler = service_node.liquidity_manager.lsps2_service_handler().unwrap();
105+
let service_node_id = service_node.channel_manager.get_our_node_id();
106+
107+
let client_handler = client_node.liquidity_manager.lsps2_client_handler().unwrap();
108+
let client_node_id = client_node.channel_manager.get_our_node_id();
109+
110+
let get_info_request_id = client_handler.request_opening_params(service_node_id, None);
111+
let get_info_request = get_lsps_message!(client_node, service_node_id);
112+
113+
service_node
114+
.liquidity_manager
115+
.handle_custom_message(get_info_request, &client_node_id)
116+
.unwrap();
117+
118+
let get_info_event = service_node.liquidity_manager.next_event().unwrap();
119+
match get_info_event {
120+
Event::LSPS2Service(LSPS2ServiceEvent::GetInfo {
121+
request_id,
122+
counterparty_node_id,
123+
token,
124+
}) => {
125+
assert_eq!(request_id, get_info_request_id);
126+
assert_eq!(counterparty_node_id, client_node_id);
127+
assert_eq!(token, None);
128+
},
129+
_ => panic!("Unexpected event"),
130+
}
131+
132+
let raw_opening_params = RawOpeningFeeParams {
133+
min_fee_msat: 100,
134+
proportional: 21,
135+
valid_until: DateTime::parse_from_rfc3339("2035-05-20T08:30:45Z").unwrap().into(),
136+
min_lifetime: 144,
137+
max_client_to_self_delay: 128,
138+
min_payment_size_msat: 1,
139+
max_payment_size_msat: 100_000_000,
140+
};
141+
142+
service_handler
143+
.opening_fee_params_generated(
144+
&client_node_id,
145+
get_info_request_id.clone(),
146+
vec![raw_opening_params],
147+
)
148+
.unwrap();
149+
let get_info_response = get_lsps_message!(service_node, client_node_id);
150+
151+
client_node
152+
.liquidity_manager
153+
.handle_custom_message(get_info_response, &service_node_id)
154+
.unwrap();
155+
156+
let opening_params_event = client_node.liquidity_manager.next_event().unwrap();
157+
let opening_fee_params = match opening_params_event {
158+
Event::LSPS2Client(LSPS2ClientEvent::OpeningParametersReady {
159+
request_id,
160+
counterparty_node_id,
161+
opening_fee_params_menu,
162+
}) => {
163+
assert_eq!(request_id, get_info_request_id);
164+
assert_eq!(counterparty_node_id, service_node_id);
165+
let opening_fee_params = opening_fee_params_menu.first().unwrap().clone();
166+
assert!(is_valid_opening_fee_params(&opening_fee_params, &promise_secret));
167+
opening_fee_params
168+
},
169+
_ => panic!("Unexpected event"),
170+
};
171+
172+
let payment_size_msat = Some(1_000_000);
173+
let buy_request_id = client_handler
174+
.select_opening_params(service_node_id, payment_size_msat, opening_fee_params.clone())
175+
.unwrap();
176+
177+
let buy_request = get_lsps_message!(client_node, service_node_id);
178+
service_node.liquidity_manager.handle_custom_message(buy_request, &client_node_id).unwrap();
179+
180+
let buy_event = service_node.liquidity_manager.next_event().unwrap();
181+
match buy_event {
182+
Event::LSPS2Service(LSPS2ServiceEvent::BuyRequest {
183+
request_id,
184+
counterparty_node_id,
185+
opening_fee_params: ofp,
186+
payment_size_msat: psm,
187+
}) => {
188+
assert_eq!(request_id, buy_request_id);
189+
assert_eq!(counterparty_node_id, client_node_id);
190+
assert_eq!(opening_fee_params, ofp);
191+
assert_eq!(payment_size_msat, psm);
192+
},
193+
_ => panic!("Unexpected event"),
194+
}
195+
196+
let user_channel_id = 42;
197+
let cltv_expiry_delta = 144;
198+
let intercept_scid = service_node.channel_manager.get_intercept_scid();
199+
let client_trusts_lsp = true;
200+
201+
service_handler
202+
.invoice_parameters_generated(
203+
&client_node_id,
204+
buy_request_id.clone(),
205+
intercept_scid,
206+
cltv_expiry_delta,
207+
client_trusts_lsp,
208+
user_channel_id,
209+
)
210+
.unwrap();
211+
212+
let buy_response = get_lsps_message!(service_node, client_node_id);
213+
client_node.liquidity_manager.handle_custom_message(buy_response, &service_node_id).unwrap();
214+
215+
let invoice_params_event = client_node.liquidity_manager.next_event().unwrap();
216+
match invoice_params_event {
217+
Event::LSPS2Client(LSPS2ClientEvent::InvoiceParametersReady {
218+
request_id,
219+
counterparty_node_id,
220+
intercept_scid: iscid,
221+
cltv_expiry_delta: ced,
222+
payment_size_msat: psm,
223+
}) => {
224+
assert_eq!(request_id, buy_request_id);
225+
assert_eq!(counterparty_node_id, service_node_id);
226+
assert_eq!(intercept_scid, iscid);
227+
assert_eq!(cltv_expiry_delta, ced);
228+
assert_eq!(payment_size_msat, psm);
229+
},
230+
_ => panic!("Unexpected event"),
231+
};
232+
233+
let description = "asdf";
234+
let expiry_secs = 3600;
235+
let _invoice = create_jit_invoice(
236+
&client_node,
237+
service_node_id,
238+
intercept_scid,
239+
cltv_expiry_delta,
240+
payment_size_msat,
241+
description,
242+
expiry_secs,
243+
)
244+
.unwrap();
245+
}

0 commit comments

Comments
 (0)