Skip to content

Commit 63a8b55

Browse files
Add BOLT12 test with external node
Introduce `do_bolt12_cycle_with_external_node` to test BOLT12 payments between LDK and external nodes, including offers without and with specified amounts, as well as receiving payments. Extend ExternalLightningNode trait with `generate_offer` and `pay_offer`.
1 parent 835d2c7 commit 63a8b55

File tree

2 files changed

+117
-1
lines changed

2 files changed

+117
-1
lines changed

tests/common/external_node.rs

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ use std::sync::OnceLock;
2929

3030
use crate::common::{
3131
distribute_funds_unconfirmed, expect_channel_pending_event, expect_channel_ready_event,
32-
expect_event, generate_blocks_and_wait, premine_blocks, random_config, wait_for_tx,
32+
expect_event, expect_payment_successful_event, generate_blocks_and_wait, premine_blocks,
33+
random_config, wait_for_tx,
3334
};
3435

3536
pub trait ExternalLightningNode {
@@ -46,6 +47,14 @@ pub trait ExternalLightningNode {
4647
fn create_new_address(&mut self) -> String;
4748

4849
fn open_channel(&mut self, node_id: PublicKey, funding_amount_sat: u64);
50+
51+
fn generate_offer(&mut self, _amount_msat: Option<u64>, _description: &str) -> String {
52+
panic!("Not implemented")
53+
}
54+
55+
fn pay_offer(&mut self, _offer: &str, _amount_msat: Option<u64>) -> String {
56+
panic!("Not implemented")
57+
}
4958
}
5059

5160
static BITCOIND_CLIENT: OnceLock<BitcoindClient> = OnceLock::new();
@@ -309,3 +318,72 @@ pub(crate) fn do_external_node_opens_channel_simple_transactions_with_ldk<
309318

310319
node.stop().unwrap();
311320
}
321+
322+
pub(crate) fn do_bolt12_cycle_with_external_node<E: ExternalLightningNode>(external_node: &mut E) {
323+
// Initialize LDK node and clients
324+
let (node, bitcoind_client, electrs_client) = setup_test_node(true);
325+
326+
// setup external node info
327+
let (external_node_id, external_node_address) = external_node.get_node_info();
328+
329+
// Open the channel
330+
add_onchain_funds(&bitcoind_client, &electrs_client, &node);
331+
let funding_amount_sat = 2_000_000;
332+
let push_msat = Some(500_000_000);
333+
let (user_channel_id, funding_txo) = open_channel(
334+
&bitcoind_client,
335+
&electrs_client,
336+
&node,
337+
external_node_id,
338+
external_node_address,
339+
funding_amount_sat,
340+
push_msat,
341+
);
342+
343+
// Send a payment to the external node, without specifying an amount
344+
let mut payer_note = "without specifying an amount";
345+
let offer_string = external_node.generate_offer(None, payer_note);
346+
let offer = offer::Offer::from_str(&offer_string).unwrap();
347+
let mut amount_msat = 100_000_000;
348+
let payment_id = node
349+
.bolt12_payment()
350+
.send_using_amount(&offer, amount_msat, None, Some(payer_note.to_string()))
351+
.unwrap();
352+
expect_payment_successful_event!(node, Some(payment_id), None);
353+
354+
// Send a payment to the external node, specifying an amount
355+
amount_msat = 100_000_000;
356+
payer_note = "specifying an amount";
357+
let offer_string = external_node.generate_offer(Some(amount_msat), payer_note);
358+
let offer = offer::Offer::from_str(&offer_string).unwrap();
359+
let payment_id =
360+
node.bolt12_payment().send(&offer, None, Some(payer_note.to_string())).unwrap();
361+
expect_payment_successful_event!(node, Some(payment_id), None);
362+
363+
// Send a payment to LDK, without specifying an amount
364+
let offer = node.bolt12_payment().receive(0, "", None, None).unwrap();
365+
let offer_string: String = offer.to_string();
366+
let amount_msat = 9_000_000;
367+
external_node.pay_offer(&offer_string, Some(amount_msat));
368+
expect_event!(node, PaymentReceived);
369+
370+
// Send a payment to LDK, specifying an amount
371+
let offer = node.bolt12_payment().receive(0, "", None, None).unwrap();
372+
let offer_string: String = offer.to_string();
373+
let amount_msat = 9_000_000;
374+
external_node.pay_offer(&offer_string, Some(amount_msat));
375+
expect_event!(node, PaymentReceived);
376+
377+
// Close the channel
378+
close_channel(
379+
&bitcoind_client,
380+
&electrs_client,
381+
external_node,
382+
&node,
383+
funding_txo,
384+
external_node_id,
385+
&user_channel_id,
386+
None,
387+
);
388+
node.stop().unwrap();
389+
}

tests/integration_tests_cln.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use std::default::Default;
2121
use std::str::FromStr;
2222

2323
use crate::common::external_node::{
24+
do_bolt12_cycle_with_external_node,
2425
do_external_node_opens_channel_simple_transactions_with_ldk,
2526
do_ldk_opens_channel_full_cycle_with_external_node, init_bitcoind_client,
2627
ExternalLightningNode,
@@ -54,6 +55,13 @@ fn test_cln_opens_channel_with_ldk() {
5455
do_external_node_opens_channel_simple_transactions_with_ldk(&mut client);
5556
}
5657

58+
#[test]
59+
fn test_simple_bolt12() {
60+
init_bitcoind_client();
61+
let mut client = ClnClient::new();
62+
do_bolt12_cycle_with_external_node(&mut client);
63+
}
64+
5765
struct ClnClient {
5866
pub client: LightningRPC,
5967
pub rng: rand::rngs::ThreadRng,
@@ -162,4 +170,34 @@ impl ExternalLightningNode for ClnClient {
162170
)
163171
.unwrap();
164172
}
173+
174+
fn generate_offer(&mut self, amount_msat: Option<u64>, description: &str) -> String {
175+
let mut input = serde_json::json!({});
176+
if let Some(amt) = amount_msat {
177+
input["amount"] = amt.to_string().into();
178+
} else {
179+
input["amount"] = "any".into();
180+
}
181+
input["description"] = description.into();
182+
let offer: serde_json::Value = self.client.call("offer", input).unwrap();
183+
offer["bolt12"].as_str().unwrap().to_string()
184+
}
185+
186+
fn pay_offer(&mut self, offer: &str, amount_msat: Option<u64>) -> String {
187+
let input = if let Some(amt) = amount_msat {
188+
serde_json::json!({
189+
"offer": offer,
190+
"amount_msat": amt,
191+
})
192+
} else {
193+
serde_json::json!({
194+
"offer": offer,
195+
})
196+
};
197+
198+
let response: serde_json::Value = self.client.call("fetchinvoice", input).unwrap();
199+
self.pay_invoice(response["invoice"].as_str().unwrap());
200+
201+
response["invoice"].as_str().unwrap().to_string()
202+
}
165203
}

0 commit comments

Comments
 (0)