Expand `chain::Listen` trivially to accept filtered block data 2022-04-listen-filtered-blocks
authorMatt Corallo <git@bluematt.me>
Tue, 26 Apr 2022 15:03:39 +0000 (15:03 +0000)
committerMatt Corallo <git@bluematt.me>
Tue, 26 Apr 2022 19:14:19 +0000 (19:14 +0000)
The `chain::Listen` interface provides a block-connection-based
alternative to the `chain::Confirm` interface, which supports
providing transaction data at a time separate from the block
connection time.

For users who are downloading the full headers tree (e.g. from a
node over the Bitcoin P2P protocol) but who are not downloading
full blocks (e.g. because they're using BIP 157/158 filtering)
there is no API that matches exactly their event stream -
`chain::Listen` requries full blocks for each block,
`chain::Confirm` requires breaking each connection event into two
calls.

Given its incredibly trivial to take a `TransactionData` in
addition to a `Block` in `chain::Listen` we do so here, adding a
default-implementation `block_connected` which simply creates the
`TransactionData`, which ultimately all of the `chain::Listen`
implementations currently do anyway.

Closes #1128.

lightning-block-sync/src/init.rs
lightning-block-sync/src/test_utils.rs
lightning/src/chain/chainmonitor.rs
lightning/src/chain/channelmonitor.rs
lightning/src/chain/mod.rs
lightning/src/ln/channelmanager.rs

index f5d839d21ca31931ca08199a24c00e8f14867f06..b3f745bd26e361e670e66436abb7b1dae7310979 100644 (file)
@@ -4,7 +4,7 @@
 use crate::{BlockSource, BlockSourceResult, Cache, ChainNotifier};
 use crate::poll::{ChainPoller, Validate, ValidatedBlockHeader};
 
-use bitcoin::blockdata::block::{Block, BlockHeader};
+use bitcoin::blockdata::block::BlockHeader;
 use bitcoin::hash_types::BlockHash;
 use bitcoin::network::constants::Network;
 
@@ -203,7 +203,7 @@ impl<'a, C: Cache> Cache for ReadOnlyCache<'a, C> {
 struct DynamicChainListener<'a, L: chain::Listen + ?Sized>(&'a L);
 
 impl<'a, L: chain::Listen + ?Sized> chain::Listen for DynamicChainListener<'a, L> {
-       fn block_connected(&self, _block: &Block, _height: u32) {
+       fn filtered_block_connected(&self, _header: &BlockHeader, _txdata: &chain::transaction::TransactionData, _height: u32) {
                unreachable!()
        }
 
@@ -216,10 +216,10 @@ impl<'a, L: chain::Listen + ?Sized> chain::Listen for DynamicChainListener<'a, L
 struct ChainListenerSet<'a, L: chain::Listen + ?Sized>(Vec<(u32, &'a L)>);
 
 impl<'a, L: chain::Listen + ?Sized> chain::Listen for ChainListenerSet<'a, L> {
-       fn block_connected(&self, block: &Block, height: u32) {
+       fn filtered_block_connected(&self, header: &BlockHeader, txdata: &chain::transaction::TransactionData, height: u32) {
                for (starting_height, chain_listener) in self.0.iter() {
                        if height > *starting_height {
-                               chain_listener.block_connected(block, height);
+                               chain_listener.filtered_block_connected(header, txdata, height);
                        }
                }
        }
index fe57c0c606d02d5cfbc5bf79f3bba1637904711f..c101d4bd3e8f0bdd516d2c0039cb579ceb5d5b89 100644 (file)
@@ -166,7 +166,7 @@ impl BlockSource for Blockchain {
 pub struct NullChainListener;
 
 impl chain::Listen for NullChainListener {
-       fn block_connected(&self, _block: &Block, _height: u32) {}
+       fn filtered_block_connected(&self, _header: &BlockHeader, _txdata: &chain::transaction::TransactionData, _height: u32) {}
        fn block_disconnected(&self, _header: &BlockHeader, _height: u32) {}
 }
 
@@ -195,13 +195,13 @@ impl MockChainListener {
 }
 
 impl chain::Listen for MockChainListener {
-       fn block_connected(&self, block: &Block, height: u32) {
+       fn filtered_block_connected(&self, header: &BlockHeader, _txdata: &chain::transaction::TransactionData, height: u32) {
                match self.expected_blocks_connected.borrow_mut().pop_front() {
                        None => {
-                               panic!("Unexpected block connected: {:?}", block.block_hash());
+                               panic!("Unexpected block connected: {:?}", header.block_hash());
                        },
                        Some(expected_block) => {
-                               assert_eq!(block.block_hash(), expected_block.header.block_hash());
+                               assert_eq!(header.block_hash(), expected_block.header.block_hash());
                                assert_eq!(height, expected_block.height);
                        },
                }
index 19095fa2375a6d8c3a52a61262736af9ac2fd61c..aae260e735bdbae5c0a538af8024e7e2ef2a78cb 100644 (file)
@@ -23,7 +23,7 @@
 //! events. The remote server would make use of [`ChainMonitor`] for block processing and for
 //! servicing [`ChannelMonitor`] updates from the client.
 
-use bitcoin::blockdata::block::{Block, BlockHeader};
+use bitcoin::blockdata::block::BlockHeader;
 use bitcoin::hash_types::Txid;
 
 use chain;
@@ -501,9 +501,7 @@ where
        L::Target: Logger,
        P::Target: Persist<ChannelSigner>,
 {
-       fn block_connected(&self, block: &Block, height: u32) {
-               let header = &block.header;
-               let txdata: Vec<_> = block.txdata.iter().enumerate().collect();
+       fn filtered_block_connected(&self, header: &BlockHeader, txdata: &TransactionData, height: u32) {
                log_debug!(self.logger, "New best block {} at height {} provided via block_connected", header.block_hash(), height);
                self.process_chain_data(header, Some(height), &txdata, |monitor, txdata| {
                        monitor.block_connected(
index fb2cbaddf533a9aa56cbc8586ba7f68f997345f7..681d895f27e05fa6ef42105f468bae7a1a258250 100644 (file)
@@ -20,7 +20,7 @@
 //! security-domain-separated system design, you should consider having multiple paths for
 //! ChannelMonitors to get out of the HSM and onto monitoring devices.
 
-use bitcoin::blockdata::block::{Block, BlockHeader};
+use bitcoin::blockdata::block::BlockHeader;
 use bitcoin::blockdata::transaction::{TxOut,Transaction};
 use bitcoin::blockdata::script::{Script, Builder};
 use bitcoin::blockdata::opcodes;
@@ -3007,9 +3007,8 @@ where
        F::Target: FeeEstimator,
        L::Target: Logger,
 {
-       fn block_connected(&self, block: &Block, height: u32) {
-               let txdata: Vec<_> = block.txdata.iter().enumerate().collect();
-               self.0.block_connected(&block.header, &txdata, height, &*self.1, &*self.2, &*self.3);
+       fn filtered_block_connected(&self, header: &BlockHeader, txdata: &TransactionData, height: u32) {
+               self.0.block_connected(header, txdata, height, &*self.1, &*self.2, &*self.3);
        }
 
        fn block_disconnected(&self, header: &BlockHeader, height: u32) {
index 25e5a97d288df42d4a2baf2263aae9e9d493f28e..24eb09e7090b212ea9867aba9844ed6993c83735 100644 (file)
@@ -87,9 +87,20 @@ pub trait Access {
 /// sourcing chain data using a block-oriented API should prefer this interface over [`Confirm`].
 /// Such clients fetch the entire header chain whereas clients using [`Confirm`] only fetch headers
 /// when needed.
+///
+/// By using [`Listen::filtered_block_connected`] this interface supports clients fetching the
+/// entire header chain and only blocks with matching transaction data using BIP 157 filters or
+/// other similar filtering.
 pub trait Listen {
+       /// Notifies the listener that a block was added at the given height, with the transaction data
+       /// possibly filtered.
+       fn filtered_block_connected(&self, header: &BlockHeader, txdata: &TransactionData, height: u32);
+
        /// Notifies the listener that a block was added at the given height.
-       fn block_connected(&self, block: &Block, height: u32);
+       fn block_connected(&self, block: &Block, height: u32) {
+               let txdata: Vec<_> = block.txdata.iter().enumerate().collect();
+               self.filtered_block_connected(&block.header, &txdata, height);
+       }
 
        /// Notifies the listener that a block was removed at the given height.
        fn block_disconnected(&self, header: &BlockHeader, height: u32);
@@ -355,8 +366,8 @@ pub struct WatchedOutput {
 }
 
 impl<T: Listen> Listen for core::ops::Deref<Target = T> {
-       fn block_connected(&self, block: &Block, height: u32) {
-               (**self).block_connected(block, height);
+       fn filtered_block_connected(&self, header: &BlockHeader, txdata: &TransactionData, height: u32) {
+               (**self).filtered_block_connected(header, txdata, height);
        }
 
        fn block_disconnected(&self, header: &BlockHeader, height: u32) {
@@ -369,9 +380,9 @@ where
        T::Target: Listen,
        U::Target: Listen,
 {
-       fn block_connected(&self, block: &Block, height: u32) {
-               self.0.block_connected(block, height);
-               self.1.block_connected(block, height);
+       fn filtered_block_connected(&self, header: &BlockHeader, txdata: &TransactionData, height: u32) {
+               self.0.filtered_block_connected(header, txdata, height);
+               self.1.filtered_block_connected(header, txdata, height);
        }
 
        fn block_disconnected(&self, header: &BlockHeader, height: u32) {
index 93746f0fde05cbc6b3ae3272a6851f36ef7d993c..df1ba36c7e447ef4df1e133338d73c5505a2b271 100644 (file)
@@ -18,7 +18,7 @@
 //! imply it needs to fail HTLCs/payments/channels it manages).
 //!
 
-use bitcoin::blockdata::block::{Block, BlockHeader};
+use bitcoin::blockdata::block::BlockHeader;
 use bitcoin::blockdata::transaction::Transaction;
 use bitcoin::blockdata::constants::genesis_block;
 use bitcoin::network::constants::Network;
@@ -5303,18 +5303,17 @@ where
        F::Target: FeeEstimator,
        L::Target: Logger,
 {
-       fn block_connected(&self, block: &Block, height: u32) {
+       fn filtered_block_connected(&self, header: &BlockHeader, txdata: &TransactionData, height: u32) {
                {
                        let best_block = self.best_block.read().unwrap();
-                       assert_eq!(best_block.block_hash(), block.header.prev_blockhash,
+                       assert_eq!(best_block.block_hash(), header.prev_blockhash,
                                "Blocks must be connected in chain-order - the connected header must build on the last connected header");
                        assert_eq!(best_block.height(), height - 1,
                                "Blocks must be connected in chain-order - the connected block height must be one greater than the previous height");
                }
 
-               let txdata: Vec<_> = block.txdata.iter().enumerate().collect();
-               self.transactions_confirmed(&block.header, &txdata, height);
-               self.best_block_updated(&block.header, height);
+               self.transactions_confirmed(header, txdata, height);
+               self.best_block_updated(header, height);
        }
 
        fn block_disconnected(&self, header: &BlockHeader, height: u32) {