Merge pull request #1845 from TheBlueMatt/2022-11-future-wake-fix
[rust-lightning] / lightning-block-sync / src / lib.rs
index 8854aa3e8e4bf8186b6cc119451e62ddc6710fae..189a68be0654dab1453ef459d3046d5d0001c17d 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.
 
+// 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)]
 
@@ -61,11 +64,11 @@ 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.
        ///
@@ -73,7 +76,7 @@ pub trait BlockSource : Sync + Send {
        /// to allow for a more efficient lookup.
        ///
        /// [`get_header`]: Self::get_header
-       fn get_best_block<'a>(&'a mut self) -> AsyncBlockSourceResult<(BlockHash, Option<u32>)>;
+       fn get_best_block<'a>(&'a self) -> AsyncBlockSourceResult<(BlockHash, Option<u32>)>;
 }
 
 /// Result type for `BlockSource` requests.
@@ -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<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);
+                       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(_) => {},
+               }
+       }
+
 }