X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning-block-sync%2Fsrc%2Flib.rs;h=3561a1b5d769f19381dfdf1bec1e7749cdf8c2d8;hb=afdcd1c19880a0e982184fbb576b392b45e1c795;hp=321dd57e4713a638c86ba4fc033a89473d0129af;hpb=83595dbfdc9669e52b7095a6c2bc087a4398cc1d;p=rust-lightning diff --git a/lightning-block-sync/src/lib.rs b/lightning-block-sync/src/lib.rs index 321dd57e..3561a1b5 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)] @@ -25,6 +28,8 @@ pub mod http; pub mod init; pub mod poll; +pub mod gossip; + #[cfg(feature = "rest-client")] pub mod rest; @@ -65,7 +70,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 +100,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, @@ -129,6 +134,9 @@ impl BlockSourceError { } /// Converts the error into the underlying error. + /// + /// May contain an [`std::io::Error`] from the [`BlockSource`]. See implementations for further + /// details, if any. pub fn into_inner(self) -> Box { self.error } @@ -136,7 +144,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 +157,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 +413,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); + .map_err(|e| (e, Some(new_tip)))?; + 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 +733,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(_) => {}, + } + } + }