|
| 1 | +#![cfg(cln_test)] |
| 2 | + |
| 3 | +use ldk_node::{Config, LogLevel}; |
| 4 | + |
| 5 | +use lightning::ln::msgs::SocketAddress; |
| 6 | + |
| 7 | +use bitcoin::{Address, Amount, Network, OutPoint, Txid}; |
| 8 | + |
| 9 | +use bitcoincore_rpc::bitcoincore_rpc_json::AddressType; |
| 10 | +use bitcoincore_rpc::Client as BitcoindClient; |
| 11 | +use bitcoincore_rpc::RpcApi; |
| 12 | + |
| 13 | +use electrum_client::Client as ElectrumClient; |
| 14 | +use electrum_client::ElectrumApi; |
| 15 | + |
| 16 | +use rand::distributions::Alphanumeric; |
| 17 | +use rand::{thread_rng, Rng}; |
| 18 | + |
| 19 | +use std::path::PathBuf; |
| 20 | +use std::time::Duration; |
| 21 | + |
| 22 | +macro_rules! expect_event { |
| 23 | + ($node: expr, $event_type: ident) => {{ |
| 24 | + match $node.wait_next_event() { |
| 25 | + ref e @ Event::$event_type { .. } => { |
| 26 | + println!("{} got event {:?}", $node.node_id(), e); |
| 27 | + $node.event_handled(); |
| 28 | + } |
| 29 | + ref e => { |
| 30 | + panic!("{} got unexpected event!: {:?}", std::stringify!($node), e); |
| 31 | + } |
| 32 | + } |
| 33 | + }}; |
| 34 | +} |
| 35 | + |
| 36 | +pub(crate) use expect_event; |
| 37 | + |
| 38 | +macro_rules! expect_channel_pending_event { |
| 39 | + ($node: expr, $counterparty_node_id: expr) => {{ |
| 40 | + match $node.wait_next_event() { |
| 41 | + ref e @ Event::ChannelPending { funding_txo, counterparty_node_id, .. } => { |
| 42 | + println!("{} got event {:?}", $node.node_id(), e); |
| 43 | + assert_eq!(counterparty_node_id, $counterparty_node_id); |
| 44 | + $node.event_handled(); |
| 45 | + funding_txo |
| 46 | + } |
| 47 | + ref e => { |
| 48 | + panic!("{} got unexpected event!: {:?}", std::stringify!($node), e); |
| 49 | + } |
| 50 | + } |
| 51 | + }}; |
| 52 | +} |
| 53 | + |
| 54 | +pub(crate) use expect_channel_pending_event; |
| 55 | + |
| 56 | +macro_rules! expect_channel_ready_event { |
| 57 | + ($node: expr, $counterparty_node_id: expr) => {{ |
| 58 | + match $node.wait_next_event() { |
| 59 | + ref e @ Event::ChannelReady { channel_id, counterparty_node_id, .. } => { |
| 60 | + println!("{} got event {:?}", $node.node_id(), e); |
| 61 | + assert_eq!(counterparty_node_id, Some($counterparty_node_id)); |
| 62 | + $node.event_handled(); |
| 63 | + channel_id |
| 64 | + } |
| 65 | + ref e => { |
| 66 | + panic!("{} got unexpected event!: {:?}", std::stringify!($node), e); |
| 67 | + } |
| 68 | + } |
| 69 | + }}; |
| 70 | +} |
| 71 | + |
| 72 | +pub(crate) use expect_channel_ready_event; |
| 73 | + |
| 74 | +pub(crate) fn random_storage_path() -> PathBuf { |
| 75 | + let mut temp_path = std::env::temp_dir(); |
| 76 | + let mut rng = thread_rng(); |
| 77 | + let rand_dir: String = (0..7).map(|_| rng.sample(Alphanumeric) as char).collect(); |
| 78 | + temp_path.push(rand_dir); |
| 79 | + temp_path |
| 80 | +} |
| 81 | + |
| 82 | +pub(crate) fn random_port() -> u16 { |
| 83 | + let mut rng = thread_rng(); |
| 84 | + rng.gen_range(5000..65535) |
| 85 | +} |
| 86 | + |
| 87 | +pub(crate) fn random_listening_addresses() -> Vec<SocketAddress> { |
| 88 | + let num_addresses = 2; |
| 89 | + let mut listening_addresses = Vec::with_capacity(num_addresses); |
| 90 | + |
| 91 | + for _ in 0..num_addresses { |
| 92 | + let rand_port = random_port(); |
| 93 | + let address: SocketAddress = format!("127.0.0.1:{}", rand_port).parse().unwrap(); |
| 94 | + listening_addresses.push(address); |
| 95 | + } |
| 96 | + |
| 97 | + listening_addresses |
| 98 | +} |
| 99 | + |
| 100 | +pub(crate) fn random_config() -> Config { |
| 101 | + let mut config = Config::default(); |
| 102 | + |
| 103 | + config.network = Network::Regtest; |
| 104 | + println!("Setting network: {}", config.network); |
| 105 | + |
| 106 | + let rand_dir = random_storage_path(); |
| 107 | + println!("Setting random LDK storage dir: {}", rand_dir.display()); |
| 108 | + config.storage_dir_path = rand_dir.to_str().unwrap().to_owned(); |
| 109 | + |
| 110 | + let rand_listening_addresses = random_listening_addresses(); |
| 111 | + println!("Setting random LDK listening addresses: {:?}", rand_listening_addresses); |
| 112 | + config.listening_addresses = Some(rand_listening_addresses); |
| 113 | + |
| 114 | + config.log_level = LogLevel::Gossip; |
| 115 | + |
| 116 | + config |
| 117 | +} |
| 118 | + |
| 119 | +pub(crate) fn generate_blocks_and_wait( |
| 120 | + bitcoind: &BitcoindClient, electrs: &ElectrumClient, num: usize, |
| 121 | +) { |
| 122 | + let _ = bitcoind.create_wallet("ldk_node_test", None, None, None, None); |
| 123 | + let _ = bitcoind.load_wallet("ldk_node_test"); |
| 124 | + print!("Generating {} blocks...", num); |
| 125 | + let cur_height = bitcoind.get_block_count().expect("failed to get current block height"); |
| 126 | + let address = bitcoind |
| 127 | + .get_new_address(Some("test"), Some(AddressType::Legacy)) |
| 128 | + .expect("failed to get new address"); |
| 129 | + // TODO: expect this Result once the WouldBlock issue is resolved upstream. |
| 130 | + let _block_hashes_res = bitcoind.generate_to_address(num as u64, &address); |
| 131 | + wait_for_block(electrs, cur_height as usize + num); |
| 132 | + print!(" Done!"); |
| 133 | + println!("\n"); |
| 134 | +} |
| 135 | + |
| 136 | +pub(crate) fn wait_for_block(electrs: &ElectrumClient, min_height: usize) { |
| 137 | + let mut header = match electrs.block_headers_subscribe() { |
| 138 | + Ok(header) => header, |
| 139 | + Err(_) => { |
| 140 | + // While subscribing should succeed the first time around, we ran into some cases where |
| 141 | + // it didn't. Since we can't proceed without subscribing, we try again after a delay |
| 142 | + // and panic if it still fails. |
| 143 | + std::thread::sleep(Duration::from_secs(1)); |
| 144 | + electrs.block_headers_subscribe().expect("failed to subscribe to block headers") |
| 145 | + } |
| 146 | + }; |
| 147 | + loop { |
| 148 | + if header.height >= min_height { |
| 149 | + break; |
| 150 | + } |
| 151 | + header = exponential_backoff_poll(|| { |
| 152 | + electrs.ping().expect("failed to ping electrs"); |
| 153 | + electrs.block_headers_pop().expect("failed to pop block header") |
| 154 | + }); |
| 155 | + } |
| 156 | +} |
| 157 | + |
| 158 | +pub(crate) fn wait_for_tx(electrs: &ElectrumClient, txid: Txid) { |
| 159 | + let mut tx_res = electrs.transaction_get(&txid); |
| 160 | + loop { |
| 161 | + if tx_res.is_ok() { |
| 162 | + break; |
| 163 | + } |
| 164 | + tx_res = exponential_backoff_poll(|| { |
| 165 | + electrs.ping().unwrap(); |
| 166 | + Some(electrs.transaction_get(&txid)) |
| 167 | + }); |
| 168 | + } |
| 169 | +} |
| 170 | + |
| 171 | +pub(crate) fn wait_for_outpoint_spend(electrs: &ElectrumClient, outpoint: OutPoint) { |
| 172 | + let tx = electrs.transaction_get(&outpoint.txid).unwrap(); |
| 173 | + let txout_script = tx.output.get(outpoint.vout as usize).unwrap().clone().script_pubkey; |
| 174 | + let mut is_spent = !electrs.script_get_history(&txout_script).unwrap().is_empty(); |
| 175 | + loop { |
| 176 | + if is_spent { |
| 177 | + break; |
| 178 | + } |
| 179 | + |
| 180 | + is_spent = exponential_backoff_poll(|| { |
| 181 | + electrs.ping().unwrap(); |
| 182 | + Some(!electrs.script_get_history(&txout_script).unwrap().is_empty()) |
| 183 | + }); |
| 184 | + } |
| 185 | +} |
| 186 | + |
| 187 | +pub(crate) fn exponential_backoff_poll<T, F>(mut poll: F) -> T |
| 188 | +where |
| 189 | + F: FnMut() -> Option<T>, |
| 190 | +{ |
| 191 | + let mut delay = Duration::from_millis(64); |
| 192 | + let mut tries = 0; |
| 193 | + loop { |
| 194 | + match poll() { |
| 195 | + Some(data) => break data, |
| 196 | + None if delay.as_millis() < 512 => { |
| 197 | + delay = delay.mul_f32(2.0); |
| 198 | + } |
| 199 | + |
| 200 | + None => {} |
| 201 | + } |
| 202 | + assert!(tries < 20, "Reached max tries."); |
| 203 | + tries += 1; |
| 204 | + std::thread::sleep(delay); |
| 205 | + } |
| 206 | +} |
| 207 | + |
| 208 | +pub(crate) fn premine_and_distribute_funds( |
| 209 | + bitcoind: &BitcoindClient, electrs: &ElectrumClient, addrs: Vec<Address>, amount: Amount, |
| 210 | +) { |
| 211 | + let _ = bitcoind.create_wallet("ldk_node_test", None, None, None, None); |
| 212 | + let _ = bitcoind.load_wallet("ldk_node_test"); |
| 213 | + generate_blocks_and_wait(bitcoind, electrs, 101); |
| 214 | + |
| 215 | + for addr in addrs { |
| 216 | + let txid = |
| 217 | + bitcoind.send_to_address(&addr, amount, None, None, None, None, None, None).unwrap(); |
| 218 | + wait_for_tx(electrs, txid); |
| 219 | + } |
| 220 | + |
| 221 | + generate_blocks_and_wait(bitcoind, electrs, 1); |
| 222 | +} |
0 commit comments