From a7b2eb6d98eafa84f0516c9ba8321a3f653a84bd Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Tue, 16 Jun 2020 15:10:17 -0700 Subject: [PATCH] Remove ChainWatchInterface from BlockNotifier ChainListeners should be independent of each other, but in practice this is not the case because ChainWatchInterface introduces a dependency between them. Push ChainWatchInterface down into the ChainListener implementations where needed. Update ChainListener's block_connected method to take a slice of the form &[(usize, &Transaction)] where each transaction is paired with its position within the block. --- fuzz/src/chanmon_consistency.rs | 11 +- fuzz/src/full_stack.rs | 10 +- fuzz/src/router.rs | 5 +- lightning/src/chain/chaininterface.rs | 88 ++++-------- lightning/src/ln/chanmon_update_fail_tests.rs | 10 +- lightning/src/ln/channel.rs | 12 +- lightning/src/ln/channelmanager.rs | 10 +- lightning/src/ln/channelmonitor.rs | 27 ++-- lightning/src/ln/functional_test_utils.rs | 48 ++++--- lightning/src/ln/functional_tests.rs | 132 +++++++++++------- lightning/src/ln/onion_route_tests.rs | 23 ++- lightning/src/ln/reorg_tests.rs | 34 +++-- lightning/src/util/test_utils.rs | 4 +- 13 files changed, 223 insertions(+), 191 deletions(-) diff --git a/fuzz/src/chanmon_consistency.rs b/fuzz/src/chanmon_consistency.rs index ca05e5db..661bda39 100644 --- a/fuzz/src/chanmon_consistency.rs +++ b/fuzz/src/chanmon_consistency.rs @@ -308,16 +308,11 @@ pub fn do_test(data: &[u8], out: Out) { macro_rules! confirm_txn { ($node: expr) => { { let mut header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }; - let mut txn = Vec::with_capacity(channel_txn.len()); - let mut posn = Vec::with_capacity(channel_txn.len()); - for i in 0..channel_txn.len() { - txn.push(&channel_txn[i]); - posn.push(i + 1); - } - $node.block_connected(&header, 1, &txn, &posn); + 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 { header = BlockHeader { version: 0x20000000, prev_blockhash: header.block_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }; - $node.block_connected(&header, i, &Vec::new(), &[0; 0]); + $node.block_connected(&header, &[], i); } } } } diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs index 42da4d90..98dc228d 100644 --- a/fuzz/src/full_stack.rs +++ b/fuzz/src/full_stack.rs @@ -183,15 +183,13 @@ impl<'a> MoneyLossDetector<'a> { } fn connect_block(&mut self, all_txn: &[Transaction]) { - let mut txn = Vec::with_capacity(all_txn.len()); - let mut txn_idxs = Vec::with_capacity(all_txn.len()); + 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); - txn.push(tx); - txn_idxs.push(idx + 1); + txdata.push((idx + 1, tx)); }, _ => {}, } @@ -200,8 +198,8 @@ impl<'a> MoneyLossDetector<'a> { 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(&header, self.height as u32, &txn[..], &txn_idxs[..]); - (*self.monitor).block_connected(&header, self.height as u32, &txn[..], &txn_idxs[..]); + 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] = header.block_hash(); } else { diff --git a/fuzz/src/router.rs b/fuzz/src/router.rs index ffc46083..e9880b06 100644 --- a/fuzz/src/router.rs +++ b/fuzz/src/router.rs @@ -8,7 +8,8 @@ // licenses. 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}; @@ -84,7 +85,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 f0a4b648..eb8bc33a 100644 --- a/lightning/src/chain/chaininterface.rs +++ b/lightning/src/chain/chaininterface.rs @@ -63,7 +63,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 @@ -79,22 +79,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. - /// - /// The txn_matched array should be set to references to transactions which matched the - /// relevant installed watch outpoints/txn, or the full set of transactions in the block. - /// - /// Note that if txn_matched includes only matched transactions, and a new - /// transaction/outpoint is watched during a block_connected call, the block *must* be - /// re-scanned with the new transaction/outpoints and block_connected should be called - /// again with the same header and (at least) the new transactions. - /// - /// Note that if non-new transaction/outpoints are be registered during a call, a second call - /// *must not* happen. - /// - /// This also means those counting confirmations using block_connected callbacks should watch - /// for duplicate headers and not count them towards confirmations! - fn block_connected(&self, header: &BlockHeader, height: u32, txn_matched: &[&Transaction], indexes_of_txn_matched: &[usize]); + /// 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) @@ -228,7 +216,7 @@ impl ChainWatchedUtil { /// aliases prevents issues such as overly long function definitions. /// /// (C-not exported) as we let clients handle any reference counting they need to do -pub type BlockNotifierArc = Arc, C>>; +pub type BlockNotifierArc = Arc>>; /// BlockNotifierRef is useful when you want a BlockNotifier that points to ChainListeners /// with nonstatic lifetimes. This is useful for when static lifetimes are not needed. Nonstatic @@ -237,29 +225,26 @@ pub type BlockNotifierArc = Arc, C> /// requires parameters with static lifetimes), in which case BlockNotifierArc is a more /// appropriate type. Defining these type aliases for common usages prevents issues such as /// overly long function definitions. -pub type BlockNotifierRef<'a, C> = BlockNotifier<'a, &'a ChainListener, C>; +pub type BlockNotifierRef<'a> = BlockNotifier<'a, &'a ChainListener>; -/// Utility for notifying listeners about new blocks, and handling block rescans if new watch -/// data is registered. +/// Utility for notifying listeners when blocks are connected or disconnected. /// /// Rather than using a plain BlockNotifier, it is preferable to use either a BlockNotifierArc /// or a BlockNotifierRef for conciseness. See their documentation for more details, but essentially /// you should default to using a BlockNotifierRef, and use a BlockNotifierArc instead when you /// require ChainListeners with static lifetimes, such as when you're using lightning-net-tokio. -pub struct BlockNotifier<'a, CL: Deref + 'a, C: Deref> - where CL::Target: ChainListener + 'a, C::Target: ChainWatchInterface { +pub struct BlockNotifier<'a, CL: Deref + 'a> + where CL::Target: ChainListener + 'a { listeners: Mutex>, - chain_monitor: C, phantom: PhantomData<&'a ()>, } -impl<'a, CL: Deref + 'a, C: Deref> BlockNotifier<'a, CL, C> - where CL::Target: ChainListener + 'a, C::Target: ChainWatchInterface { +impl<'a, CL: Deref + 'a> BlockNotifier<'a, CL> + where CL::Target: ChainListener + 'a { /// Constructs a new BlockNotifier without any listeners. - pub fn new(chain_monitor: C) -> BlockNotifier<'a, CL, C> { + pub fn new() -> BlockNotifier<'a, CL> { BlockNotifier { listeners: Mutex::new(Vec::new()), - chain_monitor, phantom: PhantomData, } } @@ -284,36 +269,13 @@ impl<'a, CL: Deref + 'a, C: Deref> BlockNotifier<'a, CL, C> vec.retain(|item | !ptr::eq(&(**item), &(*listener))); } - /// Notify listeners that a block was connected given a full, unfiltered block. - /// - /// Handles re-scanning the block and calling block_connected again if listeners register new - /// watch data during the callbacks for you (see ChainListener::block_connected for more info). + /// Notify listeners that a block was connected. pub fn block_connected(&self, block: &Block, height: u32) { - let mut reentered = true; - while reentered { - let matched_indexes = self.chain_monitor.filter_block(block); - let mut matched_txn = Vec::new(); - for index in matched_indexes.iter() { - matched_txn.push(&block.txdata[*index]); - } - reentered = self.block_connected_checked(&block.header, height, matched_txn.as_slice(), matched_indexes.as_slice()); - } - } - - /// Notify listeners that a block was connected, given pre-filtered list of transactions in the - /// block which matched the filter (probably using does_match_tx). - /// - /// Returns true if notified listeners registered additional watch data (implying that the - /// block must be re-scanned and this function called again prior to further block_connected - /// calls, see ChainListener::block_connected for more info). - pub fn block_connected_checked(&self, header: &BlockHeader, height: u32, txn_matched: &[&Transaction], indexes_of_txn_matched: &[usize]) -> bool { - let last_seen = self.chain_monitor.reentered(); - + let txdata: Vec<_> = block.txdata.iter().enumerate().collect(); let listeners = self.listeners.lock().unwrap(); for listener in listeners.iter() { - listener.block_connected(header, height, txn_matched, indexes_of_txn_matched); + listener.block_connected(&block.header, &txdata, height); } - return last_seen != self.chain_monitor.reentered(); } /// Notify listeners that a block was disconnected. @@ -375,24 +337,24 @@ 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 mut matched_txids = HashSet::new(); { let watched = self.watched.lock().unwrap(); - for (index, transaction) in block.txdata.iter().enumerate() { + for (index, transaction) in txdata.iter().enumerate() { // A tx matches the filter if it either matches the filter directly (via // does_match_tx_unguarded) or if it is a descendant of another matched // transaction within the same block, which we check for in the loop. - let mut matched = self.does_match_tx_unguarded(transaction, &watched); - for input in transaction.input.iter() { + let mut matched = self.does_match_tx_unguarded(transaction.1, &watched); + for input in transaction.1.input.iter() { if matched || matched_txids.contains(&input.previous_output.txid) { matched = true; break; } } if matched { - matched_txids.insert(transaction.txid()); + matched_txids.insert(transaction.1.txid()); matched_index.push(index); } } @@ -436,7 +398,7 @@ mod tests { fn register_listener_test() { let chanmon_cfgs = create_chanmon_cfgs(1); let node_cfgs = create_node_cfgs(1, &chanmon_cfgs); - let block_notifier = BlockNotifier::new(node_cfgs[0].chain_monitor); + let block_notifier = BlockNotifier::new(); assert_eq!(block_notifier.listeners.lock().unwrap().len(), 0); let listener = &node_cfgs[0].chan_monitor.simple_monitor as &ChainListener; block_notifier.register_listener(listener); @@ -450,7 +412,7 @@ mod tests { fn unregister_single_listener_test() { let chanmon_cfgs = create_chanmon_cfgs(2); let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); - let block_notifier = BlockNotifier::new(node_cfgs[0].chain_monitor); + let block_notifier = BlockNotifier::new(); let listener1 = &node_cfgs[0].chan_monitor.simple_monitor as &ChainListener; let listener2 = &node_cfgs[1].chan_monitor.simple_monitor as &ChainListener; block_notifier.register_listener(listener1); @@ -469,7 +431,7 @@ mod tests { fn unregister_single_listener_ref_test() { let chanmon_cfgs = create_chanmon_cfgs(2); let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); - let block_notifier = BlockNotifier::new(node_cfgs[0].chain_monitor); + let block_notifier = BlockNotifier::new(); block_notifier.register_listener(&node_cfgs[0].chan_monitor.simple_monitor as &ChainListener); block_notifier.register_listener(&node_cfgs[1].chan_monitor.simple_monitor as &ChainListener); let vec = block_notifier.listeners.lock().unwrap(); @@ -486,7 +448,7 @@ mod tests { fn unregister_multiple_of_the_same_listeners_test() { let chanmon_cfgs = create_chanmon_cfgs(2); let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); - let block_notifier = BlockNotifier::new(node_cfgs[0].chain_monitor); + let block_notifier = BlockNotifier::new(); let listener1 = &node_cfgs[0].chan_monitor.simple_monitor as &ChainListener; let listener2 = &node_cfgs[1].chan_monitor.simple_monitor as &ChainListener; block_notifier.register_listener(listener1); diff --git a/lightning/src/ln/chanmon_update_fail_tests.rs b/lightning/src/ln/chanmon_update_fail_tests.rs index bf2f8c69..edba2b70 100644 --- a/lightning/src/ln/chanmon_update_fail_tests.rs +++ b/lightning/src/ln/chanmon_update_fail_tests.rs @@ -1756,11 +1756,11 @@ fn do_during_funding_monitor_fail(confirm_a_first: bool, restore_b_before_conf: }; if confirm_a_first { - confirm_transaction(&nodes[0].block_notifier, &nodes[0].chain_monitor, &funding_tx, funding_tx.version); + confirm_transaction(&nodes[0].block_notifier, &funding_tx); nodes[1].node.handle_funding_locked(&nodes[0].node.get_our_node_id(), &get_event_msg!(nodes[0], MessageSendEvent::SendFundingLocked, nodes[1].node.get_our_node_id())); } else { assert!(!restore_b_before_conf); - confirm_transaction(&nodes[1].block_notifier, &nodes[1].chain_monitor, &funding_tx, funding_tx.version); + confirm_transaction(&nodes[1].block_notifier, &funding_tx); assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty()); } @@ -1772,7 +1772,7 @@ fn do_during_funding_monitor_fail(confirm_a_first: bool, restore_b_before_conf: assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty()); if !restore_b_before_conf { - confirm_transaction(&nodes[1].block_notifier, &nodes[1].chain_monitor, &funding_tx, funding_tx.version); + confirm_transaction(&nodes[1].block_notifier, &funding_tx); assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty()); assert!(nodes[1].node.get_and_clear_pending_events().is_empty()); } @@ -1785,12 +1785,12 @@ fn do_during_funding_monitor_fail(confirm_a_first: bool, restore_b_before_conf: let (channel_id, (announcement, as_update, bs_update)) = if !confirm_a_first { nodes[0].node.handle_funding_locked(&nodes[1].node.get_our_node_id(), &get_event_msg!(nodes[1], MessageSendEvent::SendFundingLocked, nodes[0].node.get_our_node_id())); - confirm_transaction(&nodes[0].block_notifier, &nodes[0].chain_monitor, &funding_tx, funding_tx.version); + confirm_transaction(&nodes[0].block_notifier, &funding_tx); let (funding_locked, channel_id) = create_chan_between_nodes_with_value_confirm_second(&nodes[1], &nodes[0]); (channel_id, create_chan_between_nodes_with_value_b(&nodes[0], &nodes[1], &funding_locked)) } else { if restore_b_before_conf { - confirm_transaction(&nodes[1].block_notifier, &nodes[1].chain_monitor, &funding_tx, funding_tx.version); + confirm_transaction(&nodes[1].block_notifier, &funding_tx); } let (funding_locked, channel_id) = create_chan_between_nodes_with_value_confirm_second(&nodes[0], &nodes[1]); (channel_id, create_chan_between_nodes_with_value_b(&nodes[1], &nodes[0], &funding_locked)) diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index dd9170b4..5ce39592 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -3315,7 +3315,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, header: &BlockHeader, height: u32, txn_matched: &[&Transaction], indexes_of_txn_matched: &[usize]) -> 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 { @@ -3335,7 +3335,7 @@ impl Channel { } } if non_shutdown_state & !(ChannelState::TheirFundingLocked as u32) == ChannelState::FundingSent as u32 { - for (ref tx, index_in_block) in txn_matched.iter().zip(indexes_of_txn_matched) { + 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() || @@ -3366,14 +3366,14 @@ impl Channel { } } } - if height > 0xff_ff_ff || (*index_in_block) > 0xff_ff_ff { + if height > 0xff_ff_ff || (index_in_block) > 0xff_ff_ff { panic!("Block was bogus - either height 16 million or had > 16 million transactions"); } assert!(txo_idx <= 0xffff); // txo_idx is a (u16 as usize), so this is just listed here for completeness self.funding_tx_confirmations = 1; - self.short_channel_id = Some(((height as u64) << (5*8)) | - ((*index_in_block as u64) << (2*8)) | - ((txo_idx as u64) << (0*8))); + self.short_channel_id = Some(((height as u64) << (5*8)) | + ((index_in_block as u64) << (2*8)) | + ((txo_idx as u64) << (0*8))); } } } diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 59d513b3..e4ba3541 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -18,8 +18,8 @@ //! imply it needs to fail HTLCs/payments/channels it manages). use bitcoin::blockdata::block::BlockHeader; -use bitcoin::blockdata::transaction::Transaction; use bitcoin::blockdata::constants::genesis_block; +use bitcoin::blockdata::transaction::Transaction; use bitcoin::network::constants::Network; use bitcoin::hashes::{Hash, HashEngine}; @@ -3062,9 +3062,9 @@ impl = matched_indexes.iter().map(|index| txdata[*index].1).collect(); + let last_seen = self.chain_monitor.reentered(); + let block_hash = header.block_hash(); + { + let mut monitors = self.monitors.lock().unwrap(); + for monitor in monitors.values_mut() { + let txn_outputs = monitor.block_connected(&matched_txn, height, &block_hash, &*self.broadcaster, &*self.fee_estimator, &*self.logger); + + for (ref txid, ref outputs) in txn_outputs { + for (idx, output) in outputs.iter().enumerate() { + self.chain_monitor.install_watch_outpoint((txid.clone(), idx as u32), &output.script_pubkey); + } } } } + reentered = last_seen != self.chain_monitor.reentered(); } } diff --git a/lightning/src/ln/functional_test_utils.rs b/lightning/src/ln/functional_test_utils.rs index bcd7fe2d..403bac57 100644 --- a/lightning/src/ln/functional_test_utils.rs +++ b/lightning/src/ln/functional_test_utils.rs @@ -27,7 +27,7 @@ use util::errors::APIError; use util::config::UserConfig; use util::ser::{ReadableArgs, Writeable, Readable}; -use bitcoin::blockdata::block::BlockHeader; +use bitcoin::blockdata::block::{Block, BlockHeader}; use bitcoin::blockdata::transaction::{Transaction, TxOut}; use bitcoin::network::constants::Network; @@ -44,24 +44,38 @@ use std::mem; use std::collections::HashMap; pub const CHAN_CONFIRM_DEPTH: u32 = 100; -pub fn confirm_transaction<'a, 'b: 'a>(notifier: &'a chaininterface::BlockNotifierRef<'b, &chaininterface::ChainWatchInterfaceUtil>, chain: &chaininterface::ChainWatchInterfaceUtil, tx: &Transaction, chan_id: i32) { - assert!(chain.does_match_tx(tx)); - let mut header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }; - notifier.block_connected_checked(&header, 1, &[tx; 1], &[chan_id as usize; 1]); +pub fn confirm_transaction<'a, 'b: 'a>(notifier: &'a chaininterface::BlockNotifierRef<'b>, tx: &Transaction) { + let dummy_tx = Transaction { version: 0, lock_time: 0, input: Vec::new(), output: Vec::new() }; + let dummy_tx_count = tx.version as usize; + let mut block = Block { + header: BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }, + txdata: vec![dummy_tx; dummy_tx_count], + }; + block.txdata.push(tx.clone()); + notifier.block_connected(&block, 1); for i in 2..CHAN_CONFIRM_DEPTH { - header = BlockHeader { version: 0x20000000, prev_blockhash: header.block_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }; - notifier.block_connected_checked(&header, i, &vec![], &[0; 0]); + block = Block { + header: BlockHeader { version: 0x20000000, prev_blockhash: block.header.block_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }, + txdata: vec![], + }; + notifier.block_connected(&block, i); } } -pub fn connect_blocks<'a, 'b>(notifier: &'a chaininterface::BlockNotifierRef<'b, &chaininterface::ChainWatchInterfaceUtil>, depth: u32, height: u32, parent: bool, prev_blockhash: BlockHash) -> BlockHash { - let mut header = BlockHeader { version: 0x2000000, prev_blockhash: if parent { prev_blockhash } else { Default::default() }, merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }; - notifier.block_connected_checked(&header, height + 1, &Vec::new(), &Vec::new()); +pub fn connect_blocks<'a, 'b>(notifier: &'a chaininterface::BlockNotifierRef<'b>, depth: u32, height: u32, parent: bool, prev_blockhash: BlockHash) -> BlockHash { + let mut block = Block { + header: BlockHeader { version: 0x2000000, prev_blockhash: if parent { prev_blockhash } else { Default::default() }, merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }, + txdata: vec![], + }; + notifier.block_connected(&block, height + 1); for i in 2..depth + 1 { - header = BlockHeader { version: 0x20000000, prev_blockhash: header.block_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }; - notifier.block_connected_checked(&header, height + i, &Vec::new(), &Vec::new()); + block = Block { + header: BlockHeader { version: 0x20000000, prev_blockhash: block.header.block_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }, + txdata: vec![], + }; + notifier.block_connected(&block, height + i); } - header.block_hash() + block.header.block_hash() } pub struct TestChanMonCfg { @@ -82,7 +96,7 @@ pub struct NodeCfg<'a> { } pub struct Node<'a, 'b: 'a, 'c: 'b> { - pub block_notifier: chaininterface::BlockNotifierRef<'a, &'c chaininterface::ChainWatchInterfaceUtil>, + pub block_notifier: chaininterface::BlockNotifierRef<'a>, pub chain_monitor: &'c chaininterface::ChainWatchInterfaceUtil, pub tx_broadcaster: &'c test_utils::TestBroadcaster, pub chan_monitor: &'b test_utils::TestChannelMonitor<'c>, @@ -373,7 +387,7 @@ pub fn create_chan_between_nodes_with_value_init<'a, 'b, 'c>(node_a: &Node<'a, ' } pub fn create_chan_between_nodes_with_value_confirm_first<'a, 'b, 'c, 'd>(node_recv: &'a Node<'b, 'c, 'c>, node_conf: &'a Node<'b, 'c, 'd>, tx: &Transaction) { - confirm_transaction(&node_conf.block_notifier, &node_conf.chain_monitor, &tx, tx.version); + confirm_transaction(&node_conf.block_notifier, &tx); node_recv.node.handle_funding_locked(&node_conf.node.get_our_node_id(), &get_event_msg!(node_conf, MessageSendEvent::SendFundingLocked, node_recv.node.get_our_node_id())); } @@ -399,7 +413,7 @@ pub fn create_chan_between_nodes_with_value_confirm_second<'a, 'b, 'c>(node_recv pub fn create_chan_between_nodes_with_value_confirm<'a, 'b, 'c, 'd>(node_a: &'a Node<'b, 'c, 'd>, node_b: &'a Node<'b, 'c, 'd>, tx: &Transaction) -> ((msgs::FundingLocked, msgs::AnnouncementSignatures), [u8; 32]) { create_chan_between_nodes_with_value_confirm_first(node_a, node_b, tx); - confirm_transaction(&node_a.block_notifier, &node_a.chain_monitor, &tx, tx.version); + confirm_transaction(&node_a.block_notifier, &tx); create_chan_between_nodes_with_value_confirm_second(node_b, node_a) } @@ -1108,7 +1122,7 @@ pub fn create_network<'a, 'b: 'a, 'c: 'b>(node_count: usize, cfgs: &'b Vec(_name: &str, test_case: { // reset block height - let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }; + let block = Block { + header: BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }, + txdata: vec![], + }; for ix in 0..nodes.len() { - nodes[ix].block_notifier.block_connected_checked(&header, 1, &[], &[]); + nodes[ix].block_notifier.block_connected(&block, 1); } macro_rules! expect_event { @@ -440,9 +443,12 @@ fn test_onion_failure() { run_onion_failure_test("expiry_too_soon", 0, &nodes, &route, &payment_hash, |msg| { let height = msg.cltv_expiry - CLTV_CLAIM_BUFFER - LATENCY_GRACE_PERIOD_BLOCKS + 1; - let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }; + let block = Block { + header: BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }, + txdata: vec![], + }; - nodes[1].block_notifier.block_connected_checked(&header, height, &[], &[]); + nodes[1].block_notifier.block_connected(&block, height); }, ||{}, true, Some(UPDATE|14), Some(msgs::HTLCFailChannelUpdate::ChannelUpdateMessage{msg: ChannelUpdate::dummy()})); run_onion_failure_test("unknown_payment_hash", 2, &nodes, &route, &payment_hash, |_| {}, || { @@ -451,9 +457,12 @@ fn test_onion_failure() { run_onion_failure_test("final_expiry_too_soon", 1, &nodes, &route, &payment_hash, |msg| { let height = msg.cltv_expiry - CLTV_CLAIM_BUFFER - LATENCY_GRACE_PERIOD_BLOCKS + 1; - let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }; + let block = Block { + header: BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }, + txdata: vec![], + }; - nodes[2].block_notifier.block_connected_checked(&header, height, &[], &[]); + nodes[2].block_notifier.block_connected(&block, height); }, || {}, true, Some(17), None); run_onion_failure_test("final_incorrect_cltv_expiry", 1, &nodes, &route, &payment_hash, |_| {}, || { diff --git a/lightning/src/ln/reorg_tests.rs b/lightning/src/ln/reorg_tests.rs index cb274b5e..d960727d 100644 --- a/lightning/src/ln/reorg_tests.rs +++ b/lightning/src/ln/reorg_tests.rs @@ -51,8 +51,7 @@ fn do_test_onchain_htlc_reorg(local_commitment: bool, claim: bool) { check_added_monitors!(nodes[2], 1); get_htlc_update_msgs!(nodes[2], nodes[1].node.get_our_node_id()); - let mut headers = Vec::new(); - let mut header = BlockHeader { version: 0x2000_0000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }; + let header = BlockHeader { version: 0x2000_0000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }; let claim_txn = if local_commitment { // Broadcast node 1 commitment txn to broadcast the HTLC-Timeout let node_1_commitment_txn = get_local_commitment_txn!(nodes[1], chan_2.2); @@ -101,33 +100,44 @@ fn do_test_onchain_htlc_reorg(local_commitment: bool, claim: bool) { }; check_added_monitors!(nodes[1], 1); check_closed_broadcast!(nodes[1], false); // We should get a BroadcastChannelUpdate (and *only* a BroadcstChannelUpdate) - headers.push(header.clone()); + let mut block = Block { header, txdata: vec![] }; + let mut blocks = Vec::new(); + blocks.push(block.clone()); // At CHAN_CONFIRM_DEPTH + 1 we have a confirmation count of 1, so CHAN_CONFIRM_DEPTH + // ANTI_REORG_DELAY - 1 will give us a confirmation count of ANTI_REORG_DELAY - 1. for i in CHAN_CONFIRM_DEPTH + 2..CHAN_CONFIRM_DEPTH + ANTI_REORG_DELAY - 1 { - header = BlockHeader { version: 0x20000000, prev_blockhash: header.block_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }; - nodes[1].block_notifier.block_connected_checked(&header, i, &vec![], &[0; 0]); - headers.push(header.clone()); + block = Block { + header: BlockHeader { version: 0x20000000, prev_blockhash: block.block_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }, + txdata: vec![], + }; + nodes[1].block_notifier.block_connected(&block, i); + blocks.push(block.clone()); } check_added_monitors!(nodes[1], 0); assert_eq!(nodes[1].node.get_and_clear_pending_events().len(), 0); if claim { // Now reorg back to CHAN_CONFIRM_DEPTH and confirm node 2's broadcasted transactions: - for (height, header) in (CHAN_CONFIRM_DEPTH + 1..CHAN_CONFIRM_DEPTH + ANTI_REORG_DELAY - 1).zip(headers.iter()).rev() { - nodes[1].block_notifier.block_disconnected(&header, height); + for (height, block) in (CHAN_CONFIRM_DEPTH + 1..CHAN_CONFIRM_DEPTH + ANTI_REORG_DELAY - 1).zip(blocks.iter()).rev() { + nodes[1].block_notifier.block_disconnected(&block.header, height); } - header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }; - nodes[1].block_notifier.block_connected(&Block { header, txdata: claim_txn }, CHAN_CONFIRM_DEPTH + 1); + block = Block { + header: BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }, + txdata: claim_txn, + }; + nodes[1].block_notifier.block_connected(&block, CHAN_CONFIRM_DEPTH + 1); // ChannelManager only polls ManyChannelMonitor::get_and_clear_pending_monitor_events when we // probe it for events, so we probe non-message events here (which should still end up empty): assert_eq!(nodes[1].node.get_and_clear_pending_events().len(), 0); } else { // Confirm the timeout tx and check that we fail the HTLC backwards - header = BlockHeader { version: 0x20000000, prev_blockhash: header.block_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }; - nodes[1].block_notifier.block_connected_checked(&header, CHAN_CONFIRM_DEPTH + ANTI_REORG_DELAY, &vec![], &[0; 0]); + block = Block { + header: BlockHeader { version: 0x20000000, prev_blockhash: block.block_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }, + txdata: vec![], + }; + nodes[1].block_notifier.block_connected(&block, CHAN_CONFIRM_DEPTH + ANTI_REORG_DELAY); expect_pending_htlcs_forwardable!(nodes[1]); } diff --git a/lightning/src/util/test_utils.rs b/lightning/src/util/test_utils.rs index bdd4620c..07dcb0b3 100644 --- a/lightning/src/util/test_utils.rs +++ b/lightning/src/util/test_utils.rs @@ -24,7 +24,7 @@ use util::ser::{Readable, Writer, Writeable}; 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}; @@ -408,7 +408,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