Make `test_esplora_syncs` more robust
[rust-lightning] / lightning-transaction-sync / tests / integration_tests.rs
1 #![cfg(any(feature = "esplora-blocking", feature = "esplora-async"))]
2 use lightning_transaction_sync::EsploraSyncClient;
3 use lightning::chain::{Confirm, Filter};
4 use lightning::chain::transaction::TransactionData;
5 use lightning::util::logger::{Logger, Record};
6
7 use electrsd::{bitcoind, bitcoind::BitcoinD, ElectrsD};
8 use bitcoin::{Amount, Txid, BlockHash, BlockHeader};
9 use bitcoin::blockdata::constants::genesis_block;
10 use bitcoin::network::constants::Network;
11 use electrsd::bitcoind::bitcoincore_rpc::bitcoincore_rpc_json::AddressType;
12 use bitcoind::bitcoincore_rpc::RpcApi;
13 use electrum_client::ElectrumApi;
14
15 use once_cell::sync::OnceCell;
16
17 use std::env;
18 use std::sync::Mutex;
19 use std::time::Duration;
20 use std::collections::{HashMap, HashSet};
21
22 static BITCOIND: OnceCell<BitcoinD> = OnceCell::new();
23 static ELECTRSD: OnceCell<ElectrsD> = OnceCell::new();
24 static PREMINE: OnceCell<()> = OnceCell::new();
25 static MINER_LOCK: OnceCell<Mutex<()>> = OnceCell::new();
26
27 fn get_bitcoind() -> &'static BitcoinD {
28         BITCOIND.get_or_init(|| {
29                 let bitcoind_exe =
30                         env::var("BITCOIND_EXE").ok().or_else(|| bitcoind::downloaded_exe_path().ok()).expect(
31                                 "you need to provide an env var BITCOIND_EXE or specify a bitcoind version feature",
32                                 );
33                 let mut conf = bitcoind::Conf::default();
34                 conf.network = "regtest";
35                 let bitcoind = BitcoinD::with_conf(bitcoind_exe, &conf).unwrap();
36                 std::thread::sleep(Duration::from_secs(1));
37                 bitcoind
38         })
39 }
40
41 fn get_electrsd() -> &'static ElectrsD {
42         ELECTRSD.get_or_init(|| {
43                 let bitcoind = get_bitcoind();
44                 let electrs_exe =
45                         env::var("ELECTRS_EXE").ok().or_else(electrsd::downloaded_exe_path).expect(
46                                 "you need to provide env var ELECTRS_EXE or specify an electrsd version feature",
47                         );
48                 let mut conf = electrsd::Conf::default();
49                 conf.http_enabled = true;
50                 conf.network = "regtest";
51                 let electrsd = ElectrsD::with_conf(electrs_exe, &bitcoind, &conf).unwrap();
52                 std::thread::sleep(Duration::from_secs(1));
53                 electrsd
54         })
55 }
56
57 fn generate_blocks_and_wait(num: usize) {
58         let miner_lock = MINER_LOCK.get_or_init(|| Mutex::new(()));
59         let _miner = miner_lock.lock().unwrap();
60         let cur_height = get_bitcoind().client.get_block_count().expect("failed to get current block height");
61         let address = get_bitcoind().client.get_new_address(Some("test"), Some(AddressType::Legacy)).expect("failed to get new address");
62         // TODO: expect this Result once the WouldBlock issue is resolved upstream.
63         let _block_hashes_res = get_bitcoind().client.generate_to_address(num as u64, &address);
64         wait_for_block(cur_height as usize + num);
65 }
66
67 fn wait_for_block(min_height: usize) {
68         let mut header = match get_electrsd().client.block_headers_subscribe() {
69                 Ok(header) => header,
70                 Err(_) => {
71                         // While subscribing should succeed the first time around, we ran into some cases where
72                         // it didn't. Since we can't proceed without subscribing, we try again after a delay
73                         // and panic if it still fails.
74                         std::thread::sleep(Duration::from_secs(1));
75                         get_electrsd().client.block_headers_subscribe().expect("failed to subscribe to block headers")
76                 }
77         };
78
79         loop {
80                 if header.height >= min_height {
81                         break;
82                 }
83                 header = exponential_backoff_poll(|| {
84                         get_electrsd().trigger().expect("failed to trigger electrsd");
85                         get_electrsd().client.ping().expect("failed to ping electrsd");
86                         get_electrsd().client.block_headers_pop().expect("failed to pop block header")
87                 });
88         }
89 }
90
91 fn exponential_backoff_poll<T, F>(mut poll: F) -> T
92 where
93         F: FnMut() -> Option<T>,
94 {
95         let mut delay = Duration::from_millis(64);
96         let mut tries = 0;
97         loop {
98                 match poll() {
99                         Some(data) => break data,
100                         None if delay.as_millis() < 512 => {
101                                 delay = delay.mul_f32(2.0);
102                                 tries += 1;
103                         }
104                         None if tries == 10 => panic!("Exceeded our maximum wait time."),
105                         None => tries += 1,
106                 }
107
108                 std::thread::sleep(delay);
109         }
110 }
111
112 fn premine() {
113         PREMINE.get_or_init(|| {
114                 generate_blocks_and_wait(101);
115         });
116 }
117
118 #[derive(Debug)]
119 enum TestConfirmableEvent {
120         Confirmed(Txid, BlockHash, u32),
121         Unconfirmed(Txid),
122         BestBlockUpdated(BlockHash, u32),
123 }
124
125 struct TestConfirmable {
126         pub confirmed_txs: Mutex<HashMap<Txid, (BlockHash, u32)>>,
127         pub unconfirmed_txs: Mutex<HashSet<Txid>>,
128         pub best_block: Mutex<(BlockHash, u32)>,
129         pub events: Mutex<Vec<TestConfirmableEvent>>,
130 }
131
132 impl TestConfirmable {
133         pub fn new() -> Self {
134                 let genesis_hash = genesis_block(Network::Regtest).block_hash();
135                 Self {
136                         confirmed_txs: Mutex::new(HashMap::new()),
137                         unconfirmed_txs: Mutex::new(HashSet::new()),
138                         best_block: Mutex::new((genesis_hash, 0)),
139                         events: Mutex::new(Vec::new()),
140                 }
141         }
142 }
143
144 impl Confirm for TestConfirmable {
145         fn transactions_confirmed(&self, header: &BlockHeader, txdata: &TransactionData<'_>, height: u32) {
146                 for (_, tx) in txdata {
147                         let txid = tx.txid();
148                         let block_hash = header.block_hash();
149                         self.confirmed_txs.lock().unwrap().insert(txid, (block_hash, height));
150                         self.unconfirmed_txs.lock().unwrap().remove(&txid);
151                         self.events.lock().unwrap().push(TestConfirmableEvent::Confirmed(txid, block_hash, height));
152                 }
153         }
154
155         fn transaction_unconfirmed(&self, txid: &Txid) {
156                 self.unconfirmed_txs.lock().unwrap().insert(*txid);
157                 self.confirmed_txs.lock().unwrap().remove(txid);
158                 self.events.lock().unwrap().push(TestConfirmableEvent::Unconfirmed(*txid));
159         }
160
161         fn best_block_updated(&self, header: &BlockHeader, height: u32) {
162                 let block_hash = header.block_hash();
163                 *self.best_block.lock().unwrap() = (block_hash, height);
164                 self.events.lock().unwrap().push(TestConfirmableEvent::BestBlockUpdated(block_hash, height));
165         }
166
167         fn get_relevant_txids(&self) -> Vec<(Txid, Option<BlockHash>)> {
168                 self.confirmed_txs.lock().unwrap().iter().map(|(&txid, (hash, _))| (txid, Some(*hash))).collect::<Vec<_>>()
169         }
170 }
171
172 pub struct TestLogger {}
173
174 impl Logger for TestLogger {
175         fn log(&self, record: &Record) {
176                 println!("{} -- {}",
177                                 record.level,
178                                 record.args);
179         }
180 }
181
182 #[test]
183 #[cfg(feature = "esplora-blocking")]
184 fn test_esplora_syncs() {
185         premine();
186         let mut logger = TestLogger {};
187         let esplora_url = format!("http://{}", get_electrsd().esplora_url.as_ref().unwrap());
188         let tx_sync = EsploraSyncClient::new(esplora_url, &mut logger);
189         let confirmable = TestConfirmable::new();
190
191         // Check we pick up on new best blocks
192         let expected_height = 0u32;
193         assert_eq!(confirmable.best_block.lock().unwrap().1, expected_height);
194
195         tx_sync.sync(vec![&confirmable]).unwrap();
196
197         let expected_height = get_bitcoind().client.get_block_count().unwrap() as u32;
198         assert_eq!(confirmable.best_block.lock().unwrap().1, expected_height);
199
200         let events = std::mem::take(&mut *confirmable.events.lock().unwrap());
201         assert_eq!(events.len(), 1);
202
203         // Check registered confirmed transactions are marked confirmed
204         let new_address = get_bitcoind().client.get_new_address(Some("test"), Some(AddressType::Legacy)).unwrap();
205         let txid = get_bitcoind().client.send_to_address(&new_address, Amount::from_sat(5000), None, None, None, None, None, None).unwrap();
206         tx_sync.register_tx(&txid, &new_address.script_pubkey());
207
208         tx_sync.sync(vec![&confirmable]).unwrap();
209
210         let events = std::mem::take(&mut *confirmable.events.lock().unwrap());
211         assert_eq!(events.len(), 0);
212         assert!(confirmable.confirmed_txs.lock().unwrap().is_empty());
213         assert!(confirmable.unconfirmed_txs.lock().unwrap().is_empty());
214
215         generate_blocks_and_wait(1);
216         tx_sync.sync(vec![&confirmable]).unwrap();
217
218         let events = std::mem::take(&mut *confirmable.events.lock().unwrap());
219         assert_eq!(events.len(), 2);
220         assert!(confirmable.confirmed_txs.lock().unwrap().contains_key(&txid));
221         assert!(confirmable.unconfirmed_txs.lock().unwrap().is_empty());
222
223         // Check previously confirmed transactions are marked unconfirmed when they are reorged.
224         let best_block_hash = get_bitcoind().client.get_best_block_hash().unwrap();
225         get_bitcoind().client.invalidate_block(&best_block_hash).unwrap();
226
227         // We're getting back to the previous height with a new tip, but best block shouldn't change.
228         generate_blocks_and_wait(1);
229         assert_ne!(get_bitcoind().client.get_best_block_hash().unwrap(), best_block_hash);
230         tx_sync.sync(vec![&confirmable]).unwrap();
231         let events = std::mem::take(&mut *confirmable.events.lock().unwrap());
232         assert_eq!(events.len(), 0);
233
234         // Now we're surpassing previous height, getting new tip.
235         generate_blocks_and_wait(1);
236         assert_ne!(get_bitcoind().client.get_best_block_hash().unwrap(), best_block_hash);
237         tx_sync.sync(vec![&confirmable]).unwrap();
238
239         // Transaction still confirmed but under new tip.
240         assert!(confirmable.confirmed_txs.lock().unwrap().contains_key(&txid));
241         assert!(confirmable.unconfirmed_txs.lock().unwrap().is_empty());
242
243         // Check we got unconfirmed, then reconfirmed in the meantime.
244         let events = std::mem::take(&mut *confirmable.events.lock().unwrap());
245         assert_eq!(events.len(), 3);
246
247         match events[0] {
248                 TestConfirmableEvent::Unconfirmed(t) => {
249                         assert_eq!(t, txid);
250                 },
251                 _ => panic!("Unexpected event"),
252         }
253
254         match events[1] {
255                 TestConfirmableEvent::BestBlockUpdated(..) => {},
256                 _ => panic!("Unexpected event"),
257         }
258
259         match events[2] {
260                 TestConfirmableEvent::Confirmed(t, _, _) => {
261                         assert_eq!(t, txid);
262                 },
263                 _ => panic!("Unexpected event"),
264         }
265 }
266
267 #[tokio::test]
268 #[cfg(feature = "esplora-async")]
269 async fn test_esplora_syncs() {
270         premine();
271         let mut logger = TestLogger {};
272         let esplora_url = format!("http://{}", get_electrsd().esplora_url.as_ref().unwrap());
273         let tx_sync = EsploraSyncClient::new(esplora_url, &mut logger);
274         let confirmable = TestConfirmable::new();
275
276         // Check we pick up on new best blocks
277         let expected_height = 0u32;
278         assert_eq!(confirmable.best_block.lock().unwrap().1, expected_height);
279
280         tx_sync.sync(vec![&confirmable]).await.unwrap();
281
282         let expected_height = get_bitcoind().client.get_block_count().unwrap() as u32;
283         assert_eq!(confirmable.best_block.lock().unwrap().1, expected_height);
284
285         let events = std::mem::take(&mut *confirmable.events.lock().unwrap());
286         assert_eq!(events.len(), 1);
287
288         // Check registered confirmed transactions are marked confirmed
289         let new_address = get_bitcoind().client.get_new_address(Some("test"), Some(AddressType::Legacy)).unwrap();
290         let txid = get_bitcoind().client.send_to_address(&new_address, Amount::from_sat(5000), None, None, None, None, None, None).unwrap();
291         tx_sync.register_tx(&txid, &new_address.script_pubkey());
292
293         tx_sync.sync(vec![&confirmable]).await.unwrap();
294
295         let events = std::mem::take(&mut *confirmable.events.lock().unwrap());
296         assert_eq!(events.len(), 0);
297         assert!(confirmable.confirmed_txs.lock().unwrap().is_empty());
298         assert!(confirmable.unconfirmed_txs.lock().unwrap().is_empty());
299
300         generate_blocks_and_wait(1);
301         tx_sync.sync(vec![&confirmable]).await.unwrap();
302
303         let events = std::mem::take(&mut *confirmable.events.lock().unwrap());
304         assert_eq!(events.len(), 2);
305         assert!(confirmable.confirmed_txs.lock().unwrap().contains_key(&txid));
306         assert!(confirmable.unconfirmed_txs.lock().unwrap().is_empty());
307
308         // Check previously confirmed transactions are marked unconfirmed when they are reorged.
309         let best_block_hash = get_bitcoind().client.get_best_block_hash().unwrap();
310         get_bitcoind().client.invalidate_block(&best_block_hash).unwrap();
311
312         // We're getting back to the previous height with a new tip, but best block shouldn't change.
313         generate_blocks_and_wait(1);
314         assert_ne!(get_bitcoind().client.get_best_block_hash().unwrap(), best_block_hash);
315         tx_sync.sync(vec![&confirmable]).await.unwrap();
316         let events = std::mem::take(&mut *confirmable.events.lock().unwrap());
317         assert_eq!(events.len(), 0);
318
319         // Now we're surpassing previous height, getting new tip.
320         generate_blocks_and_wait(1);
321         assert_ne!(get_bitcoind().client.get_best_block_hash().unwrap(), best_block_hash);
322         tx_sync.sync(vec![&confirmable]).await.unwrap();
323
324         // Transaction still confirmed but under new tip.
325         assert!(confirmable.confirmed_txs.lock().unwrap().contains_key(&txid));
326         assert!(confirmable.unconfirmed_txs.lock().unwrap().is_empty());
327
328         // Check we got unconfirmed, then reconfirmed in the meantime.
329         let events = std::mem::take(&mut *confirmable.events.lock().unwrap());
330         assert_eq!(events.len(), 3);
331
332         match events[0] {
333                 TestConfirmableEvent::Unconfirmed(t) => {
334                         assert_eq!(t, txid);
335                 },
336                 _ => panic!("Unexpected event"),
337         }
338
339         match events[1] {
340                 TestConfirmableEvent::BestBlockUpdated(..) => {},
341                 _ => panic!("Unexpected event"),
342         }
343
344         match events[2] {
345                 TestConfirmableEvent::Confirmed(t, _, _) => {
346                         assert_eq!(t, txid);
347                 },
348                 _ => panic!("Unexpected event"),
349         }
350 }