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};
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;
15 use once_cell::sync::OnceCell;
19 use std::time::Duration;
20 use std::collections::{HashMap, HashSet};
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();
27 fn get_bitcoind() -> &'static BitcoinD {
28 BITCOIND.get_or_init(|| {
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",
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));
41 fn get_electrsd() -> &'static ElectrsD {
42 ELECTRSD.get_or_init(|| {
43 let bitcoind = get_bitcoind();
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",
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));
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);
67 fn wait_for_block(min_height: usize) {
68 let mut header = match get_electrsd().client.block_headers_subscribe() {
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")
80 if header.height >= min_height {
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")
91 fn exponential_backoff_poll<T, F>(mut poll: F) -> T
93 F: FnMut() -> Option<T>,
95 let mut delay = Duration::from_millis(64);
99 Some(data) => break data,
100 None if delay.as_millis() < 512 => {
101 delay = delay.mul_f32(2.0);
104 None if tries == 10 => panic!("Exceeded our maximum wait time."),
108 std::thread::sleep(delay);
113 PREMINE.get_or_init(|| {
114 generate_blocks_and_wait(101);
119 enum TestConfirmableEvent {
120 Confirmed(Txid, BlockHash, u32),
122 BestBlockUpdated(BlockHash, u32),
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>>,
132 impl TestConfirmable {
133 pub fn new() -> Self {
134 let genesis_hash = genesis_block(Network::Regtest).block_hash();
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()),
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));
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));
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));
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<_>>()
172 pub struct TestLogger {}
174 impl Logger for TestLogger {
175 fn log(&self, record: &Record) {
183 #[cfg(feature = "esplora-blocking")]
184 fn test_esplora_syncs() {
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();
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);
195 tx_sync.sync(vec![&confirmable]).unwrap();
197 let expected_height = get_bitcoind().client.get_block_count().unwrap() as u32;
198 assert_eq!(confirmable.best_block.lock().unwrap().1, expected_height);
200 let events = std::mem::take(&mut *confirmable.events.lock().unwrap());
201 assert_eq!(events.len(), 1);
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());
208 tx_sync.sync(vec![&confirmable]).unwrap();
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());
215 generate_blocks_and_wait(1);
216 tx_sync.sync(vec![&confirmable]).unwrap();
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());
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();
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);
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();
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());
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);
248 TestConfirmableEvent::Unconfirmed(t) => {
251 _ => panic!("Unexpected event"),
255 TestConfirmableEvent::BestBlockUpdated(..) => {},
256 _ => panic!("Unexpected event"),
260 TestConfirmableEvent::Confirmed(t, _, _) => {
263 _ => panic!("Unexpected event"),
268 #[cfg(feature = "esplora-async")]
269 async fn test_esplora_syncs() {
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();
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);
280 tx_sync.sync(vec![&confirmable]).await.unwrap();
282 let expected_height = get_bitcoind().client.get_block_count().unwrap() as u32;
283 assert_eq!(confirmable.best_block.lock().unwrap().1, expected_height);
285 let events = std::mem::take(&mut *confirmable.events.lock().unwrap());
286 assert_eq!(events.len(), 1);
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());
293 tx_sync.sync(vec![&confirmable]).await.unwrap();
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());
300 generate_blocks_and_wait(1);
301 tx_sync.sync(vec![&confirmable]).await.unwrap();
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());
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();
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);
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();
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());
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);
333 TestConfirmableEvent::Unconfirmed(t) => {
336 _ => panic!("Unexpected event"),
340 TestConfirmableEvent::BestBlockUpdated(..) => {},
341 _ => panic!("Unexpected event"),
345 TestConfirmableEvent::Confirmed(t, _, _) => {
348 _ => panic!("Unexpected event"),