Fix unused warning for un-accessed enum variant field in net-tokio
[rust-lightning] / lightning-block-sync / src / lib.rs
index db536fe7d43778ccb062be5813548f2072353a44..4a01d4673b31e91d56c3cb350d995c1b7a3d7403 100644 (file)
 //!
 //! 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.
-//!
-//! [`SpvClient`]: struct.SpvClient.html
-//! [`BlockSource`]: trait.BlockSource.html
+
+#![deny(rustdoc::broken_intra_doc_links)]
+#![deny(rustdoc::private_intra_doc_links)]
+
+#![deny(missing_docs)]
+#![deny(unsafe_code)]
+
+#![cfg_attr(docsrs, feature(doc_auto_cfg))]
 
 #[cfg(any(feature = "rest-client", feature = "rpc-client"))]
 pub mod http;
@@ -22,6 +27,8 @@ pub mod http;
 pub mod init;
 pub mod poll;
 
+pub mod gossip;
+
 #[cfg(feature = "rest-client")]
 pub mod rest;
 
@@ -39,9 +46,9 @@ mod utils;
 
 use crate::poll::{ChainTip, Poll, ValidatedBlockHeader};
 
-use bitcoin::blockdata::block::{Block, BlockHeader};
+use bitcoin::blockdata::block::{Block, Header};
 use bitcoin::hash_types::BlockHash;
-use bitcoin::util::uint::Uint256;
+use bitcoin::pow::Work;
 
 use lightning::chain;
 use lightning::chain::Listen;
@@ -58,29 +65,28 @@ pub trait BlockSource : Sync + Send {
        ///
        /// Implementations that cannot find headers based on the hash should return a `Transient` error
        /// when `height_hint` is `None`.
-       fn get_header<'a>(&'a mut self, header_hash: &'a BlockHash, height_hint: Option<u32>) -> AsyncBlockSourceResult<'a, BlockHeaderData>;
+       fn get_header<'a>(&'a self, header_hash: &'a BlockHash, height_hint: Option<u32>) -> AsyncBlockSourceResult<'a, BlockHeaderData>;
 
        /// Returns the block for a given hash. A headers-only block source should return a `Transient`
        /// error.
-       fn get_block<'a>(&'a mut 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.
        ///
        /// When polling a block source, [`Poll`] implementations may pass the height to [`get_header`]
        /// to allow for a more efficient lookup.
        ///
-       /// [`Poll`]: poll/trait.Poll.html
-       /// [`get_header`]: #tymethod.get_header
-       fn get_best_block<'a>(&'a mut self) -> AsyncBlockSourceResult<(BlockHash, Option<u32>)>;
+       /// [`get_header`]: Self::get_header
+       fn get_best_block<'a>(&'a self) -> AsyncBlockSourceResult<(BlockHash, Option<u32>)>;
 }
 
 /// Result type for `BlockSource` requests.
-type BlockSourceResult<T> = Result<T, BlockSourceError>;
+pub type BlockSourceResult<T> = Result<T, BlockSourceError>;
 
 // TODO: Replace with BlockSourceResult once `async` trait functions are supported. For details,
 // see: https://areweasyncyet.rs.
 /// Result type for asynchronous `BlockSource` requests.
-type AsyncBlockSourceResult<'a, T> = Pin<Box<dyn Future<Output = BlockSourceResult<T>> + 'a + Send>>;
+pub type AsyncBlockSourceResult<'a, T> = Pin<Box<dyn Future<Output = BlockSourceResult<T>> + 'a + Send>>;
 
 /// Error type for `BlockSource` requests.
 ///
@@ -93,7 +99,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,
@@ -127,6 +133,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<dyn std::error::Error + Send + Sync> {
                self.error
        }
@@ -134,17 +143,28 @@ 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,
+       pub header: Header,
 
        /// The block height where the genesis block has height 0.
        pub height: u32,
 
-       /// The total chain work in expected number of double-SHA256 hashes required to build a chain
-       /// of equivalent weight.
-       pub chainwork: Uint256,
+       /// The total chain work required to build a chain of equivalent weight.
+       pub chainwork: Work,
+}
+
+/// 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(Header),
 }
 
 /// A lightweight client for keeping a listener in sync with the chain, allowing for Simplified
@@ -176,8 +196,6 @@ where L::Target: chain::Listen {
 /// Implementations may define how long to retain headers such that it's unlikely they will ever be
 /// needed to disconnect a block.  In cases where block sources provide access to headers on stale
 /// forks reliably, caches may be entirely unnecessary.
-///
-/// [`ChainNotifier`]: struct.ChainNotifier.html
 pub trait Cache {
        /// Retrieves the block header keyed by the given block hash.
        fn look_up(&self, block_hash: &BlockHash) -> Option<&ValidatedBlockHeader>;
@@ -218,7 +236,7 @@ impl<'a, P: Poll, C: Cache, L: Deref> SpvClient<'a, P, C, L> where L::Target: ch
        /// * `header_cache` is used to look up and store headers on the best chain
        /// * `chain_listener` is notified of any blocks connected or disconnected
        ///
-       /// [`poll_best_tip`]: struct.SpvClient.html#method.poll_best_tip
+       /// [`poll_best_tip`]: SpvClient::poll_best_tip
        pub fn new(
                chain_tip: ValidatedBlockHeader,
                chain_poller: P,
@@ -273,7 +291,7 @@ impl<'a, P: Poll, C: Cache, L: Deref> SpvClient<'a, P, C, L> where L::Target: ch
 
 /// Notifies [listeners] of blocks that have been connected or disconnected from the chain.
 ///
-/// [listeners]: ../../lightning/chain/trait.Listen.html
+/// [listeners]: lightning::chain::Listen
 pub struct ChainNotifier<'a, C: Cache, L: Deref> where L::Target: chain::Listen {
        /// Cache for looking up headers before fetching from a block source.
        header_cache: &'a mut C,
@@ -393,13 +411,22 @@ impl<'a, C: Cache, L: Deref> ChainNotifier<'a, C, L> where L::Target: chain::Lis
                chain_poller: &mut P,
        ) -> Result<(), (BlockSourceError, Option<ValidatedBlockHeader>)> {
                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 +731,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(_) => {},
+               }
+       }
+
 }