Skip to content

Commit 441801d

Browse files
Matt Coralloclaude
andcommitted
Cover LSPS2 BOLT12 JIT payments
Exercise the BOLT12 receive flow with LSPS2 JIT channels via plain BOLT12 offers relying on the parameters negotiated at startup, both for the initial channel open and for paying an offer after LSP restart. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent e019741 commit 441801d

1 file changed

Lines changed: 201 additions & 0 deletions

File tree

tests/integration_tests_rust.rs

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2127,6 +2127,207 @@ async fn drop_in_async_context() {
21272127
node.stop().unwrap();
21282128
}
21292129

2130+
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
2131+
async fn lsps2_bolt12_payment_succeeds_after_lsp_restart() {
2132+
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
2133+
2134+
let esplora_url = format!("http://{}", electrsd.esplora_url.as_ref().unwrap());
2135+
2136+
let mut sync_config = EsploraSyncConfig::default();
2137+
sync_config.background_sync_config = None;
2138+
2139+
let lsps2_service_config = LSPS2ServiceConfig {
2140+
require_token: None,
2141+
advertise_service: false,
2142+
channel_opening_fee_ppm: 10_000,
2143+
channel_over_provisioning_ppm: 100_000,
2144+
max_payment_size_msat: 1_000_000_000,
2145+
min_payment_size_msat: 0,
2146+
min_channel_lifetime: 100,
2147+
min_channel_opening_fee_msat: 0,
2148+
max_client_to_self_delay: 1024,
2149+
client_trusts_lsp: true,
2150+
disable_client_reserve: false,
2151+
};
2152+
2153+
let service_config = random_config(true);
2154+
setup_builder!(service_builder, service_config.node_config);
2155+
service_builder.set_chain_source_esplora(esplora_url.clone(), Some(sync_config));
2156+
service_builder.enable_liquidity_provider(lsps2_service_config);
2157+
let service_node = service_builder.build(service_config.node_entropy.into()).unwrap();
2158+
service_node.start().unwrap();
2159+
let service_node_id = service_node.node_id();
2160+
let service_addr = service_node.listening_addresses().unwrap().first().unwrap().clone();
2161+
2162+
let client_config = random_config(true);
2163+
setup_builder!(client_builder, client_config.node_config);
2164+
client_builder.set_chain_source_esplora(esplora_url.clone(), Some(sync_config));
2165+
client_builder.add_liquidity_source(service_node_id, service_addr.clone(), None, true);
2166+
let client_node = client_builder.build(client_config.node_entropy.into()).unwrap();
2167+
client_node.start().unwrap();
2168+
2169+
let payer_config = random_config(true);
2170+
setup_builder!(payer_builder, payer_config.node_config);
2171+
payer_builder.set_chain_source_esplora(esplora_url, Some(sync_config));
2172+
let payer_node = payer_builder.build(payer_config.node_entropy.into()).unwrap();
2173+
payer_node.start().unwrap();
2174+
2175+
let service_onchain_addr = service_node.onchain_payment().new_address().unwrap();
2176+
let client_onchain_addr = client_node.onchain_payment().new_address().unwrap();
2177+
let payer_onchain_addr = payer_node.onchain_payment().new_address().unwrap();
2178+
2179+
premine_and_distribute_funds(
2180+
&bitcoind.client,
2181+
&electrsd.client,
2182+
vec![service_onchain_addr, client_onchain_addr, payer_onchain_addr],
2183+
Amount::from_sat(10_000_000),
2184+
)
2185+
.await;
2186+
service_node.sync_wallets().unwrap();
2187+
client_node.sync_wallets().unwrap();
2188+
payer_node.sync_wallets().unwrap();
2189+
2190+
open_channel(&payer_node, &service_node, 5_000_000, false, &electrsd).await;
2191+
generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await;
2192+
service_node.sync_wallets().unwrap();
2193+
payer_node.sync_wallets().unwrap();
2194+
expect_channel_ready_event!(payer_node, service_node.node_id());
2195+
expect_channel_ready_event!(service_node, payer_node.node_id());
2196+
2197+
let jit_amount_msat = 100_000_000;
2198+
let offer = client_node
2199+
.bolt12_payment()
2200+
.receive(jit_amount_msat, "lsps2-bolt12-after-restart", None, Some(1))
2201+
.unwrap();
2202+
2203+
service_node.stop().unwrap();
2204+
service_node.start().unwrap();
2205+
2206+
// Ensure peers are connected after the restart before paying the offer.
2207+
let _ = payer_node.connect(service_node_id, service_addr.clone(), false);
2208+
let _ = client_node.connect(service_node_id, service_addr, false);
2209+
2210+
let payment_id = payer_node
2211+
.bolt12_payment()
2212+
.send(&offer, Some(1), Some("restart".to_string()), None)
2213+
.unwrap();
2214+
2215+
expect_channel_pending_event!(service_node, client_node.node_id());
2216+
expect_channel_ready_event!(service_node, client_node.node_id());
2217+
expect_channel_pending_event!(client_node, service_node.node_id());
2218+
expect_channel_ready_event!(client_node, service_node.node_id());
2219+
2220+
let service_fee_msat = (jit_amount_msat * 10_000) / 1_000_000;
2221+
let expected_received_amount_msat = jit_amount_msat - service_fee_msat;
2222+
2223+
expect_payment_successful_event!(payer_node, Some(payment_id), None);
2224+
expect_payment_received_event!(client_node, expected_received_amount_msat);
2225+
}
2226+
2227+
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
2228+
async fn lsps2_bolt12_jit_channel_opens_successfully() {
2229+
// Verify the full BOLT12 + LSPS2 JIT channel flow: a client with no pre-existing channels
2230+
// creates a plain BOLT12 offer, a payer pays it, and the LSP opens a channel just-in-time.
2231+
// The LSPS2 parameters negotiated at startup are injected into the offer's invoices as
2232+
// JIT-channel blinded payment paths by the client's router.
2233+
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
2234+
2235+
let esplora_url = format!("http://{}", electrsd.esplora_url.as_ref().unwrap());
2236+
2237+
let mut sync_config = EsploraSyncConfig::default();
2238+
sync_config.background_sync_config = None;
2239+
2240+
let lsps2_service_config = LSPS2ServiceConfig {
2241+
require_token: None,
2242+
advertise_service: false,
2243+
channel_opening_fee_ppm: 10_000,
2244+
channel_over_provisioning_ppm: 100_000,
2245+
max_payment_size_msat: 1_000_000_000,
2246+
min_payment_size_msat: 0,
2247+
min_channel_lifetime: 100,
2248+
min_channel_opening_fee_msat: 0,
2249+
max_client_to_self_delay: 1024,
2250+
client_trusts_lsp: true,
2251+
disable_client_reserve: false,
2252+
};
2253+
2254+
let service_config = random_config(true);
2255+
setup_builder!(service_builder, service_config.node_config);
2256+
service_builder.set_chain_source_esplora(esplora_url.clone(), Some(sync_config));
2257+
service_builder.enable_liquidity_provider(lsps2_service_config);
2258+
let service_node = service_builder.build(service_config.node_entropy.into()).unwrap();
2259+
service_node.start().unwrap();
2260+
let service_node_id = service_node.node_id();
2261+
let service_addr = service_node.listening_addresses().unwrap().first().unwrap().clone();
2262+
2263+
let client_config = random_config(true);
2264+
setup_builder!(client_builder, client_config.node_config);
2265+
client_builder.set_chain_source_esplora(esplora_url.clone(), Some(sync_config));
2266+
client_builder.add_liquidity_source(service_node_id, service_addr.clone(), None, true);
2267+
let client_node = client_builder.build(client_config.node_entropy.into()).unwrap();
2268+
client_node.start().unwrap();
2269+
2270+
let payer_config = random_config(true);
2271+
setup_builder!(payer_builder, payer_config.node_config);
2272+
payer_builder.set_chain_source_esplora(esplora_url, Some(sync_config));
2273+
let payer_node = payer_builder.build(payer_config.node_entropy.into()).unwrap();
2274+
payer_node.start().unwrap();
2275+
2276+
let service_onchain_addr = service_node.onchain_payment().new_address().unwrap();
2277+
let client_onchain_addr = client_node.onchain_payment().new_address().unwrap();
2278+
let payer_onchain_addr = payer_node.onchain_payment().new_address().unwrap();
2279+
2280+
premine_and_distribute_funds(
2281+
&bitcoind.client,
2282+
&electrsd.client,
2283+
vec![service_onchain_addr, client_onchain_addr, payer_onchain_addr],
2284+
Amount::from_sat(10_000_000),
2285+
)
2286+
.await;
2287+
service_node.sync_wallets().unwrap();
2288+
client_node.sync_wallets().unwrap();
2289+
payer_node.sync_wallets().unwrap();
2290+
2291+
open_channel(&payer_node, &service_node, 5_000_000, false, &electrsd).await;
2292+
generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await;
2293+
service_node.sync_wallets().unwrap();
2294+
payer_node.sync_wallets().unwrap();
2295+
expect_channel_ready_event!(payer_node, service_node.node_id());
2296+
expect_channel_ready_event!(service_node, payer_node.node_id());
2297+
2298+
assert_eq!(
2299+
service_node.list_channels().len(),
2300+
1,
2301+
"Only payer-service channel should exist before JIT flow"
2302+
);
2303+
2304+
let jit_amount_msat = 100_000_000;
2305+
let offer = client_node
2306+
.bolt12_payment()
2307+
.receive(jit_amount_msat, "jit-payment", None, Some(1))
2308+
.unwrap();
2309+
2310+
let payment_id =
2311+
payer_node.bolt12_payment().send(&offer, Some(1), Some("pay".to_string()), None).unwrap();
2312+
2313+
expect_channel_pending_event!(service_node, client_node.node_id());
2314+
expect_channel_ready_event!(service_node, client_node.node_id());
2315+
expect_channel_pending_event!(client_node, service_node.node_id());
2316+
expect_channel_ready_event!(client_node, service_node.node_id());
2317+
2318+
let service_fee_msat = (jit_amount_msat * 10_000) / 1_000_000;
2319+
let expected_received = jit_amount_msat - service_fee_msat;
2320+
expect_payment_successful_event!(payer_node, Some(payment_id), None);
2321+
expect_payment_received_event!(client_node, expected_received);
2322+
2323+
// The LSP should now have two channels: payer<->service and service<->client.
2324+
assert_eq!(
2325+
service_node.list_channels().len(),
2326+
2,
2327+
"JIT channel should have been opened alongside the payer channel"
2328+
);
2329+
}
2330+
21302331
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
21312332
async fn lsps2_client_trusts_lsp() {
21322333
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();

0 commit comments

Comments
 (0)