|
9 | 9 |
|
10 | 10 | mod common; |
11 | 11 |
|
12 | | -use std::default::Default; |
13 | 12 | use std::str::FromStr; |
14 | 13 |
|
15 | | -use clightningrpc::lightningrpc::LightningRPC; |
16 | | -use clightningrpc::responses::NetworkAddress; |
17 | 14 | use electrsd::corepc_client::client_sync::Auth; |
18 | 15 | use electrsd::corepc_node::Client as BitcoindClient; |
19 | 16 | use electrum_client::Client as ElectrumClient; |
20 | | -use ldk_node::bitcoin::secp256k1::PublicKey; |
21 | 17 | use ldk_node::bitcoin::Amount; |
22 | | -use ldk_node::lightning::ln::msgs::SocketAddress; |
23 | 18 | use ldk_node::{Builder, Event}; |
24 | | -use lightning_invoice::{Bolt11Invoice, Bolt11InvoiceDescription, Description}; |
25 | | -use rand::distr::Alphanumeric; |
26 | | -use rand::{rng, Rng}; |
| 19 | +use proptest::prelude::*; |
| 20 | +use proptest::proptest; |
27 | 21 |
|
28 | | -#[tokio::test(flavor = "multi_thread", worker_threads = 1)] |
29 | | -async fn test_cln() { |
30 | | - // Setup bitcoind / electrs clients |
31 | | - let bitcoind_client = BitcoindClient::new_with_auth( |
| 22 | +use common::cln::TestClnNode; |
| 23 | +use common::external_node::ExternalNode; |
| 24 | +use common::scenarios::*; |
| 25 | + |
| 26 | +fn setup_clients() -> (BitcoindClient, ElectrumClient, TestClnNode) { |
| 27 | + let bitcoind = BitcoindClient::new_with_auth( |
32 | 28 | "http://127.0.0.1:18443", |
33 | 29 | Auth::UserPass("user".to_string(), "pass".to_string()), |
34 | 30 | ) |
35 | 31 | .unwrap(); |
36 | | - let electrs_client = ElectrumClient::new("tcp://127.0.0.1:50001").unwrap(); |
37 | | - |
38 | | - // Give electrs a kick. |
39 | | - common::generate_blocks_and_wait(&bitcoind_client, &electrs_client, 1).await; |
| 32 | + let electrs = ElectrumClient::new("tcp://127.0.0.1:50001").unwrap(); |
| 33 | + let cln = TestClnNode::from_env(); |
| 34 | + (bitcoind, electrs, cln) |
| 35 | +} |
40 | 36 |
|
41 | | - // Setup LDK Node |
| 37 | +fn setup_ldk_node() -> ldk_node::Node { |
42 | 38 | let config = common::random_config(true); |
43 | 39 | let mut builder = Builder::from_config(config.node_config); |
44 | 40 | builder.set_chain_source_esplora("http://127.0.0.1:3002".to_string(), None); |
45 | | - |
46 | 41 | let node = builder.build(config.node_entropy).unwrap(); |
47 | 42 | node.start().unwrap(); |
| 43 | + node |
| 44 | +} |
48 | 45 |
|
49 | | - // Premine some funds and distribute |
50 | | - let address = node.onchain_payment().new_address().unwrap(); |
51 | | - let premine_amount = Amount::from_sat(5_000_000); |
52 | | - common::premine_and_distribute_funds( |
53 | | - &bitcoind_client, |
54 | | - &electrs_client, |
55 | | - vec![address], |
56 | | - premine_amount, |
57 | | - ) |
58 | | - .await; |
59 | | - |
60 | | - // Setup CLN |
61 | | - let sock = "/tmp/lightning-rpc"; |
62 | | - let cln_client = LightningRPC::new(&sock); |
63 | | - let cln_info = { |
64 | | - loop { |
65 | | - let info = cln_client.getinfo().unwrap(); |
66 | | - // Wait for CLN to sync block height before channel open. |
67 | | - // Prevents crash due to unset blockheight (see LDK Node issue #527). |
68 | | - if info.blockheight > 0 { |
69 | | - break info; |
70 | | - } |
71 | | - tokio::time::sleep(std::time::Duration::from_millis(250)).await; |
72 | | - } |
73 | | - }; |
74 | | - let cln_node_id = PublicKey::from_str(&cln_info.id).unwrap(); |
75 | | - let cln_address: SocketAddress = match cln_info.binding.first().unwrap() { |
76 | | - NetworkAddress::Ipv4 { address, port } => { |
77 | | - std::net::SocketAddrV4::new(*address, *port).into() |
78 | | - }, |
79 | | - NetworkAddress::Ipv6 { address, port } => { |
80 | | - std::net::SocketAddrV6::new(*address, *port, 0, 0).into() |
81 | | - }, |
82 | | - _ => { |
83 | | - panic!() |
84 | | - }, |
85 | | - }; |
86 | | - |
87 | | - node.sync_wallets().unwrap(); |
88 | | - |
89 | | - // Open the channel |
90 | | - let funding_amount_sat = 1_000_000; |
91 | | - |
92 | | - node.open_channel(cln_node_id, cln_address, funding_amount_sat, Some(500_000_000), None) |
| 46 | +#[tokio::test(flavor = "multi_thread", worker_threads = 1)] |
| 47 | +async fn test_cln_basic_channel_cycle() { |
| 48 | + let (bitcoind, electrs, cln) = setup_clients(); |
| 49 | + cln.wait_for_sync().await; |
| 50 | + common::generate_blocks_and_wait(&bitcoind, &electrs, 1).await; |
| 51 | + |
| 52 | + let node = setup_ldk_node(); |
| 53 | + setup_interop_test(&node, &cln, &bitcoind, &electrs).await; |
| 54 | + |
| 55 | + let (user_channel_id, _ext_channel_id) = |
| 56 | + open_channel_to_external(&node, &cln, &bitcoind, &electrs, 1_000_000, Some(500_000_000)) |
| 57 | + .await; |
| 58 | + |
| 59 | + // LDK -> CLN payment |
| 60 | + let invoice = cln.create_invoice(10_000_000, "cln-test-send").await.unwrap(); |
| 61 | + let parsed = lightning_invoice::Bolt11Invoice::from_str(&invoice).unwrap(); |
| 62 | + node.bolt11_payment().send(&parsed, None).unwrap(); |
| 63 | + common::expect_event!(node, PaymentSuccessful); |
| 64 | + |
| 65 | + // CLN -> LDK payment |
| 66 | + let ldk_invoice = node |
| 67 | + .bolt11_payment() |
| 68 | + .receive( |
| 69 | + 10_000_000, |
| 70 | + &lightning_invoice::Bolt11InvoiceDescription::Direct( |
| 71 | + lightning_invoice::Description::new("cln-test-recv".to_string()).unwrap(), |
| 72 | + ), |
| 73 | + 3600, |
| 74 | + ) |
93 | 75 | .unwrap(); |
| 76 | + cln.pay_invoice(&ldk_invoice.to_string()).await.unwrap(); |
| 77 | + common::expect_event!(node, PaymentReceived); |
94 | 78 |
|
95 | | - let funding_txo = common::expect_channel_pending_event!(node, cln_node_id); |
96 | | - common::wait_for_tx(&electrs_client, funding_txo.txid).await; |
97 | | - common::generate_blocks_and_wait(&bitcoind_client, &electrs_client, 6).await; |
98 | | - node.sync_wallets().unwrap(); |
99 | | - let user_channel_id = common::expect_channel_ready_event!(node, cln_node_id); |
| 79 | + test_cooperative_close_by_ldk(&node, &cln, &user_channel_id).await; |
| 80 | + node.stop().unwrap(); |
| 81 | +} |
100 | 82 |
|
101 | | - // Send a payment to CLN |
102 | | - let mut rng = rng(); |
103 | | - let rand_label: String = (0..7).map(|_| rng.sample(Alphanumeric) as char).collect(); |
104 | | - let cln_invoice = |
105 | | - cln_client.invoice(Some(10_000_000), &rand_label, &rand_label, None, None, None).unwrap(); |
106 | | - let parsed_invoice = Bolt11Invoice::from_str(&cln_invoice.bolt11).unwrap(); |
| 83 | +#[tokio::test(flavor = "multi_thread", worker_threads = 1)] |
| 84 | +async fn test_cln_disconnect_reconnect() { |
| 85 | + let (bitcoind, electrs, cln) = setup_clients(); |
| 86 | + cln.wait_for_sync().await; |
| 87 | + common::generate_blocks_and_wait(&bitcoind, &electrs, 1).await; |
107 | 88 |
|
108 | | - node.bolt11_payment().send(&parsed_invoice, None).unwrap(); |
109 | | - common::expect_event!(node, PaymentSuccessful); |
110 | | - let cln_listed_invoices = |
111 | | - cln_client.listinvoices(Some(&rand_label), None, None, None).unwrap().invoices; |
112 | | - assert_eq!(cln_listed_invoices.len(), 1); |
113 | | - assert_eq!(cln_listed_invoices.first().unwrap().status, "paid"); |
114 | | - |
115 | | - // Send a payment to LDK |
116 | | - let rand_label: String = (0..7).map(|_| rng.sample(Alphanumeric) as char).collect(); |
117 | | - let invoice_description = |
118 | | - Bolt11InvoiceDescription::Direct(Description::new(rand_label).unwrap()); |
119 | | - let ldk_invoice = |
120 | | - node.bolt11_payment().receive(10_000_000, &invoice_description, 3600).unwrap(); |
121 | | - cln_client.pay(&ldk_invoice.to_string(), Default::default()).unwrap(); |
122 | | - common::expect_event!(node, PaymentReceived); |
| 89 | + let node = setup_ldk_node(); |
| 90 | + setup_interop_test(&node, &cln, &bitcoind, &electrs).await; |
| 91 | + let (_user_ch, _ext_ch) = |
| 92 | + open_channel_to_external(&node, &cln, &bitcoind, &electrs, 1_000_000, Some(500_000_000)) |
| 93 | + .await; |
123 | 94 |
|
124 | | - node.close_channel(&user_channel_id, cln_node_id).unwrap(); |
125 | | - common::expect_event!(node, ChannelClosed); |
| 95 | + test_disconnect_reconnect_idle(&node, &cln, &bitcoind, &electrs, &Side::Ldk).await; |
| 96 | + test_disconnect_reconnect_idle(&node, &cln, &bitcoind, &electrs, &Side::External).await; |
| 97 | + |
| 98 | + node.stop().unwrap(); |
| 99 | +} |
| 100 | + |
| 101 | +#[tokio::test(flavor = "multi_thread", worker_threads = 1)] |
| 102 | +async fn test_cln_force_close_by_ldk() { |
| 103 | + let (bitcoind, electrs, cln) = setup_clients(); |
| 104 | + cln.wait_for_sync().await; |
| 105 | + common::generate_blocks_and_wait(&bitcoind, &electrs, 1).await; |
| 106 | + |
| 107 | + let node = setup_ldk_node(); |
| 108 | + setup_interop_test(&node, &cln, &bitcoind, &electrs).await; |
| 109 | + let (user_ch, _ext_ch) = |
| 110 | + open_channel_to_external(&node, &cln, &bitcoind, &electrs, 1_000_000, Some(500_000_000)) |
| 111 | + .await; |
| 112 | + |
| 113 | + test_force_close_by_ldk(&node, &cln, &bitcoind, &electrs, &user_ch).await; |
126 | 114 | node.stop().unwrap(); |
127 | 115 | } |
| 116 | + |
| 117 | +#[tokio::test(flavor = "multi_thread", worker_threads = 1)] |
| 118 | +async fn test_cln_force_close_by_external() { |
| 119 | + let (bitcoind, electrs, cln) = setup_clients(); |
| 120 | + cln.wait_for_sync().await; |
| 121 | + common::generate_blocks_and_wait(&bitcoind, &electrs, 1).await; |
| 122 | + |
| 123 | + let node = setup_ldk_node(); |
| 124 | + setup_interop_test(&node, &cln, &bitcoind, &electrs).await; |
| 125 | + let (_user_ch, ext_ch) = |
| 126 | + open_channel_to_external(&node, &cln, &bitcoind, &electrs, 1_000_000, Some(500_000_000)) |
| 127 | + .await; |
| 128 | + |
| 129 | + test_force_close_by_external(&node, &cln, &bitcoind, &electrs, &ext_ch).await; |
| 130 | + node.stop().unwrap(); |
| 131 | +} |
| 132 | + |
| 133 | +proptest! { |
| 134 | + #![proptest_config(proptest::test_runner::Config::with_cases(8))] |
| 135 | + #[test] |
| 136 | + fn test_cln_interop_proptest( |
| 137 | + disconnect_phase in prop_oneof![ |
| 138 | + Just(Phase::ChannelOpen), |
| 139 | + Just(Phase::Payment), |
| 140 | + Just(Phase::Close), |
| 141 | + Just(Phase::Idle), |
| 142 | + ], |
| 143 | + disconnect_initiator in prop_oneof![Just(Side::Ldk), Just(Side::External)], |
| 144 | + close_type in prop_oneof![Just(CloseType::Cooperative), Just(CloseType::Force)], |
| 145 | + close_initiator in prop_oneof![Just(Side::Ldk), Just(Side::External)], |
| 146 | + payment_type in prop_oneof![Just(PayType::Bolt11), Just(PayType::Keysend)], |
| 147 | + ) { |
| 148 | + let rt = tokio::runtime::Builder::new_multi_thread() |
| 149 | + .enable_all() |
| 150 | + .build() |
| 151 | + .unwrap(); |
| 152 | + rt.block_on(async { |
| 153 | + let (bitcoind, electrs, cln) = setup_clients(); |
| 154 | + cln.wait_for_sync().await; |
| 155 | + common::generate_blocks_and_wait(&bitcoind, &electrs, 1).await; |
| 156 | + |
| 157 | + let node = setup_ldk_node(); |
| 158 | + run_interop_property_test( |
| 159 | + &node, &cln, &bitcoind, &electrs, |
| 160 | + disconnect_phase, disconnect_initiator, close_type, |
| 161 | + close_initiator, payment_type, |
| 162 | + ).await; |
| 163 | + node.stop().unwrap(); |
| 164 | + }); |
| 165 | + } |
| 166 | +} |
0 commit comments