Add subcrate which impls a simple SPV client from Bitcoin Core RPC
[rust-lightning] / lightning-block-sync / src / lib.rs
1 //! An implementation of a simple SPV client which can interrogate abstract block sources to keep
2 //! lightning objects on the best chain.
3 //!
4 //! With feature `rpc-client` we provide a client which can fetch blocks from Bitcoin Core's RPC
5 //! interface.
6 //!
7 //! With feature `rest-client` we provide a client which can fetch blocks from Bitcoin Core's REST
8 //! interface.
9 //!
10 //! Both provided clients support either blocking TCP reads from std::net::TcpStream or, with
11 //! feature `tokio`, tokio::net::TcpStream inside a Tokio runtime.
12
13 #[cfg(any(feature = "rest-client", feature = "rpc-client"))]
14 mod utils;
15
16 #[cfg(any(feature = "rest-client", feature = "rpc-client"))]
17 pub mod http_clients;
18
19 use lightning::chain::{chaininterface, keysinterface};
20 use lightning::chain::chaininterface::{BlockNotifierArc, ChainListener};
21 use lightning::ln::channelmonitor::{ChannelMonitor, ManyChannelMonitor};
22 use lightning::ln::channelmanager::SimpleArcChannelManager;
23
24 use bitcoin::hashes::hex::ToHex;
25
26 use bitcoin::blockdata::block::{Block, BlockHeader};
27 use bitcoin::util::hash::BitcoinHash;
28 use bitcoin::util::uint::Uint256;
29 use bitcoin::hash_types::BlockHash;
30
31 use std::future::Future;
32 use std::vec::Vec;
33 use std::pin::Pin;
34 use std::ops::DerefMut;
35
36 #[derive(Clone, Debug, PartialEq)]
37 /// A block header and some associated data. This information should be available from most block
38 /// sources (and, notably, is available in Bitcoin Core's RPC and REST interfaces).
39 pub struct BlockHeaderData {
40         /// The total chain work, in expected number of double-SHA256 hashes required to build a chain
41         /// of equivalent weight
42         pub chainwork: Uint256,
43         /// The block height, with the genesis block heigh set to 0
44         pub height: u32,
45         /// The block header itself
46         pub header: BlockHeader
47 }
48
49 #[derive(Debug, Clone)]
50 /// Failure type for requests to block sources.
51 pub enum BlockSourceRespErr {
52         /// Indicates a BlockSource provided bogus data. After this is returned once we will never
53         /// bother polling the returning BlockSource for block data again, so use it sparingly.
54         BogusData,
55         /// Indicates the BlockSource isn't responsive or may be misconfigured but we want to continue
56         /// polling it.
57         NoResponse,
58 }
59 /// Abstract type for a source of block header and block data.
60 pub trait BlockSource : Sync + Send {
61         /// Gets the header for a given hash. The height the header should be at is provided, though
62         /// note that you must return either the header with the requested hash, or an Err, not a
63         /// different header with the same eight.
64         ///
65         /// For sources which cannot find headers based on the hash, returning NoResponse when
66         /// height_hint is None is fine, though get_best_block() should never return a None for height
67         /// on the same source. Such a source should never be used in init_sync_chain_monitor as it
68         /// doesn't have any initial height information.
69         ///
70         /// Sadly rust's trait system hasn't grown the ability to take impl/differentially-sized return
71         /// values yet, so we have to Box + dyn the future.
72         fn get_header<'a>(&'a mut self, header_hash: &'a BlockHash, height_hint: Option<u32>) -> Pin<Box<dyn Future<Output = Result<BlockHeaderData, BlockSourceRespErr>> + 'a + Send>>;
73
74         /// Gets the block for a given hash. BlockSources may be headers-only, in which case they
75         /// should always return Err(BlockSourceRespErr::NoResponse) here.
76         /// Sadly rust's trait system hasn't grown the ability to take impl/differentially-sized return
77         /// values yet, so we have to Box + dyn the future.
78         fn get_block<'a>(&'a mut self, header_hash: &'a BlockHash) -> Pin<Box<dyn Future<Output = Result<Block, BlockSourceRespErr>> + 'a + Send>>;
79
80         /// Gets the best block hash and, optionally, its height.
81         /// Including the height doesn't impact the chain-scannling algorithm, but it is passed to
82         /// get_header() which may allow some BlockSources to more effeciently find the target header.
83         ///
84         /// Sadly rust's trait system hasn't grown the ability to take impl/differentially-sized return
85         /// values yet, so we have to Box + dyn the future.
86         fn get_best_block<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = Result<(BlockHash, Option<u32>), BlockSourceRespErr>> + 'a + Send>>;
87 }
88
89 /// Stateless header checks on a given header.
90 #[inline]
91 fn stateless_check_header(header: &BlockHeader) -> Result<(), BlockSourceRespErr> {
92         if header.validate_pow(&header.target()).is_err() {
93                 Err(BlockSourceRespErr::BogusData)
94         } else { Ok(()) }
95 }
96
97 /// Check that child_header correctly builds on previous_header - the claimed work differential
98 /// matches the actual PoW in child_header and the difficulty transition is possible, ie within 4x.
99 /// Includes stateless header checks on previous_header.
100 fn check_builds_on(child_header: &BlockHeaderData, previous_header: &BlockHeaderData, mainnet: bool) -> Result<(), BlockSourceRespErr> {
101         if child_header.header.prev_blockhash != previous_header.header.bitcoin_hash() {
102                 return Err(BlockSourceRespErr::BogusData);
103         }
104
105         stateless_check_header(&previous_header.header)?;
106         let new_work = child_header.header.work();
107         if previous_header.height != child_header.height - 1 ||
108                         previous_header.chainwork + new_work != child_header.chainwork {
109                 return Err(BlockSourceRespErr::BogusData);
110         }
111         if mainnet {
112                 if child_header.height % 2016 == 0 {
113                         let prev_work = previous_header.header.work();
114                         if new_work > prev_work << 2 || new_work < prev_work >> 2 {
115                                 return Err(BlockSourceRespErr::BogusData)
116                         }
117                 } else if child_header.header.bits != previous_header.header.bits {
118                         return Err(BlockSourceRespErr::BogusData)
119                 }
120         }
121         Ok(())
122 }
123
124 enum ForkStep {
125         ForkPoint(BlockHeaderData),
126         DisconnectBlock(BlockHeaderData),
127         ConnectBlock(BlockHeaderData),
128 }
129 fn find_fork_step<'a>(steps_tx: &'a mut Vec<ForkStep>, current_header: BlockHeaderData, prev_header: &'a BlockHeaderData, block_source: &'a mut dyn BlockSource, head_blocks: &'a [BlockHeaderData], mainnet: bool) -> Pin<Box<dyn Future<Output=Result<(), BlockSourceRespErr>> + Send + 'a>> {
130         Box::pin(async move {
131                 if prev_header.header.prev_blockhash == current_header.header.prev_blockhash {
132                         // Found the fork, get the fork point header and we're done!
133                         steps_tx.push(ForkStep::DisconnectBlock(prev_header.clone()));
134                         steps_tx.push(ForkStep::ConnectBlock(current_header));
135                         if !head_blocks.is_empty() {
136                                 let new_prev_header = head_blocks.last().unwrap();
137                                 steps_tx.push(ForkStep::ForkPoint(new_prev_header.clone()));
138                         } else {
139                                 let new_prev_header = block_source.get_header(&prev_header.header.prev_blockhash, Some(prev_header.height - 1)).await?;
140                                 check_builds_on(&prev_header, &new_prev_header, mainnet)?;
141                                 steps_tx.push(ForkStep::ForkPoint(new_prev_header.clone()));
142                         }
143                 } else if current_header.height == 0 {
144                         // We're connect through genesis, we must be on a different chain!
145                         return Err(BlockSourceRespErr::BogusData);
146                 } else if prev_header.height < current_header.height {
147                         if prev_header.height + 1 == current_header.height &&
148                                         prev_header.header.bitcoin_hash() == current_header.header.prev_blockhash {
149                                 // Current header is the one above prev_header, we're done!
150                                 steps_tx.push(ForkStep::ConnectBlock(current_header));
151                         } else {
152                                 // Current is higher than the prev, walk current down by listing blocks we need to
153                                 // connect
154                                 let new_cur_header = block_source.get_header(&current_header.header.prev_blockhash, Some(current_header.height - 1)).await?;
155                                 check_builds_on(&current_header, &new_cur_header, mainnet)?;
156                                 steps_tx.push(ForkStep::ConnectBlock(current_header));
157                                 find_fork_step(steps_tx, new_cur_header, prev_header, block_source, head_blocks, mainnet).await?;
158                         }
159                 } else if prev_header.height > current_header.height {
160                         // Previous is higher, walk it back and recurse
161                         steps_tx.push(ForkStep::DisconnectBlock(prev_header.clone()));
162                         if !head_blocks.is_empty() {
163                                 let new_prev_header = head_blocks.last().unwrap();
164                                 let new_head_blocks = &head_blocks[..head_blocks.len() - 1];
165                                 find_fork_step(steps_tx, current_header, new_prev_header, block_source, new_head_blocks, mainnet).await?;
166                         } else {
167                                 let new_prev_header = block_source.get_header(&prev_header.header.prev_blockhash, Some(prev_header.height - 1)).await?;
168                                 check_builds_on(&prev_header, &new_prev_header, mainnet)?;
169                                 find_fork_step(steps_tx, current_header, &new_prev_header, block_source, head_blocks, mainnet).await?;
170                         }
171                 } else {
172                         // Target and current are at the same height, but we're not at fork yet, walk
173                         // both back and recurse
174                         let new_cur_header = block_source.get_header(&current_header.header.prev_blockhash, Some(current_header.height - 1)).await?;
175                         check_builds_on(&current_header, &new_cur_header, mainnet)?;
176                         steps_tx.push(ForkStep::ConnectBlock(current_header));
177                         steps_tx.push(ForkStep::DisconnectBlock(prev_header.clone()));
178                         if !head_blocks.is_empty() {
179                                 let new_prev_header = head_blocks.last().unwrap();
180                                 let new_head_blocks = &head_blocks[..head_blocks.len() - 1];
181                                 find_fork_step(steps_tx, new_cur_header, new_prev_header, block_source, new_head_blocks, mainnet).await?;
182                         } else {
183                                 let new_prev_header = block_source.get_header(&prev_header.header.prev_blockhash, Some(prev_header.height - 1)).await?;
184                                 check_builds_on(&prev_header, &new_prev_header, mainnet)?;
185                                 find_fork_step(steps_tx, new_cur_header, &new_prev_header, block_source, head_blocks, mainnet).await?;
186                         }
187                 }
188                 Ok(())
189         })
190 }
191 /// Walks backwards from current_header and prev_header finding the fork and sending ForkStep events
192 /// into the steps_tx Sender. There is no ordering guarantee between different ForkStep types, but
193 /// DisconnectBlock and ConnectBlock events are each in reverse, height-descending order.
194 async fn find_fork<'a>(current_header: BlockHeaderData, prev_header: &'a BlockHeaderData, block_source: &'a mut dyn BlockSource, mut head_blocks: &'a [BlockHeaderData], mainnet: bool) -> Result<Vec<ForkStep>, BlockSourceRespErr> {
195         let mut steps_tx = Vec::new();
196         if current_header.header == prev_header.header { return Ok(steps_tx); }
197
198         // If we have cached headers, they have to end with where we used to be
199         head_blocks = if !head_blocks.is_empty() {
200                 assert_eq!(head_blocks.last().unwrap(), prev_header);
201                 &head_blocks[..head_blocks.len() - 1]
202         } else { head_blocks };
203
204         find_fork_step(&mut steps_tx, current_header, &prev_header, block_source, head_blocks, mainnet).await?;
205         Ok(steps_tx)
206 }
207
208 /// A dummy trait for capturing an object which wants the chain to be replayed.
209 /// Implemented for lightning BlockNotifiers for general use, as well as
210 /// ChannelManagers and ChannelMonitors to allow for easy replaying of chain
211 /// data upon deserialization.
212 pub trait AChainListener {
213         fn a_block_connected(&mut self, block: &Block, height: u32);
214         fn a_block_disconnected(&mut self, header: &BlockHeader, height: u32);
215 }
216
217 impl AChainListener for &BlockNotifierArc {
218         fn a_block_connected(&mut self, block: &Block, height: u32) {
219                 self.block_connected(block, height);
220         }
221         fn a_block_disconnected(&mut self, header: &BlockHeader, height: u32) {
222                 self.block_disconnected(header, height);
223         }
224 }
225
226 impl<M, B, F> AChainListener for &SimpleArcChannelManager<M, B, F>
227                 where M: ManyChannelMonitor<keysinterface::InMemoryChannelKeys>,
228                       B: chaininterface::BroadcasterInterface, F: chaininterface::FeeEstimator {
229         fn a_block_connected(&mut self, block: &Block, height: u32) {
230                 let mut txn = Vec::with_capacity(block.txdata.len());
231                 let mut idxn = Vec::with_capacity(block.txdata.len());
232                 for (i, tx) in block.txdata.iter().enumerate() {
233                         txn.push(tx);
234                         idxn.push(i as u32);
235                 }
236                 self.block_connected(&block.header, height, &txn, &idxn);
237         }
238         fn a_block_disconnected(&mut self, header: &BlockHeader, height: u32) {
239                 self.block_disconnected(header, height);
240         }
241 }
242
243 impl<CS, B, F> AChainListener for (&mut ChannelMonitor<CS>, &B, &F)
244                 where CS: keysinterface::ChannelKeys,
245                       B: chaininterface::BroadcasterInterface, F: chaininterface::FeeEstimator {
246         fn a_block_connected(&mut self, block: &Block, height: u32) {
247                 let mut txn = Vec::with_capacity(block.txdata.len());
248                 for tx in block.txdata.iter() {
249                         txn.push(tx);
250                 }
251                 self.0.block_connected(&txn, height, &block.bitcoin_hash(), self.1, self.2);
252         }
253         fn a_block_disconnected(&mut self, header: &BlockHeader, height: u32) {
254                 self.0.block_disconnected(height, &header.bitcoin_hash(), self.1, self.2);
255         }
256 }
257
258 /// Finds the fork point between new_header and old_header, disconnecting blocks from old_header to
259 /// get to that point and then connecting blocks until we get to new_header.
260 ///
261 /// We validate the headers along the transition path, but don't fetch blocks until we've
262 /// disconnected to the fork point. Thus, we may return an Err() that includes where our tip ended
263 /// up which may not be new_header. Note that iff the returned Err has a BlockHeaderData, the
264 /// header transition from old_header to new_header is valid.
265 async fn sync_chain_monitor<CL : AChainListener + Sized>(new_header: BlockHeaderData, old_header: &BlockHeaderData, block_source: &mut dyn BlockSource, chain_notifier: &mut CL, head_blocks: &mut Vec<BlockHeaderData>, mainnet: bool)
266                 -> Result<(), (BlockSourceRespErr, Option<BlockHeaderData>)> {
267         let mut events = find_fork(new_header, old_header, block_source, &*head_blocks, mainnet).await.map_err(|e| (e, None))?;
268
269         let mut last_disconnect_tip = None;
270         let mut new_tip = None;
271         for event in events.iter() {
272                 match &event {
273                         &ForkStep::DisconnectBlock(ref header) => {
274                                 println!("Disconnecting block {}", header.header.bitcoin_hash());
275                                 if let Some(cached_head) = head_blocks.pop() {
276                                         assert_eq!(cached_head, *header);
277                                 }
278                                 chain_notifier.a_block_disconnected(&header.header, header.height);
279                                 last_disconnect_tip = Some(header.header.prev_blockhash);
280                         },
281                         &ForkStep::ForkPoint(ref header) => {
282                                 new_tip = Some(header.clone());
283                         },
284                         _ => {},
285                 }
286         }
287
288         // If we disconnected any blocks, we should have new tip data available, which should match our
289         // cached header data if it is available. If we didn't disconnect any blocks we shouldn't have
290         // set a ForkPoint as there is no fork.
291         assert_eq!(last_disconnect_tip.is_some(), new_tip.is_some());
292         if let &Some(ref tip_header) = &new_tip {
293                 if let Some(cached_head) = head_blocks.last() {
294                         assert_eq!(cached_head, tip_header);
295                 }
296                 debug_assert_eq!(tip_header.header.bitcoin_hash(), *last_disconnect_tip.as_ref().unwrap());
297         } else {
298                 // Set new_tip to indicate that we got a valid header chain we wanted to connect to, but
299                 // failed
300                 new_tip = Some(old_header.clone());
301         }
302
303         for event in events.drain(..).rev() {
304                 if let ForkStep::ConnectBlock(header_data) = event {
305                         let block = match block_source.get_block(&header_data.header.bitcoin_hash()).await {
306                                 Err(e) => return Err((e, new_tip)),
307                                 Ok(b) => b,
308                         };
309                         if block.header != header_data.header || !block.check_merkle_root() || !block.check_witness_commitment() {
310                                 return Err((BlockSourceRespErr::BogusData, new_tip));
311                         }
312                         println!("Connecting block {}", header_data.header.bitcoin_hash().to_hex());
313                         chain_notifier.a_block_connected(&block, header_data.height);
314                         head_blocks.push(header_data.clone());
315                         new_tip = Some(header_data);
316                 }
317         }
318         Ok(())
319 }
320
321 /// Do a one-time sync of a chain listener from a single *trusted* block source bringing its view
322 /// of the latest chain tip from old_block to new_block. This is useful on startup when you need
323 /// to bring each ChannelMonitor, as well as the overall ChannelManager, into sync with each other.
324 ///
325 /// Once you have them all at the same block, you should switch to using MicroSPVClient.
326 pub async fn init_sync_chain_monitor<CL : AChainListener + Sized, B: BlockSource>(new_block: BlockHash, old_block: BlockHash, block_source: &mut B, mut chain_notifier: CL) {
327         if &old_block[..] == &[0; 32] { return; }
328
329         let new_header = block_source.get_header(&new_block, None).await.unwrap();
330         assert_eq!(new_header.header.bitcoin_hash(), new_block);
331         stateless_check_header(&new_header.header).unwrap();
332         let old_header = block_source.get_header(&old_block, None).await.unwrap();
333         assert_eq!(old_header.header.bitcoin_hash(), old_block);
334         stateless_check_header(&old_header.header).unwrap();
335         sync_chain_monitor(new_header, &old_header, block_source, &mut chain_notifier, &mut Vec::new(), false).await.unwrap();
336 }
337
338 /// Keep the chain that a chain listener knows about up-to-date with the best chain from any of the
339 /// given block_sources.
340 ///
341 /// This implements a pretty bare-bones SPV client, checking all relevant commitments and finding
342 /// the heaviest chain, but not storing the full header chain, leading to some important
343 /// limitations.
344 ///
345 /// While we never check full difficulty transition logic, the mainnet option enables checking that
346 /// difficulty transitions only happen every two weeks and never shift difficulty more than 4x in
347 /// either direction, which is sufficient to prevent most minority hashrate attacks.
348 ///
349 /// We cache any headers which we connect until every block source is in agreement on the best tip.
350 /// This prevents one block source from being able to orphan us on a fork of its own creation by
351 /// not responding to requests for old headers on that fork. However, if one block source is
352 /// unreachable this may result in our memory usage growing in accordance with the chain.
353 pub struct MicroSPVClient<'a, B: DerefMut<Target=dyn BlockSource + 'a> + Sized + Sync + Send, CL : AChainListener + Sized> {
354         chain_tip: (BlockHash, BlockHeaderData),
355         block_sources: Vec<B>,
356         backup_block_sources: Vec<B>,
357         cur_blocks: Vec<Result<BlockHash, BlockSourceRespErr>>,
358         blocks_past_common_tip: Vec<BlockHeaderData>,
359         chain_notifier: CL,
360         mainnet: bool
361 }
362 impl<'a, B: DerefMut<Target=dyn BlockSource + 'a> + Sized + Sync + Send, CL : AChainListener + Sized> MicroSPVClient<'a, B, CL> {
363         /// Create a new MicroSPVClient with a set of block sources and a chain listener which will
364         /// receive updates of the new tip.
365         ///
366         /// We assume that at least one of the provided BlockSources can provide all neccessary headers
367         /// to disconnect from the given chain_tip back to its common ancestor with the best chain.
368         /// We assume that the height, hash, and chain work given in chain_tip are correct.
369         ///
370         /// `backup_block_sources` are never queried unless we learned, via some `block_sources` source
371         /// that there exists a better, valid header chain but we failed to fetch the blocks. This is
372         /// useful when you have a block source which is more censorship-resistant than others but
373         /// which only provides headers. In this case, we can use such source(s) to learn of a censorship
374         /// attack without giving up privacy by querying a privacy-losing block sources.
375         pub fn init(chain_tip: BlockHeaderData, block_sources: Vec<B>, backup_block_sources: Vec<B>, chain_notifier: CL, mainnet: bool) -> Self {
376                 let cur_blocks = vec![Err(BlockSourceRespErr::NoResponse); block_sources.len() + backup_block_sources.len()];
377                 let blocks_past_common_tip = Vec::new();
378                 Self {
379                         chain_tip: (chain_tip.header.bitcoin_hash(), chain_tip),
380                         block_sources, backup_block_sources, cur_blocks, blocks_past_common_tip, chain_notifier, mainnet
381                 }
382         }
383         /// Check each source for a new best tip and update the chain listener accordingly.
384         /// Returns true if some blocks were [dis]connected, false otherwise.
385         pub async fn poll_best_tip(&mut self) -> bool {
386                 let mut highest_valid_tip = self.chain_tip.1.chainwork;
387                 let mut blocks_connected = false;
388
389                 macro_rules! process_source {
390                         ($cur_hash: expr, $source: expr) => { {
391                                 if let Err(BlockSourceRespErr::BogusData) = $cur_hash {
392                                         // We gave up on this provider, move on.
393                                         continue;
394                                 }
395                                 macro_rules! handle_err {
396                                         ($err: expr) => {
397                                                 match $err {
398                                                         Ok(r) => r,
399                                                         Err(BlockSourceRespErr::BogusData) => {
400                                                                 $cur_hash = Err(BlockSourceRespErr::BogusData);
401                                                                 continue;
402                                                         },
403                                                         Err(BlockSourceRespErr::NoResponse) => {
404                                                                 continue;
405                                                         },
406                                                 }
407                                         }
408                                 }
409                                 let (new_hash, height_opt) = handle_err!($source.get_best_block().await);
410                                 if new_hash == self.chain_tip.0 {
411                                         $cur_hash = Ok(new_hash);
412                                         continue;
413                                 }
414                                 let new_header = handle_err!($source.get_header(&new_hash, height_opt).await);
415                                 if new_header.header.bitcoin_hash() != new_hash {
416                                         $cur_hash = Err(BlockSourceRespErr::BogusData);
417                                         continue;
418                                 }
419                                 handle_err!(stateless_check_header(&new_header.header));
420                                 if new_header.chainwork <= self.chain_tip.1.chainwork {
421                                         $cur_hash = Ok(new_hash);
422                                         continue;
423                                 }
424
425                                 let syncres = sync_chain_monitor(new_header.clone(), &self.chain_tip.1, &mut *$source, &mut self.chain_notifier, &mut self.blocks_past_common_tip, self.mainnet).await;
426                                 if let Err((e, new_tip)) = syncres {
427                                         if let Some(tip) = new_tip {
428                                                 let tiphash = tip.header.bitcoin_hash();
429                                                 if tiphash != self.chain_tip.0 {
430                                                         self.chain_tip = (tiphash, tip);
431                                                         blocks_connected = true;
432                                                 }
433                                                 // We set cur_hash to where we got to since we don't mind dropping our
434                                                 // block header cache if its on a fork that no block sources care about,
435                                                 // but we (may) want to continue trying to get the blocks from this source
436                                                 // the next time we poll.
437                                                 $cur_hash = Ok(tiphash);
438                                                 highest_valid_tip = std::cmp::max(highest_valid_tip, new_header.chainwork);
439                                         }
440                                         handle_err!(Err(e));
441                                 } else {
442                                         highest_valid_tip = std::cmp::max(highest_valid_tip, new_header.chainwork);
443                                         self.chain_tip = (new_hash, new_header);
444                                         $cur_hash = Ok(new_hash);
445                                         blocks_connected = true;
446                                 }
447                         } }
448                 }
449
450                 for (cur_hash, source) in self.cur_blocks.iter_mut().take(self.block_sources.len())
451                                 .zip(self.block_sources.iter_mut()) {
452                         process_source!(*cur_hash, *source);
453                 }
454
455                 if highest_valid_tip != self.chain_tip.1.chainwork {
456                         for (cur_hash, source) in self.cur_blocks.iter_mut().skip(self.block_sources.len())
457                                         .zip(self.backup_block_sources.iter_mut()) {
458                                 process_source!(*cur_hash, *source);
459                                 if highest_valid_tip == self.chain_tip.1.chainwork { break; }
460                         }
461                 }
462
463                 let mut common_tip = true;
464                 for cur_hash in self.cur_blocks.iter() {
465                         if let Ok(hash) = cur_hash {
466                                 if *hash != self.chain_tip.0 {
467                                         common_tip = false;
468                                         break;
469                                 }
470                         }
471                 }
472                 if common_tip {
473                         // All block sources have the same tip. Assume we will be able to trivially get old
474                         // headers and drop our reorg cache.
475                         self.blocks_past_common_tip.clear();
476                 }
477                 blocks_connected
478         }
479 }
480
481 #[cfg(test)]
482 mod tests {
483         use super::*;
484         use bitcoin::blockdata::block::{Block, BlockHeader};
485         use bitcoin::util::uint::Uint256;
486         use std::collections::HashMap;
487         use std::sync::{Arc, Mutex};
488
489         struct ChainListener {
490                 blocks_connected: Mutex<Vec<(BlockHash, u32)>>,
491                 blocks_disconnected: Mutex<Vec<(BlockHash, u32)>>,
492         }
493         impl AChainListener for Arc<ChainListener> {
494                 fn a_block_connected(&mut self, block: &Block, height: u32) {
495                         self.blocks_connected.lock().unwrap().push((block.header.bitcoin_hash(), height));
496                 }
497                 fn a_block_disconnected(&mut self, header: &BlockHeader, height: u32) {
498                         self.blocks_disconnected.lock().unwrap().push((header.bitcoin_hash(), height));
499                 }
500         }
501
502         #[derive(Clone)]
503         struct BlockData {
504                 block: Block,
505                 chainwork: Uint256,
506                 height: u32,
507         }
508         struct Blockchain {
509                 blocks: Mutex<HashMap<BlockHash, BlockData>>,
510                 best_block: Mutex<(BlockHash, Option<u32>)>,
511                 headers_only: bool,
512                 disallowed: Mutex<bool>,
513         }
514         impl BlockSource for &Blockchain {
515                 fn get_header<'a>(&'a mut self, header_hash: &'a BlockHash, height_hint: Option<u32>) -> Pin<Box<dyn Future<Output = Result<BlockHeaderData, BlockSourceRespErr>> + 'a + Send>> {
516                         if *self.disallowed.lock().unwrap() { unreachable!(); }
517                         Box::pin(async move {
518                                 match self.blocks.lock().unwrap().get(header_hash) {
519                                         Some(block) => {
520                                                 assert_eq!(Some(block.height), height_hint);
521                                                 Ok(BlockHeaderData {
522                                                         chainwork: block.chainwork,
523                                                         height: block.height,
524                                                         header: block.block.header.clone(),
525                                                 })
526                                         },
527                                         None => Err(BlockSourceRespErr::NoResponse),
528                                 }
529                         })
530                 }
531                 fn get_block<'a>(&'a mut self, header_hash: &'a BlockHash) -> Pin<Box<dyn Future<Output = Result<Block, BlockSourceRespErr>> + 'a + Send>> {
532                         if *self.disallowed.lock().unwrap() { unreachable!(); }
533                         Box::pin(async move {
534                                 if self.headers_only {
535                                         Err(BlockSourceRespErr::NoResponse)
536                                 } else {
537                                         match self.blocks.lock().unwrap().get(header_hash) {
538                                                 Some(block) => Ok(block.block.clone()),
539                                                 None => Err(BlockSourceRespErr::NoResponse),
540                                         }
541                                 }
542                         })
543                 }
544                 fn get_best_block<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = Result<(BlockHash, Option<u32>), BlockSourceRespErr>> + 'a + Send>> {
545                         if *self.disallowed.lock().unwrap() { unreachable!(); }
546                         Box::pin(async move { Ok(self.best_block.lock().unwrap().clone()) })
547                 }
548         }
549
550         #[tokio::test]
551         async fn simple_block_connect() {
552                 let genesis = BlockData {
553                         block: bitcoin::blockdata::constants::genesis_block(bitcoin::network::constants::Network::Bitcoin),
554                         chainwork: Uint256::from_u64(0).unwrap(),
555                         height: 0,
556                 };
557
558                 // Build a chain based on genesis 1a, 2a, 3a, and 4a
559                 let block_1a = BlockData {
560                         block: Block {
561                                 header: BlockHeader {
562                                         version: 0,
563                                         prev_blockhash: genesis.block.bitcoin_hash(),
564                                         merkle_root: Default::default(), time: 0,
565                                         bits: genesis.block.header.bits,
566                                         nonce: 647569994,
567                                 },
568                                 txdata: Vec::new(),
569                         },
570                         chainwork: Uint256::from_u64(4295032833).unwrap(),
571                         height: 1
572                 };
573                 let block_1a_hash = block_1a.block.header.bitcoin_hash();
574                 let block_2a = BlockData {
575                         block: Block {
576                                 header: BlockHeader {
577                                         version: 0,
578                                         prev_blockhash: block_1a.block.bitcoin_hash(),
579                                         merkle_root: Default::default(), time: 4,
580                                         bits: genesis.block.header.bits,
581                                         nonce: 1185103332,
582                                 },
583                                 txdata: Vec::new(),
584                         },
585                         chainwork: Uint256::from_u64(4295032833 * 2).unwrap(),
586                         height: 2
587                 };
588                 let block_2a_hash = block_2a.block.header.bitcoin_hash();
589                 let block_3a = BlockData {
590                         block: Block {
591                                 header: BlockHeader {
592                                         version: 0,
593                                         prev_blockhash: block_2a.block.bitcoin_hash(),
594                                         merkle_root: Default::default(), time: 6,
595                                         bits: genesis.block.header.bits,
596                                         nonce: 198739431,
597                                 },
598                                 txdata: Vec::new(),
599                         },
600                         chainwork: Uint256::from_u64(4295032833 * 3).unwrap(),
601                         height: 3
602                 };
603                 let block_3a_hash = block_3a.block.header.bitcoin_hash();
604                 let block_4a = BlockData {
605                         block: Block {
606                                 header: BlockHeader {
607                                         version: 0,
608                                         prev_blockhash: block_3a.block.bitcoin_hash(),
609                                         merkle_root: Default::default(), time: 0,
610                                         bits: genesis.block.header.bits,
611                                         nonce: 590371681,
612                                 },
613                                 txdata: Vec::new(),
614                         },
615                         chainwork: Uint256::from_u64(4295032833 * 4).unwrap(),
616                         height: 4
617                 };
618                 let block_4a_hash = block_4a.block.header.bitcoin_hash();
619
620                 // Build a second chain based on genesis 1b, 2b, and 3b
621                 let block_1b = BlockData {
622                         block: Block {
623                                 header: BlockHeader {
624                                         version: 0,
625                                         prev_blockhash: genesis.block.bitcoin_hash(),
626                                         merkle_root: Default::default(), time: 6,
627                                         bits: genesis.block.header.bits,
628                                         nonce: 1347696353,
629                                 },
630                                 txdata: Vec::new(),
631                         },
632                         chainwork: Uint256::from_u64(4295032833).unwrap(),
633                         height: 1
634                 };
635                 let block_1b_hash = block_1b.block.header.bitcoin_hash();
636                 let block_2b = BlockData {
637                         block: Block {
638                                 header: BlockHeader {
639                                         version: 0,
640                                         prev_blockhash: block_1b.block.bitcoin_hash(),
641                                         merkle_root: Default::default(), time: 5,
642                                         bits: genesis.block.header.bits,
643                                         nonce: 144775545,
644                                 },
645                                 txdata: Vec::new(),
646                         },
647                         chainwork: Uint256::from_u64(4295032833 * 2).unwrap(),
648                         height: 2
649                 };
650                 let block_2b_hash = block_2b.block.header.bitcoin_hash();
651
652                 // Build a second chain based on 3a: 4c and 5c.
653                 let block_4c = BlockData {
654                         block: Block {
655                                 header: BlockHeader {
656                                         version: 0,
657                                         prev_blockhash: block_3a.block.bitcoin_hash(),
658                                         merkle_root: Default::default(), time: 17,
659                                         bits: genesis.block.header.bits,
660                                         nonce: 316634915,
661                                 },
662                                 txdata: Vec::new(),
663                         },
664                         chainwork: Uint256::from_u64(4295032833 * 4).unwrap(),
665                         height: 4
666                 };
667                 let block_4c_hash = block_4c.block.header.bitcoin_hash();
668                 let block_5c = BlockData {
669                         block: Block {
670                                 header: BlockHeader {
671                                         version: 0,
672                                         prev_blockhash: block_4c.block.bitcoin_hash(),
673                                         merkle_root: Default::default(), time: 3,
674                                         bits: genesis.block.header.bits,
675                                         nonce: 218413871,
676                                 },
677                                 txdata: Vec::new(),
678                         },
679                         chainwork: Uint256::from_u64(4295032833 * 5).unwrap(),
680                         height: 5
681                 };
682                 let block_5c_hash = block_5c.block.header.bitcoin_hash();
683
684                 // Create four block sources:
685                 // * chain_one and chain_two are general purpose block sources which we use to test reorgs,
686                 // * headers_chain only provides headers,
687                 // * and backup_chain is a backup which should not receive any queries (ie disallowed is
688                 //   false) until the headers_chain gets ahead of chain_one and chain_two.
689                 let mut blocks_one = HashMap::new();
690                 blocks_one.insert(genesis.block.header.bitcoin_hash(), genesis.clone());
691                 blocks_one.insert(block_1a_hash, block_1a.clone());
692                 blocks_one.insert(block_1b_hash, block_1b);
693                 blocks_one.insert(block_2b_hash, block_2b);
694                 let chain_one = Blockchain {
695                         blocks: Mutex::new(blocks_one), best_block: Mutex::new((block_2b_hash, Some(2))),
696                         headers_only: false, disallowed: Mutex::new(false)
697                 };
698
699                 let mut blocks_two = HashMap::new();
700                 blocks_two.insert(genesis.block.header.bitcoin_hash(), genesis.clone());
701                 blocks_two.insert(block_1a_hash, block_1a.clone());
702                 let chain_two = Blockchain {
703                         blocks: Mutex::new(blocks_two), best_block: Mutex::new((block_1a_hash, Some(1))),
704                         headers_only: false, disallowed: Mutex::new(false)
705                 };
706
707                 let mut blocks_three = HashMap::new();
708                 blocks_three.insert(genesis.block.header.bitcoin_hash(), genesis.clone());
709                 blocks_three.insert(block_1a_hash, block_1a.clone());
710                 let header_chain = Blockchain {
711                         blocks: Mutex::new(blocks_three), best_block: Mutex::new((block_1a_hash, Some(1))),
712                         headers_only: true, disallowed: Mutex::new(false)
713                 };
714
715                 let mut blocks_four = HashMap::new();
716                 blocks_four.insert(genesis.block.header.bitcoin_hash(), genesis);
717                 blocks_four.insert(block_1a_hash, block_1a);
718                 blocks_four.insert(block_2a_hash, block_2a.clone());
719                 blocks_four.insert(block_3a_hash, block_3a.clone());
720                 let backup_chain = Blockchain {
721                         blocks: Mutex::new(blocks_four), best_block: Mutex::new((block_3a_hash, Some(3))),
722                         headers_only: false, disallowed: Mutex::new(true)
723                 };
724
725                 // Stand up a client at block_1a with all four sources:
726                 let chain_notifier = Arc::new(ChainListener {
727                         blocks_connected: Mutex::new(Vec::new()), blocks_disconnected: Mutex::new(Vec::new())
728                 });
729                 let mut source_one = &chain_one;
730                 let mut source_two = &chain_two;
731                 let mut source_three = &header_chain;
732                 let mut source_four = &backup_chain;
733                 let mut client = MicroSPVClient::init((&chain_one).get_header(&block_1a_hash, Some(1)).await.unwrap(),
734                         vec![&mut source_one as &mut dyn BlockSource, &mut source_two as &mut dyn BlockSource, &mut source_three as &mut dyn BlockSource],
735                         vec![&mut source_four as &mut dyn BlockSource],
736                         Arc::clone(&chain_notifier), true);
737
738                 // Test that we will reorg onto 2b because chain_one knows about 1b + 2b
739                 assert!(client.poll_best_tip().await);
740                 assert_eq!(&chain_notifier.blocks_disconnected.lock().unwrap()[..], &[(block_1a_hash, 1)][..]);
741                 assert_eq!(&chain_notifier.blocks_connected.lock().unwrap()[..], &[(block_1b_hash, 1), (block_2b_hash, 2)][..]);
742                 assert_eq!(client.blocks_past_common_tip.len(), 2);
743                 assert_eq!(client.blocks_past_common_tip[0].header.bitcoin_hash(), block_1b_hash);
744                 assert_eq!(client.blocks_past_common_tip[1].header.bitcoin_hash(), block_2b_hash);
745
746                 // Test that even if chain_one (which we just got blocks from) stops responding to block or
747                 // header requests we can still reorg back because we never wiped our block cache as
748                 // chain_two always considered the "a" chain to contain the tip. We do this by simply
749                 // wiping the blocks chain_one knows about:
750                 chain_one.blocks.lock().unwrap().clear();
751                 chain_notifier.blocks_connected.lock().unwrap().clear();
752                 chain_notifier.blocks_disconnected.lock().unwrap().clear();
753
754                 // First test that nothing happens if nothing changes:
755                 assert!(!client.poll_best_tip().await);
756                 assert!(chain_notifier.blocks_disconnected.lock().unwrap().is_empty());
757                 assert!(chain_notifier.blocks_connected.lock().unwrap().is_empty());
758
759                 // Now add block 2a and 3a to chain_two and test that we reorg appropriately:
760                 chain_two.blocks.lock().unwrap().insert(block_2a_hash, block_2a.clone());
761                 chain_two.blocks.lock().unwrap().insert(block_3a_hash, block_3a.clone());
762                 *chain_two.best_block.lock().unwrap() = (block_3a_hash, Some(3));
763
764                 assert!(client.poll_best_tip().await);
765                 assert_eq!(&chain_notifier.blocks_disconnected.lock().unwrap()[..], &[(block_2b_hash, 2), (block_1b_hash, 1)][..]);
766                 assert_eq!(&chain_notifier.blocks_connected.lock().unwrap()[..], &[(block_1a_hash, 1), (block_2a_hash, 2), (block_3a_hash, 3)][..]);
767
768                 // Note that blocks_past_common_tip is not wiped as chain_one still returns 2a as its tip
769                 // (though a smarter MicroSPVClient may wipe 1a and 2a from the set eventually.
770                 assert_eq!(client.blocks_past_common_tip.len(), 3);
771                 assert_eq!(client.blocks_past_common_tip[0].header.bitcoin_hash(), block_1a_hash);
772                 assert_eq!(client.blocks_past_common_tip[1].header.bitcoin_hash(), block_2a_hash);
773                 assert_eq!(client.blocks_past_common_tip[2].header.bitcoin_hash(), block_3a_hash);
774
775                 chain_notifier.blocks_connected.lock().unwrap().clear();
776                 chain_notifier.blocks_disconnected.lock().unwrap().clear();
777
778                 // Test that after chain_one and header_chain consider 3a as their tip that we'll wipe our
779                 // block header cache:
780                 *chain_one.best_block.lock().unwrap() = (block_3a_hash, Some(3));
781                 *header_chain.best_block.lock().unwrap() = (block_3a_hash, Some(3));
782                 assert!(!client.poll_best_tip().await);
783                 assert!(chain_notifier.blocks_disconnected.lock().unwrap().is_empty());
784                 assert!(chain_notifier.blocks_connected.lock().unwrap().is_empty());
785
786                 assert!(client.blocks_past_common_tip.is_empty());
787
788                 // Test that setting the header chain to 4a does...almost nothing (though backup_chain
789                 // should now be queried) since we can't get the blocks from anywhere.
790                 header_chain.blocks.lock().unwrap().insert(block_2a_hash, block_2a);
791                 header_chain.blocks.lock().unwrap().insert(block_3a_hash, block_3a);
792                 header_chain.blocks.lock().unwrap().insert(block_4a_hash, block_4a.clone());
793                 *header_chain.best_block.lock().unwrap() = (block_4a_hash, Some(4));
794                 *backup_chain.disallowed.lock().unwrap() = false;
795
796                 assert!(!client.poll_best_tip().await);
797                 assert!(chain_notifier.blocks_disconnected.lock().unwrap().is_empty());
798                 assert!(chain_notifier.blocks_connected.lock().unwrap().is_empty());
799                 assert!(client.blocks_past_common_tip.is_empty());
800
801                 // But if backup_chain *also* has 4a, we'll fetch it from there:
802                 backup_chain.blocks.lock().unwrap().insert(block_4a_hash, block_4a);
803                 *backup_chain.best_block.lock().unwrap() = (block_4a_hash, Some(4));
804
805                 assert!(client.poll_best_tip().await);
806                 assert!(chain_notifier.blocks_disconnected.lock().unwrap().is_empty());
807                 assert_eq!(&chain_notifier.blocks_connected.lock().unwrap()[..], &[(block_4a_hash, 4)][..]);
808                 assert_eq!(client.blocks_past_common_tip.len(), 1);
809                 assert_eq!(client.blocks_past_common_tip[0].header.bitcoin_hash(), block_4a_hash);
810
811                 chain_notifier.blocks_connected.lock().unwrap().clear();
812                 chain_notifier.blocks_disconnected.lock().unwrap().clear();
813
814                 // Note that if only headers_chain has a reorg, we'll end up in a somewhat pessimal case
815                 // where we will disconnect and reconnect at each poll. We should fix this at some point by
816                 // making sure we can at least fetch one block before we disconnect, but short of using a
817                 // ton more memory there isn't much we can do in the case of two disconnects. We check that
818                 // the disconnect happens here on a one-block-disconnected reorg, even though its
819                 // non-normative behavior, as a good test of failing to reorg and returning back to the
820                 // best chain.
821                 header_chain.blocks.lock().unwrap().insert(block_4c_hash, block_4c);
822                 header_chain.blocks.lock().unwrap().insert(block_5c_hash, block_5c);
823                 *header_chain.best_block.lock().unwrap() = (block_5c_hash, Some(5));
824                 // We'll check the backup chain last, so don't give it 4a, as otherwise we'll connect it:
825                 *backup_chain.best_block.lock().unwrap() = (block_3a_hash, Some(3));
826
827                 assert!(client.poll_best_tip().await);
828                 assert_eq!(&chain_notifier.blocks_disconnected.lock().unwrap()[..], &[(block_4a_hash, 4)][..]);
829                 assert!(chain_notifier.blocks_connected.lock().unwrap().is_empty());
830
831                 chain_notifier.blocks_disconnected.lock().unwrap().clear();
832
833                 // Now reset the headers chain to 4a and test that we end up back there.
834                 *backup_chain.best_block.lock().unwrap() = (block_4a_hash, Some(4));
835                 *header_chain.best_block.lock().unwrap() = (block_4a_hash, Some(4));
836                 assert!(client.poll_best_tip().await);
837                 assert!(chain_notifier.blocks_disconnected.lock().unwrap().is_empty());
838                 assert_eq!(&chain_notifier.blocks_connected.lock().unwrap()[..], &[(block_4a_hash, 4)][..]);
839         }
840 }