From 51e26dd150da614b945fd97ec75aae134651ccb4 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Thu, 25 Jun 2020 18:07:55 -0700 Subject: [PATCH] f - Update ChainListener::block_connected Support filtered blocks by taking a &BlockHeader and &Transaction slice of the form &[(usize, &Transaction)] where each transaction is paired with its position within the block. --- fuzz/src/chanmon_consistency.rs | 17 ++++++----------- fuzz/src/full_stack.rs | 19 +++++++++---------- fuzz/src/router.rs | 5 +++-- lightning/src/chain/chaininterface.rs | 21 ++++++++++++--------- lightning/src/ln/channel.rs | 16 ++++++++-------- lightning/src/ln/channelmanager.rs | 15 ++++++++------- lightning/src/ln/channelmonitor.rs | 10 +++++----- lightning/src/ln/functional_tests.rs | 16 +++++----------- lightning/src/util/test_utils.rs | 4 ++-- 9 files changed, 58 insertions(+), 65 deletions(-) diff --git a/fuzz/src/chanmon_consistency.rs b/fuzz/src/chanmon_consistency.rs index 6d337a08..887790f5 100644 --- a/fuzz/src/chanmon_consistency.rs +++ b/fuzz/src/chanmon_consistency.rs @@ -10,7 +10,7 @@ //! channel being force-closed. use bitcoin::BitcoinHash; -use bitcoin::blockdata::block::{Block, BlockHeader}; +use bitcoin::blockdata::block::BlockHeader; use bitcoin::blockdata::transaction::{Transaction, TxOut}; use bitcoin::blockdata::script::{Builder, Script}; use bitcoin::blockdata::opcodes; @@ -306,17 +306,12 @@ pub fn do_test(data: &[u8], out: Out) { macro_rules! confirm_txn { ($node: expr) => { { - let mut block = Block { - header: BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }, - txdata: channel_txn.clone(), - }; - $node.block_connected(&block, 1); + let mut header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }; + let txdata: Vec<_> = channel_txn.iter().enumerate().map(|(i, tx)| (i + 1, tx)).collect(); + $node.block_connected(&header, &txdata, 1); for i in 2..100 { - block = Block { - header: BlockHeader { version: 0x20000000, prev_blockhash: block.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }, - txdata: vec![], - }; - $node.block_connected(&block, i); + header = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }; + $node.block_connected(&header, &[], i); } } } } diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs index 614c6290..96588104 100644 --- a/fuzz/src/full_stack.rs +++ b/fuzz/src/full_stack.rs @@ -4,7 +4,7 @@ //! or payments to send/ways to handle events generated. //! This test has been very useful, though due to its complexity good starting inputs are critical. -use bitcoin::blockdata::block::{Block, BlockHeader}; +use bitcoin::blockdata::block::BlockHeader; use bitcoin::blockdata::transaction::{Transaction, TxOut}; use bitcoin::blockdata::script::{Builder, Script}; use bitcoin::blockdata::opcodes; @@ -175,29 +175,28 @@ impl<'a> MoneyLossDetector<'a> { } fn connect_block(&mut self, all_txn: &[Transaction]) { - for tx in all_txn.iter() { + let mut txdata = Vec::with_capacity(all_txn.len()); + for (idx, tx) in all_txn.iter().enumerate() { let txid = tx.txid(); match self.txids_confirmed.entry(txid) { hash_map::Entry::Vacant(e) => { e.insert(self.height); + txdata.push((idx + 1, tx)); }, _ => {}, } } - let block = Block { - header: BlockHeader { version: 0x20000000, prev_blockhash: self.header_hashes[self.height], merkle_root: Default::default(), time: self.blocks_connected, bits: 42, nonce: 42 }, - txdata: all_txn.to_vec(), - }; + let header = BlockHeader { version: 0x20000000, prev_blockhash: self.header_hashes[self.height], merkle_root: Default::default(), time: self.blocks_connected, bits: 42, nonce: 42 }; self.height += 1; self.blocks_connected += 1; - self.manager.block_connected(&block, self.height as u32); - (*self.monitor).block_connected(&block, self.height as u32); + self.manager.block_connected(&header, &txdata, self.height as u32); + (*self.monitor).block_connected(&header, &txdata, self.height as u32); if self.header_hashes.len() > self.height { - self.header_hashes[self.height] = block.bitcoin_hash(); + self.header_hashes[self.height] = header.bitcoin_hash(); } else { assert_eq!(self.header_hashes.len(), self.height); - self.header_hashes.push(block.bitcoin_hash()); + self.header_hashes.push(header.bitcoin_hash()); } self.max_height = cmp::max(self.height, self.max_height); } diff --git a/fuzz/src/router.rs b/fuzz/src/router.rs index 31e552f5..ffb5ccb8 100644 --- a/fuzz/src/router.rs +++ b/fuzz/src/router.rs @@ -1,5 +1,6 @@ use bitcoin::blockdata::script::{Script, Builder}; -use bitcoin::blockdata::block::Block; +use bitcoin::blockdata::block::BlockHeader; +use bitcoin::blockdata::transaction::Transaction; use bitcoin::hash_types::{Txid, BlockHash}; use lightning::chain::chaininterface::{ChainError,ChainWatchInterface}; @@ -75,7 +76,7 @@ impl ChainWatchInterface for DummyChainWatcher { fn install_watch_tx(&self, _txid: &Txid, _script_pub_key: &Script) { } fn install_watch_outpoint(&self, _outpoint: (Txid, u32), _out_script: &Script) { } fn watch_all_txn(&self) { } - fn filter_block(&self, _block: &Block) -> Vec { + fn filter_block(&self, _header: &BlockHeader, _txdata: &[(usize, &Transaction)]) -> Vec { Vec::new() } fn reentered(&self) -> usize { 0 } diff --git a/lightning/src/chain/chaininterface.rs b/lightning/src/chain/chaininterface.rs index 4cf4c5fb..815ca270 100644 --- a/lightning/src/chain/chaininterface.rs +++ b/lightning/src/chain/chaininterface.rs @@ -55,7 +55,7 @@ pub trait ChainWatchInterface: Sync + Send { /// Gets the list of transaction indices within a given block that the ChainWatchInterface is /// watching for. - fn filter_block(&self, block: &Block) -> Vec; + fn filter_block(&self, header: &BlockHeader, txdata: &[(usize, &Transaction)]) -> Vec; /// Returns a usize that changes when the ChainWatchInterface's watched data is modified. /// Users of `filter_block` should pre-save a copy of `reentered`'s return value and use it to @@ -71,8 +71,10 @@ pub trait BroadcasterInterface: Sync + Send { /// A trait indicating a desire to listen for events from the chain pub trait ChainListener: Sync + Send { - /// Notifies a listener that a block was connected. - fn block_connected(&self, block: &Block, height: u32); + /// Notifies a listener that a block was connected. Transactions may be filtered and are given + /// paired with their position within the block. + fn block_connected(&self, header: &BlockHeader, txdata: &[(usize, &Transaction)], height: u32); + /// Notifies a listener that a block was disconnected. /// Unlike block_connected, this *must* never be called twice for the same disconnect event. /// Height must be the one of the block which was disconnected (not new height of the best chain) @@ -254,10 +256,11 @@ impl<'a, CL: Deref + 'a> BlockNotifier<'a, CL> { } /// Notify listeners that a block was connected. - pub fn block_connected<'b>(&self, block: &'b Block, height: u32) { + pub fn block_connected(&self, block: &Block, height: u32) { + let txdata: Vec<_> = block.txdata.iter().enumerate().collect(); let listeners = self.listeners.lock().unwrap(); for listener in listeners.iter() { - listener.block_connected(block, height); + listener.block_connected(&block.header, &txdata, height); } } @@ -320,13 +323,13 @@ impl ChainWatchInterface for ChainWatchInterfaceUtil { Err(ChainError::NotSupported) } - fn filter_block(&self, block: &Block) -> Vec { + fn filter_block(&self, _header: &BlockHeader, txdata: &[(usize, &Transaction)]) -> Vec { let mut matched_index = Vec::new(); { let watched = self.watched.lock().unwrap(); - for (index, transaction) in block.txdata.iter().enumerate() { - if self.does_match_tx_unguarded(transaction, &watched) { - matched_index.push(index); + for (i, transaction) in txdata.iter().enumerate() { + if self.does_match_tx_unguarded(transaction.1, &watched) { + matched_index.push(i); } } } diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index 26fc8dcc..d717182b 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -1,4 +1,4 @@ -use bitcoin::blockdata::block::{Block, BlockHeader}; +use bitcoin::blockdata::block::BlockHeader; use bitcoin::blockdata::script::{Script,Builder}; use bitcoin::blockdata::transaction::{TxIn, TxOut, Transaction, SigHashType}; use bitcoin::blockdata::opcodes; @@ -3314,7 +3314,7 @@ impl Channel { /// /// May return some HTLCs (and their payment_hash) which have timed out and should be failed /// back. - pub fn block_connected(&mut self, block: &Block, height: u32) -> Result<(Option, Vec<(HTLCSource, PaymentHash)>), msgs::ErrorMessage> { + pub fn block_connected(&mut self, header: &BlockHeader, txdata: &[(usize, &Transaction)], height: u32) -> Result<(Option, Vec<(HTLCSource, PaymentHash)>), msgs::ErrorMessage> { let mut timed_out_htlcs = Vec::new(); self.holding_cell_htlc_updates.retain(|htlc_update| { match htlc_update { @@ -3328,13 +3328,13 @@ impl Channel { } }); let non_shutdown_state = self.channel_state & (!MULTI_STATE_FLAGS); - if block.bitcoin_hash() != self.last_block_connected { + if header.bitcoin_hash() != self.last_block_connected { if self.funding_tx_confirmations > 0 { self.funding_tx_confirmations += 1; } } if non_shutdown_state & !(ChannelState::TheirFundingLocked as u32) == ChannelState::FundingSent as u32 { - for (index_in_block, ref tx) in block.txdata.iter().enumerate() { + for &(index_in_block, tx) in txdata.iter() { if tx.txid() == self.funding_txo.unwrap().txid { let txo_idx = self.funding_txo.unwrap().index as usize; if txo_idx >= tx.output.len() || tx.output[txo_idx].script_pubkey != self.get_funding_redeemscript().to_v0_p2wsh() || @@ -3377,9 +3377,9 @@ impl Channel { } } } - if block.bitcoin_hash() != self.last_block_connected { - self.last_block_connected = block.bitcoin_hash(); - self.update_time_counter = cmp::max(self.update_time_counter, block.header.time); + if header.bitcoin_hash() != self.last_block_connected { + self.last_block_connected = header.bitcoin_hash(); + self.update_time_counter = cmp::max(self.update_time_counter, header.time); if let Some(channel_monitor) = self.channel_monitor.as_mut() { channel_monitor.last_block_hash = self.last_block_connected; } @@ -3403,7 +3403,7 @@ impl Channel { // funding_tx_confirmed_in and return. false }; - self.funding_tx_confirmed_in = Some(block.bitcoin_hash()); + self.funding_tx_confirmed_in = Some(header.bitcoin_hash()); //TODO: Note that this must be a duplicate of the previous commitment point they sent us, //as otherwise we will have a commitment transaction that they can't revoke (well, kinda, diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index c595e102..43e88c7c 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -8,8 +8,9 @@ //! on-chain transactions (it only monitors the chain to watch for any force-closes that might //! imply it needs to fail HTLCs/payments/channels it manages). -use bitcoin::blockdata::block::{Block, BlockHeader}; +use bitcoin::blockdata::block::BlockHeader; use bitcoin::blockdata::constants::genesis_block; +use bitcoin::blockdata::transaction::Transaction; use bitcoin::network::constants::Network; use bitcoin::util::hash::BitcoinHash; @@ -2977,8 +2978,8 @@ impl= block.header.time as usize { break; } - if self.last_node_announcement_serial.compare_exchange(old_serial, block.header.time as usize, Ordering::AcqRel, Ordering::Relaxed).is_ok() { + if old_serial >= header.time as usize { break; } + if self.last_node_announcement_serial.compare_exchange(old_serial, header.time as usize, Ordering::AcqRel, Ordering::Relaxed).is_ok() { break; } } diff --git a/lightning/src/ln/channelmonitor.rs b/lightning/src/ln/channelmonitor.rs index e2ac2b8e..114c0361 100644 --- a/lightning/src/ln/channelmonitor.rs +++ b/lightning/src/ln/channelmonitor.rs @@ -11,7 +11,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::transaction::OutPoint as BitcoinOutPoint; use bitcoin::blockdata::script::{Script, Builder}; @@ -183,13 +183,13 @@ impl = matched_indexes.iter().map(|index| &block.txdata[*index]).collect(); + let matched_indexes = self.chain_monitor.filter_block(header, txdata); + let matched_txn: Vec<_> = matched_indexes.iter().map(|index| txdata[*index].1).collect(); let last_seen = self.chain_monitor.reentered(); - let block_hash = block.bitcoin_hash(); + let block_hash = header.bitcoin_hash(); { let mut monitors = self.monitors.lock().unwrap(); for monitor in monitors.values_mut() { diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index c8dd8dea..73e2c7ed 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -7244,15 +7244,12 @@ fn test_no_failure_dust_htlc_local_commitment() { output: vec![outp] }; - let block = Block { - header: BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }, - txdata: vec![dummy_tx], - }; - nodes[0].chan_monitor.simple_monitor.block_connected(&block, 1); + let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }; + nodes[0].chan_monitor.simple_monitor.block_connected(&header, &[(0, &dummy_tx)], 1); assert_eq!(nodes[0].node.get_and_clear_pending_events().len(), 0); assert_eq!(nodes[0].node.get_and_clear_pending_msg_events().len(), 0); // We broadcast a few more block to check everything is all right - connect_blocks(&nodes[0].block_notifier, 20, 1, true, block.bitcoin_hash()); + connect_blocks(&nodes[0].block_notifier, 20, 1, true, header.bitcoin_hash()); assert_eq!(nodes[0].node.get_and_clear_pending_events().len(), 0); assert_eq!(nodes[0].node.get_and_clear_pending_msg_events().len(), 0); @@ -8377,11 +8374,8 @@ fn test_update_err_monitor_lockdown() { assert!(watchtower.add_monitor(outpoint, new_monitor).is_ok()); watchtower }; - let block = Block { - header: BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }, - txdata: vec![], - }; - watchtower.simple_monitor.block_connected(&block, 200); + let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }; + watchtower.simple_monitor.block_connected(&header, &[], 200); // Try to update ChannelMonitor assert!(nodes[1].node.claim_funds(preimage, &None, 9_000_000)); diff --git a/lightning/src/util/test_utils.rs b/lightning/src/util/test_utils.rs index 33b070ee..70a37479 100644 --- a/lightning/src/util/test_utils.rs +++ b/lightning/src/util/test_utils.rs @@ -16,7 +16,7 @@ use bitcoin::BitcoinHash; use bitcoin::blockdata::constants::genesis_block; use bitcoin::blockdata::transaction::Transaction; use bitcoin::blockdata::script::{Builder, Script}; -use bitcoin::blockdata::block::Block; +use bitcoin::blockdata::block::BlockHeader; use bitcoin::blockdata::opcodes; use bitcoin::network::constants::Network; use bitcoin::hash_types::{Txid, BlockHash}; @@ -399,7 +399,7 @@ impl ChainWatchInterface for TestChainWatcher { fn install_watch_tx(&self, _txid: &Txid, _script_pub_key: &Script) { } fn install_watch_outpoint(&self, _outpoint: (Txid, u32), _out_script: &Script) { } fn watch_all_txn(&self) { } - fn filter_block<'a>(&self, _block: &'a Block) -> Vec { + fn filter_block(&self, _header: &BlockHeader, _txdata: &[(usize, &Transaction)]) -> Vec { Vec::new() } fn reentered(&self) -> usize { 0 } -- 2.30.2