From 3ee6a27bc6cef28ba52059a405913ca5c6e1dcb7 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Fri, 17 Jul 2020 22:08:34 -0700 Subject: [PATCH] Replace ChainWatchInterface in NetGraphMsgHandler ChainWatchInterface was intended as an interface for watching rather than accessing the chain. Remove get_chain_utxo and add chain::Access trait for this behavior. Wrap it with an Option in NetGraphMsgHandler in order to simplify the error interface. --- ARCH.md | 6 +- fuzz/src/full_stack.rs | 8 +- fuzz/src/router.rs | 45 +++++------ lightning-net-tokio/src/lib.rs | 4 +- lightning/src/chain/mod.rs | 26 +++++- lightning/src/ln/functional_test_utils.rs | 20 ++--- lightning/src/ln/functional_tests.rs | 10 +-- lightning/src/routing/network_graph.rs | 97 ++++++++++++----------- lightning/src/routing/router.rs | 12 ++- lightning/src/util/test_utils.rs | 38 ++++----- 10 files changed, 141 insertions(+), 125 deletions(-) diff --git a/ARCH.md b/ARCH.md index 3e4aba324..061c0ba4f 100644 --- a/ARCH.md +++ b/ARCH.md @@ -51,9 +51,9 @@ At a high level, some of the common interfaces fit together as follows: --------------- / (as EventsProvider) | PeerManager |- \ / --------------- \ / - | ----------------------- \ / - | | ChainWatchInterface | v - | ----------------------- --------- + | ----------------- \ / + | | chain::Access | v + | ----------------- --------- | | | Event | (as RoutingMessageHandler) v --------- \ -------------------- diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs index d37e961bf..61458e241 100644 --- a/fuzz/src/full_stack.rs +++ b/fuzz/src/full_stack.rs @@ -25,7 +25,8 @@ use bitcoin::hashes::HashEngine as TraitImportEngine; use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::hash_types::{Txid, BlockHash, WPubkeyHash}; -use lightning::chain::chaininterface::{BroadcasterInterface,ConfirmationTarget,ChainListener,FeeEstimator,ChainWatchInterfaceUtil}; +use lightning::chain; +use lightning::chain::chaininterface::{BroadcasterInterface,ConfirmationTarget,ChainListener,FeeEstimator}; use lightning::chain::transaction::OutPoint; use lightning::chain::keysinterface::{InMemoryChannelKeys, KeysInterface}; use lightning::ln::channelmonitor; @@ -146,7 +147,7 @@ type ChannelMan = ChannelManager< EnforcingChannelKeys, Arc, Arc, Arc>>, Arc, Arc, Arc, Arc>; -type PeerMan<'a> = PeerManager, Arc, Arc, Arc>>, Arc>; +type PeerMan<'a> = PeerManager, Arc, Arc, Arc>>, Arc>; struct MoneyLossDetector<'a> { manager: Arc, @@ -332,7 +333,6 @@ pub fn do_test(data: &[u8], logger: &Arc) { Err(_) => return, }; - let watch = Arc::new(ChainWatchInterfaceUtil::new(Network::Bitcoin)); let broadcast = Arc::new(TestBroadcaster{}); let monitor = Arc::new(channelmonitor::SimpleManyChannelMonitor::new(broadcast.clone(), Arc::clone(&logger), fee_est.clone())); @@ -343,7 +343,7 @@ pub fn do_test(data: &[u8], logger: &Arc) { config.peer_channel_config_limits.min_dust_limit_satoshis = 0; let channelmanager = Arc::new(ChannelManager::new(Network::Bitcoin, fee_est.clone(), monitor.clone(), broadcast.clone(), Arc::clone(&logger), keys_manager.clone(), config, 0)); let our_id = PublicKey::from_secret_key(&Secp256k1::signing_only(), &keys_manager.get_node_secret()); - let net_graph_msg_handler = Arc::new(NetGraphMsgHandler::new(watch.clone(), Arc::clone(&logger))); + let net_graph_msg_handler = Arc::new(NetGraphMsgHandler::new(None, Arc::clone(&logger))); let peers = RefCell::new([false; 256]); let mut loss_detector = MoneyLossDetector::new(&peers, channelmanager.clone(), monitor.clone(), PeerManager::new(MessageHandler { diff --git a/fuzz/src/router.rs b/fuzz/src/router.rs index e9880b06c..5f2114b64 100644 --- a/fuzz/src/router.rs +++ b/fuzz/src/router.rs @@ -7,12 +7,11 @@ // You may not use this file except in accordance with one or both of these // licenses. -use bitcoin::blockdata::script::{Script, Builder}; -use bitcoin::blockdata::block::BlockHeader; -use bitcoin::blockdata::transaction::Transaction; -use bitcoin::hash_types::{Txid, BlockHash}; +use bitcoin::blockdata::script::Builder; +use bitcoin::blockdata::transaction::TxOut; +use bitcoin::hash_types::BlockHash; -use lightning::chain::chaininterface::{ChainError,ChainWatchInterface}; +use lightning::chain; use lightning::ln::channelmanager::ChannelDetails; use lightning::ln::features::InitFeatures; use lightning::ln::msgs; @@ -77,26 +76,16 @@ impl InputData { } } -struct DummyChainWatcher { +struct FuzzChainSource { input: Arc, } - -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, _header: &BlockHeader, _txdata: &[(usize, &Transaction)]) -> Vec { - Vec::new() - } - fn reentered(&self) -> usize { 0 } - - fn get_chain_utxo(&self, _genesis_hash: BlockHash, _unspent_tx_output_identifier: u64) -> Result<(Script, u64), ChainError> { +impl chain::Access for FuzzChainSource { + fn get_utxo(&self, _genesis_hash: &BlockHash, _short_channel_id: u64) -> Result { match self.input.get_slice(2) { - Some(&[0, _]) => Err(ChainError::NotSupported), - Some(&[1, _]) => Err(ChainError::NotWatched), - Some(&[2, _]) => Err(ChainError::UnknownTx), - Some(&[_, x]) => Ok((Builder::new().push_int(x as i64).into_script().to_v0_p2wsh(), 0)), - None => Err(ChainError::UnknownTx), + Some(&[0, _]) => Err(chain::AccessError::UnknownChain), + Some(&[1, _]) => Err(chain::AccessError::UnknownTx), + Some(&[_, x]) => Ok(TxOut { value: 0, script_pubkey: Builder::new().push_int(x as i64).into_script().to_v0_p2wsh() }), + None => Err(chain::AccessError::UnknownTx), _ => unreachable!(), } } @@ -161,12 +150,16 @@ pub fn do_test(data: &[u8], out: Out) { } let logger: Arc = Arc::new(test_logger::TestLogger::new("".to_owned(), out)); - let chain_monitor = Arc::new(DummyChainWatcher { - input: Arc::clone(&input), - }); + let chain_source = if get_slice!(1)[0] % 2 == 0 { + None + } else { + Some(Arc::new(FuzzChainSource { + input: Arc::clone(&input), + })) + }; let our_pubkey = get_pubkey!(); - let net_graph_msg_handler = NetGraphMsgHandler::new(chain_monitor, Arc::clone(&logger)); + let net_graph_msg_handler = NetGraphMsgHandler::new(chain_source, Arc::clone(&logger)); loop { match get_slice!(1)[0] { diff --git a/lightning-net-tokio/src/lib.rs b/lightning-net-tokio/src/lib.rs index 2ab2e7d4a..0757ffd58 100644 --- a/lightning-net-tokio/src/lib.rs +++ b/lightning-net-tokio/src/lib.rs @@ -34,10 +34,10 @@ //! type TxBroadcaster = dyn lightning::chain::chaininterface::BroadcasterInterface; //! type FeeEstimator = dyn lightning::chain::chaininterface::FeeEstimator; //! type Logger = dyn lightning::util::logger::Logger; -//! type ChainWatchInterface = dyn lightning::chain::chaininterface::ChainWatchInterface; +//! type ChainAccess = dyn lightning::chain::Access; //! type ChannelMonitor = lightning::ln::channelmonitor::SimpleManyChannelMonitor, Arc, Arc>; //! type ChannelManager = lightning::ln::channelmanager::SimpleArcChannelManager; -//! type PeerManager = lightning::ln::peer_handler::SimpleArcPeerManager; +//! type PeerManager = lightning::ln::peer_handler::SimpleArcPeerManager; //! //! // Connect to node with pubkey their_node_id at addr: //! async fn connect_to_node(peer_manager: PeerManager, channel_monitor: Arc, channel_manager: ChannelManager, their_node_id: PublicKey, addr: SocketAddr) { diff --git a/lightning/src/chain/mod.rs b/lightning/src/chain/mod.rs index bbdca2135..d0b5b6501 100644 --- a/lightning/src/chain/mod.rs +++ b/lightning/src/chain/mod.rs @@ -10,7 +10,8 @@ //! Structs and traits which allow other parts of rust-lightning to interact with the blockchain. use bitcoin::blockdata::script::Script; -use bitcoin::hash_types::Txid; +use bitcoin::blockdata::transaction::TxOut; +use bitcoin::hash_types::{BlockHash, Txid}; use chain::transaction::OutPoint; @@ -18,6 +19,29 @@ pub mod chaininterface; pub mod transaction; pub mod keysinterface; +/// The `Access` trait defines behavior for accessing chain data and state, such as blocks and +/// UTXOs. +pub trait Access: Send + Sync { + /// Returns the transaction output of a funding transaction encoded by [`short_channel_id`]. + /// Returns an error if `genesis_hash` is for a different chain or if such a transaction output + /// is unknown. + /// + /// [`short_channel_id`]: https://github.com/lightningnetwork/lightning-rfc/blob/master/07-routing-gossip.md#definition-of-short_channel_id + fn get_utxo(&self, genesis_hash: &BlockHash, short_channel_id: u64) -> Result; +} + +/// An error when accessing the chain via [`Access`]. +/// +/// [`Access`]: trait.Access.html +#[derive(Clone)] +pub enum AccessError { + /// The requested chain is unknown. + UnknownChain, + + /// The requested transaction doesn't exist or hasn't confirmed. + UnknownTx, +} + /// An interface for providing [`WatchEvent`]s. /// /// [`WatchEvent`]: enum.WatchEvent.html diff --git a/lightning/src/ln/functional_test_utils.rs b/lightning/src/ln/functional_test_utils.rs index f9804f4b7..d24800013 100644 --- a/lightning/src/ln/functional_test_utils.rs +++ b/lightning/src/ln/functional_test_utils.rs @@ -111,12 +111,12 @@ pub fn disconnect_block<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, header: &Blo pub struct TestChanMonCfg { pub tx_broadcaster: test_utils::TestBroadcaster, pub fee_estimator: test_utils::TestFeeEstimator, - pub chain_monitor: chaininterface::ChainWatchInterfaceUtil, + pub chain_source: test_utils::TestChainSource, pub logger: test_utils::TestLogger, } pub struct NodeCfg<'a> { - pub chain_monitor: &'a chaininterface::ChainWatchInterfaceUtil, + pub chain_source: &'a test_utils::TestChainSource, pub tx_broadcaster: &'a test_utils::TestBroadcaster, pub fee_estimator: &'a test_utils::TestFeeEstimator, pub chan_monitor: test_utils::TestChannelMonitor<'a>, @@ -127,12 +127,12 @@ pub struct NodeCfg<'a> { pub struct Node<'a, 'b: 'a, 'c: 'b> { pub block_notifier: chaininterface::BlockNotifierRef<'a>, - pub chain_monitor: &'c chaininterface::ChainWatchInterfaceUtil, + pub chain_source: &'c test_utils::TestChainSource, pub tx_broadcaster: &'c test_utils::TestBroadcaster, pub chan_monitor: &'b test_utils::TestChannelMonitor<'c>, pub keys_manager: &'b test_utils::TestKeysInterface, pub node: &'a ChannelManager, &'c test_utils::TestBroadcaster, &'b test_utils::TestKeysInterface, &'c test_utils::TestFeeEstimator, &'c test_utils::TestLogger>, - pub net_graph_msg_handler: NetGraphMsgHandler<&'c chaininterface::ChainWatchInterfaceUtil, &'c test_utils::TestLogger>, + pub net_graph_msg_handler: NetGraphMsgHandler<&'c test_utils::TestChainSource, &'c test_utils::TestLogger>, pub node_seed: [u8; 32], pub network_payment_count: Rc>, pub network_chan_count: Rc>, @@ -155,7 +155,7 @@ impl<'a, 'b, 'c> Drop for Node<'a, 'b, 'c> { let network_graph_deser = ::read(&mut ::std::io::Cursor::new(&w.0)).unwrap(); assert!(network_graph_deser == *self.net_graph_msg_handler.network_graph.read().unwrap()); let net_graph_msg_handler = NetGraphMsgHandler::from_net_graph( - self.chain_monitor, self.logger, network_graph_deser + Some(self.chain_source), self.logger, network_graph_deser ); let mut chan_progress = 0; loop { @@ -1107,9 +1107,9 @@ pub fn create_chanmon_cfgs(node_count: usize) -> Vec { for i in 0..node_count { let tx_broadcaster = test_utils::TestBroadcaster{txn_broadcasted: Mutex::new(Vec::new())}; let fee_estimator = test_utils::TestFeeEstimator { sat_per_kw: 253 }; - let chain_monitor = chaininterface::ChainWatchInterfaceUtil::new(Network::Testnet); + let chain_source = test_utils::TestChainSource::new(Network::Testnet); let logger = test_utils::TestLogger::with_id(format!("node {}", i)); - chan_mon_cfgs.push(TestChanMonCfg{ tx_broadcaster, fee_estimator, chain_monitor, logger }); + chan_mon_cfgs.push(TestChanMonCfg{ tx_broadcaster, fee_estimator, chain_source, logger }); } chan_mon_cfgs @@ -1122,7 +1122,7 @@ pub fn create_node_cfgs<'a>(node_count: usize, chanmon_cfgs: &'a Vec(node_count: usize, cfgs: &'b Vec)>::read(&mut ::std::io::Cursor::new(previous_chan_monitor_state.0)).unwrap().1; - chain_monitor = ChainWatchInterfaceUtil::new(Network::Testnet); + chain_source = test_utils::TestChainSource::new(Network::Testnet); tx_broadcaster = test_utils::TestBroadcaster{txn_broadcasted: Mutex::new(Vec::new())}; fee_estimator = test_utils::TestFeeEstimator { sat_per_kw: 253 }; keys_manager = test_utils::TestKeysInterface::new(&nodes[0].node_seed, Network::Testnet); @@ -7515,7 +7515,7 @@ fn test_data_loss_protect() { nodes[0].node = &node_state_0; assert!(monitor.add_monitor(OutPoint { txid: chan.3.txid(), index: 0 }, chan_monitor).is_ok()); nodes[0].chan_monitor = &monitor; - nodes[0].chain_monitor = &chain_monitor; + nodes[0].chain_source = &chain_source; nodes[0].block_notifier = BlockNotifier::new(); nodes[0].block_notifier.register_listener(&nodes[0].chan_monitor.simple_monitor); diff --git a/lightning/src/routing/network_graph.rs b/lightning/src/routing/network_graph.rs index 1a4a8c1a6..cafe4fc10 100644 --- a/lightning/src/routing/network_graph.rs +++ b/lightning/src/routing/network_graph.rs @@ -16,9 +16,11 @@ use bitcoin::secp256k1; use bitcoin::hashes::sha256d::Hash as Sha256dHash; use bitcoin::hashes::Hash; use bitcoin::blockdata::script::Builder; +use bitcoin::blockdata::transaction::TxOut; use bitcoin::blockdata::opcodes; -use chain::chaininterface::{ChainError, ChainWatchInterface}; +use chain; +use chain::Access; use ln::features::{ChannelFeatures, NodeFeatures}; use ln::msgs::{DecodeError, ErrorAction, LightningError, RoutingMessageHandler, NetAddress, MAX_VALUE_MSAT}; use ln::msgs::{ChannelAnnouncement, ChannelUpdate, NodeAnnouncement, OptionalField}; @@ -51,22 +53,22 @@ pub struct LockedNetworkGraph<'a>(pub RwLockReadGuard<'a, NetworkGraph>); /// This network graph is then used for routing payments. /// Provides interface to help with initial routing sync by /// serving historical announcements. -pub struct NetGraphMsgHandler where C::Target: ChainWatchInterface, L::Target: Logger { +pub struct NetGraphMsgHandler where C::Target: chain::Access, L::Target: Logger { secp_ctx: Secp256k1, /// Representation of the payment channel network pub network_graph: RwLock, - chain_monitor: C, + chain_access: Option, full_syncs_requested: AtomicUsize, logger: L, } -impl NetGraphMsgHandler where C::Target: ChainWatchInterface, L::Target: Logger { +impl NetGraphMsgHandler where C::Target: chain::Access, L::Target: Logger { /// Creates a new tracker of the actual state of the network of channels and nodes, /// assuming a fresh network graph. /// Chain monitor is used to make sure announced channels exist on-chain, /// channel data is correct, and that the announcement is signed with /// channel owners' keys. - pub fn new(chain_monitor: C, logger: L) -> Self { + pub fn new(chain_access: Option, logger: L) -> Self { NetGraphMsgHandler { secp_ctx: Secp256k1::verification_only(), network_graph: RwLock::new(NetworkGraph { @@ -74,19 +76,19 @@ impl NetGraphMsgHandler where C::Target: ChainWatchInt nodes: BTreeMap::new(), }), full_syncs_requested: AtomicUsize::new(0), - chain_monitor, + chain_access, logger, } } /// Creates a new tracker of the actual state of the network of channels and nodes, /// assuming an existing Network Graph. - pub fn from_net_graph(chain_monitor: C, logger: L, network_graph: NetworkGraph) -> Self { + pub fn from_net_graph(chain_access: Option, logger: L, network_graph: NetworkGraph) -> Self { NetGraphMsgHandler { secp_ctx: Secp256k1::verification_only(), network_graph: RwLock::new(network_graph), full_syncs_requested: AtomicUsize::new(0), - chain_monitor, + chain_access, logger, } } @@ -117,7 +119,7 @@ macro_rules! secp_verify_sig { }; } -impl RoutingMessageHandler for NetGraphMsgHandler where C::Target: ChainWatchInterface, L::Target: Logger { +impl RoutingMessageHandler for NetGraphMsgHandler where C::Target: chain::Access, L::Target: Logger { fn handle_node_announcement(&self, msg: &msgs::NodeAnnouncement) -> Result { self.network_graph.write().unwrap().update_node_from_announcement(msg, Some(&self.secp_ctx)) } @@ -127,29 +129,33 @@ impl RoutingMessageHandler for N return Err(LightningError{err: "Channel announcement node had a channel with itself".to_owned(), action: ErrorAction::IgnoreError}); } - let utxo_value = match self.chain_monitor.get_chain_utxo(msg.contents.chain_hash, msg.contents.short_channel_id) { - Ok((script_pubkey, value)) => { - let expected_script = Builder::new().push_opcode(opcodes::all::OP_PUSHNUM_2) - .push_slice(&msg.contents.bitcoin_key_1.serialize()) - .push_slice(&msg.contents.bitcoin_key_2.serialize()) - .push_opcode(opcodes::all::OP_PUSHNUM_2) - .push_opcode(opcodes::all::OP_CHECKMULTISIG).into_script().to_v0_p2wsh(); - if script_pubkey != expected_script { - return Err(LightningError{err: format!("Channel announcement key ({}) didn't match on-chain script ({})", script_pubkey.to_hex(), expected_script.to_hex()), action: ErrorAction::IgnoreError}); - } - //TODO: Check if value is worth storing, use it to inform routing, and compare it - //to the new HTLC max field in channel_update - Some(value) - }, - Err(ChainError::NotSupported) => { + let utxo_value = match &self.chain_access { + &None => { // Tentatively accept, potentially exposing us to DoS attacks None }, - Err(ChainError::NotWatched) => { - return Err(LightningError{err: format!("Channel announced on an unknown chain ({})", msg.contents.chain_hash.encode().to_hex()), action: ErrorAction::IgnoreError}); - }, - Err(ChainError::UnknownTx) => { - return Err(LightningError{err: "Channel announced without corresponding UTXO entry".to_owned(), action: ErrorAction::IgnoreError}); + &Some(ref chain_access) => { + match chain_access.get_utxo(&msg.contents.chain_hash, msg.contents.short_channel_id) { + Ok(TxOut { value, script_pubkey }) => { + let expected_script = Builder::new().push_opcode(opcodes::all::OP_PUSHNUM_2) + .push_slice(&msg.contents.bitcoin_key_1.serialize()) + .push_slice(&msg.contents.bitcoin_key_2.serialize()) + .push_opcode(opcodes::all::OP_PUSHNUM_2) + .push_opcode(opcodes::all::OP_CHECKMULTISIG).into_script().to_v0_p2wsh(); + if script_pubkey != expected_script { + return Err(LightningError{err: format!("Channel announcement key ({}) didn't match on-chain script ({})", script_pubkey.to_hex(), expected_script.to_hex()), action: ErrorAction::IgnoreError}); + } + //TODO: Check if value is worth storing, use it to inform routing, and compare it + //to the new HTLC max field in channel_update + Some(value) + }, + Err(chain::AccessError::UnknownChain) => { + return Err(LightningError{err: format!("Channel announced on an unknown chain ({})", msg.contents.chain_hash.encode().to_hex()), action: ErrorAction::IgnoreError}); + }, + Err(chain::AccessError::UnknownTx) => { + return Err(LightningError{err: "Channel announced without corresponding UTXO entry".to_owned(), action: ErrorAction::IgnoreError}); + }, + } }, }; let result = self.network_graph.write().unwrap().update_channel_from_announcement(msg, utxo_value, Some(&self.secp_ctx)); @@ -828,7 +834,7 @@ impl NetworkGraph { #[cfg(test)] mod tests { - use chain::chaininterface; + use chain; use ln::features::{ChannelFeatures, NodeFeatures}; use routing::network_graph::{NetGraphMsgHandler, NetworkGraph}; use ln::msgs::{OptionalField, RoutingMessageHandler, UnsignedNodeAnnouncement, NodeAnnouncement, @@ -843,6 +849,7 @@ mod tests { use bitcoin::network::constants::Network; use bitcoin::blockdata::constants::genesis_block; use bitcoin::blockdata::script::Builder; + use bitcoin::blockdata::transaction::TxOut; use bitcoin::blockdata::opcodes; use hex; @@ -852,11 +859,10 @@ mod tests { use std::sync::Arc; - fn create_net_graph_msg_handler() -> (Secp256k1, NetGraphMsgHandler, Arc>) { + fn create_net_graph_msg_handler() -> (Secp256k1, NetGraphMsgHandler, Arc>) { let secp_ctx = Secp256k1::new(); let logger = Arc::new(test_utils::TestLogger::new()); - let chain_monitor = Arc::new(chaininterface::ChainWatchInterfaceUtil::new(Network::Testnet)); - let net_graph_msg_handler = NetGraphMsgHandler::new(chain_monitor, Arc::clone(&logger)); + let net_graph_msg_handler = NetGraphMsgHandler::new(None, Arc::clone(&logger)); (secp_ctx, net_graph_msg_handler) } @@ -981,9 +987,6 @@ mod tests { fn handling_channel_announcements() { let secp_ctx = Secp256k1::new(); let logger: Arc = Arc::new(test_utils::TestLogger::new()); - let chain_monitor = Arc::new(test_utils::TestChainWatcher::new()); - let net_graph_msg_handler = NetGraphMsgHandler::new(chain_monitor.clone(), Arc::clone(&logger)); - let node_1_privkey = &SecretKey::from_slice(&[42; 32]).unwrap(); let node_2_privkey = &SecretKey::from_slice(&[41; 32]).unwrap(); @@ -1020,8 +1023,7 @@ mod tests { }; // Test if the UTXO lookups were not supported - *chain_monitor.utxo_ret.lock().unwrap() = Err(chaininterface::ChainError::NotSupported); - + let mut net_graph_msg_handler = NetGraphMsgHandler::new(None, Arc::clone(&logger)); match net_graph_msg_handler.handle_channel_announcement(&valid_announcement) { Ok(res) => assert!(res), _ => panic!() @@ -1035,7 +1037,6 @@ mod tests { } } - // If we receive announcement for the same channel (with UTXO lookups disabled), // drop new one on the floor, since we can't see any changes. match net_graph_msg_handler.handle_channel_announcement(&valid_announcement) { @@ -1043,9 +1044,10 @@ mod tests { Err(e) => assert_eq!(e.err, "Already have knowledge of channel") }; - // Test if an associated transaction were not on-chain (or not confirmed). - *chain_monitor.utxo_ret.lock().unwrap() = Err(chaininterface::ChainError::UnknownTx); + let chain_source = Arc::new(test_utils::TestChainSource::new(Network::Testnet)); + *chain_source.utxo_ret.lock().unwrap() = Err(chain::AccessError::UnknownTx); + net_graph_msg_handler = NetGraphMsgHandler::new(Some(chain_source.clone()), Arc::clone(&logger)); unsigned_announcement.short_channel_id += 1; msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_announcement.encode()[..])[..]); @@ -1062,10 +1064,9 @@ mod tests { Err(e) => assert_eq!(e.err, "Channel announced without corresponding UTXO entry") }; - // Now test if the transaction is found in the UTXO set and the script is correct. unsigned_announcement.short_channel_id += 1; - *chain_monitor.utxo_ret.lock().unwrap() = Ok((good_script.clone(), 0)); + *chain_source.utxo_ret.lock().unwrap() = Ok(TxOut { value: 0, script_pubkey: good_script.clone() }); msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_announcement.encode()[..])[..]); let valid_announcement = ChannelAnnouncement { @@ -1090,14 +1091,14 @@ mod tests { // If we receive announcement for the same channel (but TX is not confirmed), // drop new one on the floor, since we can't see any changes. - *chain_monitor.utxo_ret.lock().unwrap() = Err(chaininterface::ChainError::UnknownTx); + *chain_source.utxo_ret.lock().unwrap() = Err(chain::AccessError::UnknownTx); match net_graph_msg_handler.handle_channel_announcement(&valid_announcement) { Ok(_) => panic!(), Err(e) => assert_eq!(e.err, "Channel announced without corresponding UTXO entry") }; // But if it is confirmed, replace the channel - *chain_monitor.utxo_ret.lock().unwrap() = Ok((good_script, 0)); + *chain_source.utxo_ret.lock().unwrap() = Ok(TxOut { value: 0, script_pubkey: good_script }); unsigned_announcement.features = ChannelFeatures::empty(); msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_announcement.encode()[..])[..]); let valid_announcement = ChannelAnnouncement { @@ -1169,8 +1170,8 @@ mod tests { fn handling_channel_update() { let secp_ctx = Secp256k1::new(); let logger: Arc = Arc::new(test_utils::TestLogger::new()); - let chain_monitor = Arc::new(test_utils::TestChainWatcher::new()); - let net_graph_msg_handler = NetGraphMsgHandler::new(chain_monitor.clone(), Arc::clone(&logger)); + let chain_source = Arc::new(test_utils::TestChainSource::new(Network::Testnet)); + let net_graph_msg_handler = NetGraphMsgHandler::new(Some(chain_source.clone()), Arc::clone(&logger)); let node_1_privkey = &SecretKey::from_slice(&[42; 32]).unwrap(); let node_2_privkey = &SecretKey::from_slice(&[41; 32]).unwrap(); @@ -1191,7 +1192,7 @@ mod tests { .push_slice(&PublicKey::from_secret_key(&secp_ctx, node_2_btckey).serialize()) .push_opcode(opcodes::all::OP_PUSHNUM_2) .push_opcode(opcodes::all::OP_CHECKMULTISIG).into_script().to_v0_p2wsh(); - *chain_monitor.utxo_ret.lock().unwrap() = Ok((good_script.clone(), amount_sats)); + *chain_source.utxo_ret.lock().unwrap() = Ok(TxOut { value: amount_sats, script_pubkey: good_script.clone() }); let unsigned_announcement = UnsignedChannelAnnouncement { features: ChannelFeatures::empty(), chain_hash, diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs index 1960c7b4e..56beb2ea0 100644 --- a/lightning/src/routing/router.rs +++ b/lightning/src/routing/router.rs @@ -410,7 +410,6 @@ pub fn get_route(our_node_id: &PublicKey, network: &NetworkGraph, targ #[cfg(test)] mod tests { - use chain::chaininterface; use routing::router::{get_route, RouteHint, RoutingFees}; use routing::network_graph::NetGraphMsgHandler; use ln::features::{ChannelFeatures, InitFeatures, NodeFeatures}; @@ -433,7 +432,7 @@ mod tests { use std::sync::Arc; // Using the same keys for LN and BTC ids - fn add_channel(net_graph_msg_handler: &NetGraphMsgHandler, Arc>, secp_ctx: &Secp256k1, node_1_privkey: &SecretKey, + fn add_channel(net_graph_msg_handler: &NetGraphMsgHandler, Arc>, secp_ctx: &Secp256k1, node_1_privkey: &SecretKey, node_2_privkey: &SecretKey, features: ChannelFeatures, short_channel_id: u64) { let node_id_1 = PublicKey::from_secret_key(&secp_ctx, node_1_privkey); let node_id_2 = PublicKey::from_secret_key(&secp_ctx, node_2_privkey); @@ -463,7 +462,7 @@ mod tests { }; } - fn update_channel(net_graph_msg_handler: &NetGraphMsgHandler, Arc>, secp_ctx: &Secp256k1, node_privkey: &SecretKey, update: UnsignedChannelUpdate) { + fn update_channel(net_graph_msg_handler: &NetGraphMsgHandler, Arc>, secp_ctx: &Secp256k1, node_privkey: &SecretKey, update: UnsignedChannelUpdate) { let msghash = hash_to_message!(&Sha256dHash::hash(&update.encode()[..])[..]); let valid_channel_update = ChannelUpdate { signature: secp_ctx.sign(&msghash, node_privkey), @@ -478,7 +477,7 @@ mod tests { } - fn add_or_update_node(net_graph_msg_handler: &NetGraphMsgHandler, Arc>, secp_ctx: &Secp256k1, node_privkey: &SecretKey, + fn add_or_update_node(net_graph_msg_handler: &NetGraphMsgHandler, Arc>, secp_ctx: &Secp256k1, node_privkey: &SecretKey, features: NodeFeatures, timestamp: u32) { let node_id = PublicKey::from_secret_key(&secp_ctx, node_privkey); let unsigned_announcement = UnsignedNodeAnnouncement { @@ -531,11 +530,10 @@ mod tests { } } - fn build_graph() -> (Secp256k1, NetGraphMsgHandler, std::sync::Arc>, std::sync::Arc) { + fn build_graph() -> (Secp256k1, NetGraphMsgHandler, std::sync::Arc>, std::sync::Arc) { let secp_ctx = Secp256k1::new(); let logger = Arc::new(test_utils::TestLogger::new()); - let chain_monitor = Arc::new(chaininterface::ChainWatchInterfaceUtil::new(Network::Testnet)); - let net_graph_msg_handler = NetGraphMsgHandler::new(chain_monitor, Arc::clone(&logger)); + let net_graph_msg_handler = NetGraphMsgHandler::new(None, Arc::clone(&logger)); // Build network from our_id to node7: // // -1(1)2- node0 -1(3)2- diff --git a/lightning/src/util/test_utils.rs b/lightning/src/util/test_utils.rs index 4f4ddc23f..c7f0a4903 100644 --- a/lightning/src/util/test_utils.rs +++ b/lightning/src/util/test_utils.rs @@ -7,8 +7,9 @@ // You may not use this file except in accordance with one or both of these // licenses. +use chain; use chain::chaininterface; -use chain::chaininterface::{ConfirmationTarget, ChainError, ChainWatchInterface}; +use chain::chaininterface::ConfirmationTarget; use chain::transaction::OutPoint; use chain::keysinterface; use ln::channelmonitor; @@ -22,12 +23,11 @@ use util::logger::{Logger, Level, Record}; use util::ser::{Readable, Writer, Writeable}; use bitcoin::blockdata::constants::genesis_block; -use bitcoin::blockdata::transaction::Transaction; +use bitcoin::blockdata::transaction::{Transaction, TxOut}; use bitcoin::blockdata::script::{Builder, Script}; -use bitcoin::blockdata::block::BlockHeader; use bitcoin::blockdata::opcodes; use bitcoin::network::constants::Network; -use bitcoin::hash_types::{Txid, BlockHash}; +use bitcoin::hash_types::BlockHash; use bitcoin::secp256k1::{SecretKey, PublicKey, Secp256k1, Signature}; @@ -393,27 +393,27 @@ impl TestKeysInterface { } } -pub struct TestChainWatcher { - pub utxo_ret: Mutex>, +pub struct TestChainSource { + pub genesis_hash: BlockHash, + pub utxo_ret: Mutex>, } -impl TestChainWatcher { - pub fn new() -> Self { - let script = Builder::new().push_opcode(opcodes::OP_TRUE).into_script(); - Self { utxo_ret: Mutex::new(Ok((script, u64::max_value()))) } +impl TestChainSource { + pub fn new(network: Network) -> Self { + let script_pubkey = Builder::new().push_opcode(opcodes::OP_TRUE).into_script(); + Self { + genesis_hash: genesis_block(network).block_hash(), + utxo_ret: Mutex::new(Ok(TxOut { value: u64::max_value(), script_pubkey })), + } } } -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(&self, _header: &BlockHeader, _txdata: &[(usize, &Transaction)]) -> Vec { - Vec::new() - } - fn reentered(&self) -> usize { 0 } +impl chain::Access for TestChainSource { + fn get_utxo(&self, genesis_hash: &BlockHash, _short_channel_id: u64) -> Result { + if self.genesis_hash != *genesis_hash { + return Err(chain::AccessError::UnknownChain); + } - fn get_chain_utxo(&self, _genesis_hash: BlockHash, _unspent_tx_output_identifier: u64) -> Result<(Script, u64), ChainError> { self.utxo_ret.lock().unwrap().clone() } } -- 2.39.5