1 //! An implementation of a simple SPV client which can interrogate abstract block sources to keep
2 //! lightning objects on the best chain.
4 //! With feature `rpc-client` we provide a client which can fetch blocks from Bitcoin Core's RPC
7 //! With feature `rest-client` we provide a client which can fetch blocks from Bitcoin Core's REST
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.
13 #[cfg(any(feature = "rest-client", feature = "rpc-client"))]
16 #[cfg(any(feature = "rest-client", feature = "rpc-client"))]
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;
24 use bitcoin::hashes::hex::ToHex;
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;
31 use std::future::Future;
34 use std::ops::DerefMut;
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
45 /// The block header itself
46 pub header: BlockHeader
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.
55 /// Indicates the BlockSource isn't responsive or may be misconfigured but we want to continue
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.
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.
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>>;
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>>;
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.
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>>;
89 /// Stateless header checks on a given header.
91 fn stateless_check_header(header: &BlockHeader) -> Result<(), BlockSourceRespErr> {
92 if header.validate_pow(&header.target()).is_err() {
93 Err(BlockSourceRespErr::BogusData)
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);
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);
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)
117 } else if child_header.header.bits != previous_header.header.bits {
118 return Err(BlockSourceRespErr::BogusData)
125 ForkPoint(BlockHeaderData),
126 DisconnectBlock(BlockHeaderData),
127 ConnectBlock(BlockHeaderData),
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()));
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()));
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));
152 // Current is higher than the prev, walk current down by listing blocks we need to
154 let new_cur_header = block_source.get_header(¤t_header.header.prev_blockhash, Some(current_header.height - 1)).await?;
155 check_builds_on(¤t_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?;
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?;
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?;
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(¤t_header.header.prev_blockhash, Some(current_header.height - 1)).await?;
175 check_builds_on(¤t_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?;
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?;
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); }
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 };
204 find_fork_step(&mut steps_tx, current_header, &prev_header, block_source, head_blocks, mainnet).await?;
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);
217 impl AChainListener for &BlockNotifierArc {
218 fn a_block_connected(&mut self, block: &Block, height: u32) {
219 self.block_connected(block, height);
221 fn a_block_disconnected(&mut self, header: &BlockHeader, height: u32) {
222 self.block_disconnected(header, height);
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() {
236 self.block_connected(&block.header, height, &txn, &idxn);
238 fn a_block_disconnected(&mut self, header: &BlockHeader, height: u32) {
239 self.block_disconnected(header, height);
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() {
251 self.0.block_connected(&txn, height, &block.bitcoin_hash(), self.1, self.2);
253 fn a_block_disconnected(&mut self, header: &BlockHeader, height: u32) {
254 self.0.block_disconnected(height, &header.bitcoin_hash(), self.1, self.2);
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.
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))?;
269 let mut last_disconnect_tip = None;
270 let mut new_tip = None;
271 for event in events.iter() {
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);
278 chain_notifier.a_block_disconnected(&header.header, header.height);
279 last_disconnect_tip = Some(header.header.prev_blockhash);
281 &ForkStep::ForkPoint(ref header) => {
282 new_tip = Some(header.clone());
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);
296 debug_assert_eq!(tip_header.header.bitcoin_hash(), *last_disconnect_tip.as_ref().unwrap());
298 // Set new_tip to indicate that we got a valid header chain we wanted to connect to, but
300 new_tip = Some(old_header.clone());
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)),
309 if block.header != header_data.header || !block.check_merkle_root() || !block.check_witness_commitment() {
310 return Err((BlockSourceRespErr::BogusData, new_tip));
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);
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.
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; }
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();
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.
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
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.
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>,
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.
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.
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();
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
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;
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.
395 macro_rules! handle_err {
399 Err(BlockSourceRespErr::BogusData) => {
400 $cur_hash = Err(BlockSourceRespErr::BogusData);
403 Err(BlockSourceRespErr::NoResponse) => {
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);
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);
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);
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;
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);
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;
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);
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; }
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 {
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();
484 use bitcoin::blockdata::block::{Block, BlockHeader};
485 use bitcoin::util::uint::Uint256;
486 use std::collections::HashMap;
487 use std::sync::{Arc, Mutex};
489 struct ChainListener {
490 blocks_connected: Mutex<Vec<(BlockHash, u32)>>,
491 blocks_disconnected: Mutex<Vec<(BlockHash, u32)>>,
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));
497 fn a_block_disconnected(&mut self, header: &BlockHeader, height: u32) {
498 self.blocks_disconnected.lock().unwrap().push((header.bitcoin_hash(), height));
509 blocks: Mutex<HashMap<BlockHash, BlockData>>,
510 best_block: Mutex<(BlockHash, Option<u32>)>,
512 disallowed: Mutex<bool>,
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) {
520 assert_eq!(Some(block.height), height_hint);
522 chainwork: block.chainwork,
523 height: block.height,
524 header: block.block.header.clone(),
527 None => Err(BlockSourceRespErr::NoResponse),
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)
537 match self.blocks.lock().unwrap().get(header_hash) {
538 Some(block) => Ok(block.block.clone()),
539 None => Err(BlockSourceRespErr::NoResponse),
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()) })
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(),
558 // Build a chain based on genesis 1a, 2a, 3a, and 4a
559 let block_1a = BlockData {
561 header: BlockHeader {
563 prev_blockhash: genesis.block.bitcoin_hash(),
564 merkle_root: Default::default(), time: 0,
565 bits: genesis.block.header.bits,
570 chainwork: Uint256::from_u64(4295032833).unwrap(),
573 let block_1a_hash = block_1a.block.header.bitcoin_hash();
574 let block_2a = BlockData {
576 header: BlockHeader {
578 prev_blockhash: block_1a.block.bitcoin_hash(),
579 merkle_root: Default::default(), time: 4,
580 bits: genesis.block.header.bits,
585 chainwork: Uint256::from_u64(4295032833 * 2).unwrap(),
588 let block_2a_hash = block_2a.block.header.bitcoin_hash();
589 let block_3a = BlockData {
591 header: BlockHeader {
593 prev_blockhash: block_2a.block.bitcoin_hash(),
594 merkle_root: Default::default(), time: 6,
595 bits: genesis.block.header.bits,
600 chainwork: Uint256::from_u64(4295032833 * 3).unwrap(),
603 let block_3a_hash = block_3a.block.header.bitcoin_hash();
604 let block_4a = BlockData {
606 header: BlockHeader {
608 prev_blockhash: block_3a.block.bitcoin_hash(),
609 merkle_root: Default::default(), time: 0,
610 bits: genesis.block.header.bits,
615 chainwork: Uint256::from_u64(4295032833 * 4).unwrap(),
618 let block_4a_hash = block_4a.block.header.bitcoin_hash();
620 // Build a second chain based on genesis 1b, 2b, and 3b
621 let block_1b = BlockData {
623 header: BlockHeader {
625 prev_blockhash: genesis.block.bitcoin_hash(),
626 merkle_root: Default::default(), time: 6,
627 bits: genesis.block.header.bits,
632 chainwork: Uint256::from_u64(4295032833).unwrap(),
635 let block_1b_hash = block_1b.block.header.bitcoin_hash();
636 let block_2b = BlockData {
638 header: BlockHeader {
640 prev_blockhash: block_1b.block.bitcoin_hash(),
641 merkle_root: Default::default(), time: 5,
642 bits: genesis.block.header.bits,
647 chainwork: Uint256::from_u64(4295032833 * 2).unwrap(),
650 let block_2b_hash = block_2b.block.header.bitcoin_hash();
652 // Build a second chain based on 3a: 4c and 5c.
653 let block_4c = BlockData {
655 header: BlockHeader {
657 prev_blockhash: block_3a.block.bitcoin_hash(),
658 merkle_root: Default::default(), time: 17,
659 bits: genesis.block.header.bits,
664 chainwork: Uint256::from_u64(4295032833 * 4).unwrap(),
667 let block_4c_hash = block_4c.block.header.bitcoin_hash();
668 let block_5c = BlockData {
670 header: BlockHeader {
672 prev_blockhash: block_4c.block.bitcoin_hash(),
673 merkle_root: Default::default(), time: 3,
674 bits: genesis.block.header.bits,
679 chainwork: Uint256::from_u64(4295032833 * 5).unwrap(),
682 let block_5c_hash = block_5c.block.header.bitcoin_hash();
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)
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)
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)
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)
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())
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);
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);
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();
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());
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));
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)][..]);
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);
775 chain_notifier.blocks_connected.lock().unwrap().clear();
776 chain_notifier.blocks_disconnected.lock().unwrap().clear();
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());
786 assert!(client.blocks_past_common_tip.is_empty());
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;
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());
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));
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);
811 chain_notifier.blocks_connected.lock().unwrap().clear();
812 chain_notifier.blocks_disconnected.lock().unwrap().clear();
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
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));
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());
831 chain_notifier.blocks_disconnected.lock().unwrap().clear();
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)][..]);