X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning-block-sync%2Fsrc%2Flib.rs;h=189a68be0654dab1453ef459d3046d5d0001c17d;hb=d8a20eda5f74126d96700263716ee3938d4d3c8d;hp=321dd57e4713a638c86ba4fc033a89473d0129af;hpb=711bcefbcc0999543d9622c030ff7dc8118fc26f;p=rust-lightning diff --git a/lightning-block-sync/src/lib.rs b/lightning-block-sync/src/lib.rs index 321dd57e..189a68be 100644 --- a/lightning-block-sync/src/lib.rs +++ b/lightning-block-sync/src/lib.rs @@ -13,7 +13,10 @@ //! Both features support either blocking I/O using `std::net::TcpStream` or, with feature `tokio`, //! non-blocking I/O using `tokio::net::TcpStream` from inside a Tokio runtime. +// Prefix these with `rustdoc::` when we update our MSRV to be >= 1.52 to remove warnings. #![deny(broken_intra_doc_links)] +#![deny(private_intra_doc_links)] + #![deny(missing_docs)] #![deny(unsafe_code)] @@ -65,7 +68,7 @@ pub trait BlockSource : Sync + Send { /// Returns the block for a given hash. A headers-only block source should return a `Transient` /// error. - fn get_block<'a>(&'a self, header_hash: &'a BlockHash) -> AsyncBlockSourceResult<'a, Block>; + fn get_block<'a>(&'a self, header_hash: &'a BlockHash) -> AsyncBlockSourceResult<'a, BlockData>; /// Returns the hash of the best block and, optionally, its height. /// @@ -95,7 +98,7 @@ pub struct BlockSourceError { } /// The kind of `BlockSourceError`, either persistent or transient. -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum BlockSourceErrorKind { /// Indicates an error that won't resolve when retrying a request (e.g., invalid data). Persistent, @@ -136,7 +139,7 @@ impl BlockSourceError { /// A block header and some associated data. This information should be available from most block /// sources (and, notably, is available in Bitcoin Core's RPC and REST interfaces). -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct BlockHeaderData { /// The block header itself. pub header: BlockHeader, @@ -149,6 +152,18 @@ pub struct BlockHeaderData { pub chainwork: Uint256, } +/// A block including either all its transactions or only the block header. +/// +/// [`BlockSource`] may be implemented to either always return full blocks or, in the case of +/// compact block filters (BIP 157/158), return header-only blocks when no pertinent transactions +/// match. See [`chain::Filter`] for details on how to notify a source of such transactions. +pub enum BlockData { + /// A block containing all its transactions. + FullBlock(Block), + /// A block header for when the block does not contain any pertinent transactions. + HeaderOnly(BlockHeader), +} + /// A lightweight client for keeping a listener in sync with the chain, allowing for Simplified /// Payment Verification (SPV). /// @@ -393,13 +408,22 @@ impl<'a, C: Cache, L: Deref> ChainNotifier<'a, C, L> where L::Target: chain::Lis chain_poller: &mut P, ) -> Result<(), (BlockSourceError, Option)> { for header in connected_blocks.drain(..).rev() { - let block = chain_poller + let height = header.height; + let block_data = chain_poller .fetch_block(&header).await .or_else(|e| Err((e, Some(new_tip))))?; - debug_assert_eq!(block.block_hash, header.block_hash); + debug_assert_eq!(block_data.block_hash, header.block_hash); + + match block_data.deref() { + BlockData::FullBlock(block) => { + self.chain_listener.block_connected(&block, height); + }, + BlockData::HeaderOnly(header) => { + self.chain_listener.filtered_block_connected(&header, &[], height); + }, + } self.header_cache.block_connected(header.block_hash, header); - self.chain_listener.block_connected(&block, header.height); new_tip = header; } @@ -704,4 +728,25 @@ mod chain_notifier_tests { Ok(_) => panic!("Expected error"), } } + + #[tokio::test] + async fn sync_from_chain_with_filtered_blocks() { + let mut chain = Blockchain::default().with_height(3).filtered_blocks(); + + let new_tip = chain.tip(); + let old_tip = chain.at_height(1); + let chain_listener = &MockChainListener::new() + .expect_filtered_block_connected(*chain.at_height(2)) + .expect_filtered_block_connected(*new_tip); + let mut notifier = ChainNotifier { + header_cache: &mut chain.header_cache(0..=1), + chain_listener, + }; + let mut poller = poll::ChainPoller::new(&mut chain, Network::Testnet); + match notifier.synchronize_listener(new_tip, &old_tip, &mut poller).await { + Err((e, _)) => panic!("Unexpected error: {:?}", e), + Ok(_) => {}, + } + } + }