X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Ffunctional_test_utils.rs;h=5a1a21e2999863b9f6af7b75da6c467b4228e7f3;hb=96445880f6401d178893a3cec40fe26ebc755a30;hp=84bc1a1b3f0656476aa1d00771f0368d97003cd0;hpb=d7e3320c030654ca3ff2b6ffd83ae65dfbe5e3c8;p=rust-lightning diff --git a/lightning/src/ln/functional_test_utils.rs b/lightning/src/ln/functional_test_utils.rs index 84bc1a1b..e53fd6b0 100644 --- a/lightning/src/ln/functional_test_utils.rs +++ b/lightning/src/ln/functional_test_utils.rs @@ -10,43 +10,49 @@ //! A bunch of useful utilities for building networks of nodes and exchanging messages between //! nodes for functional tests. -use crate::chain::{BestBlock, ChannelMonitorUpdateStatus, Confirm, Listen, Watch}; -use crate::sign::EntropySource; +use crate::chain::{BestBlock, ChannelMonitorUpdateStatus, Confirm, Listen, Watch, chainmonitor::Persist}; use crate::chain::channelmonitor::ChannelMonitor; use crate::chain::transaction::OutPoint; -use crate::events::{ClosureReason, Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, PathFailure, PaymentPurpose, PaymentFailureReason}; -use crate::events::bump_transaction::{BumpTransactionEventHandler, Wallet, WalletSource}; -use crate::ln::{PaymentPreimage, PaymentHash, PaymentSecret}; +use crate::events::{ClaimedHTLC, ClosureReason, Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, PathFailure, PaymentPurpose, PaymentFailureReason}; +use crate::events::bump_transaction::{BumpTransactionEvent, BumpTransactionEventHandler, Wallet, WalletSource}; +use crate::ln::{ChannelId, PaymentPreimage, PaymentHash, PaymentSecret}; use crate::ln::channelmanager::{AChannelManager, ChainParameters, ChannelManager, ChannelManagerReadArgs, RAACommitmentOrder, PaymentSendFailure, RecipientOnionFields, PaymentId, MIN_CLTV_EXPIRY_DELTA}; -use crate::routing::gossip::{P2PGossipSync, NetworkGraph, NetworkUpdate}; -use crate::routing::router::{self, PaymentParameters, Route}; use crate::ln::features::InitFeatures; use crate::ln::msgs; -use crate::ln::msgs::{ChannelMessageHandler,RoutingMessageHandler}; -use crate::util::enforcing_trait_impls::EnforcingSigner; +use crate::ln::msgs::{ChannelMessageHandler, OnionMessageHandler, RoutingMessageHandler}; +use crate::ln::peer_handler::IgnoringMessageHandler; +use crate::onion_message::messenger::OnionMessenger; +use crate::routing::gossip::{P2PGossipSync, NetworkGraph, NetworkUpdate}; +use crate::routing::router::{self, PaymentParameters, Route, RouteParameters}; +use crate::sign::{EntropySource, RandomBytes}; +use crate::util::config::{UserConfig, MaxDustHTLCExposure}; +use crate::util::errors::APIError; +#[cfg(test)] +use crate::util::logger::Logger; use crate::util::scid_utils; +use crate::util::test_channel_signer::TestChannelSigner; use crate::util::test_utils; use crate::util::test_utils::{panicking, TestChainMonitor, TestScorer, TestKeysInterface}; -use crate::util::errors::APIError; -use crate::util::config::{UserConfig, MaxDustHTLCExposure}; use crate::util::ser::{ReadableArgs, Writeable}; -use bitcoin::blockdata::block::{Block, BlockHeader}; -use bitcoin::blockdata::transaction::{Transaction, TxOut}; -use bitcoin::hash_types::BlockHash; +use bitcoin::blockdata::block::{Block, Header, Version}; +use bitcoin::blockdata::locktime::absolute::LockTime; +use bitcoin::blockdata::transaction::{Transaction, TxIn, TxOut}; +use bitcoin::hash_types::{BlockHash, TxMerkleNode}; use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::hashes::Hash as _; use bitcoin::network::constants::Network; +use bitcoin::pow::CompactTarget; use bitcoin::secp256k1::{PublicKey, SecretKey}; -use crate::io; -use crate::prelude::*; -use core::cell::RefCell; use alloc::rc::Rc; -use crate::sync::{Arc, Mutex, LockTestExt}; -use core::mem; +use core::cell::RefCell; use core::iter::repeat; -use bitcoin::{PackedLockTime, TxMerkleNode}; +use core::mem; +use core::ops::Deref; +use crate::io; +use crate::prelude::*; +use crate::sync::{Arc, Mutex, LockTestExt, RwLock}; pub const CHAN_CONFIRM_DEPTH: u32 = 10; @@ -73,6 +79,27 @@ pub fn mine_transactions<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, txn: &[&Tra let height = node.best_block_info().1 + 1; confirm_transactions_at(node, txn, height); } +/// Mine a single block containing the given transaction without extra consistency checks which may +/// impact ChannelManager state. +pub fn mine_transaction_without_consistency_checks<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, tx: &Transaction) { + let height = node.best_block_info().1 + 1; + let mut block = Block { + header: Header { + version: Version::NO_SOFT_FORK_SIGNALLING, + prev_blockhash: node.best_block_hash(), + merkle_root: TxMerkleNode::all_zeros(), + time: height, + bits: CompactTarget::from_consensus(42), + nonce: 42, + }, + txdata: Vec::new(), + }; + for _ in 0..*node.network_chan_count.borrow() { // Make sure we don't end up with channels at the same short id by offsetting by chan_count + block.txdata.push(Transaction { version: 0, lock_time: LockTime::ZERO, input: Vec::new(), output: Vec::new() }); + } + block.txdata.push((*tx).clone()); + do_connect_block_without_consistency_checks(node, block, false); +} /// Mine the given transaction at the given height, mining blocks as required to build to that /// height /// @@ -86,7 +113,7 @@ pub fn confirm_transactions_at<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, txn: } let mut txdata = Vec::new(); for _ in 0..*node.network_chan_count.borrow() { // Make sure we don't end up with channels at the same short id by offsetting by chan_count - txdata.push(Transaction { version: 0, lock_time: PackedLockTime::ZERO, input: Vec::new(), output: Vec::new() }); + txdata.push(Transaction { version: 0, lock_time: LockTime::ZERO, input: Vec::new(), output: Vec::new() }); } for tx in txn { txdata.push((*tx).clone()); @@ -188,13 +215,13 @@ impl ConnectStyle { } } -pub fn create_dummy_header(prev_blockhash: BlockHash, time: u32) -> BlockHeader { - BlockHeader { - version: 0x2000_0000, +pub fn create_dummy_header(prev_blockhash: BlockHash, time: u32) -> Header { + Header { + version: Version::NO_SOFT_FORK_SIGNALLING, prev_blockhash, merkle_root: TxMerkleNode::all_zeros(), time, - bits: 42, + bits: CompactTarget::from_consensus(42), nonce: 42, } } @@ -211,27 +238,33 @@ pub fn connect_blocks<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, depth: u32) -> assert!(depth >= 1); for i in 1..depth { let prev_blockhash = block.header.block_hash(); - do_connect_block(node, block, skip_intermediaries); + do_connect_block_with_consistency_checks(node, block, skip_intermediaries); block = create_dummy_block(prev_blockhash, height + i, Vec::new()); } let hash = block.header.block_hash(); - do_connect_block(node, block, false); + do_connect_block_with_consistency_checks(node, block, false); hash } pub fn connect_block<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, block: &Block) { - do_connect_block(node, block.clone(), false); + do_connect_block_with_consistency_checks(node, block.clone(), false); } fn call_claimable_balances<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>) { // Ensure `get_claimable_balances`' self-tests never panic - for funding_outpoint in node.chain_monitor.chain_monitor.list_monitors() { + for (funding_outpoint, _channel_id) in node.chain_monitor.chain_monitor.list_monitors() { node.chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances(); } } -fn do_connect_block<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, block: Block, skip_intermediaries: bool) { +fn do_connect_block_with_consistency_checks<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, block: Block, skip_intermediaries: bool) { + call_claimable_balances(node); + do_connect_block_without_consistency_checks(node, block, skip_intermediaries); call_claimable_balances(node); + node.node.test_process_background_events(); +} + +fn do_connect_block_without_consistency_checks<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, block: Block, skip_intermediaries: bool) { let height = node.best_block_info().1 + 1; #[cfg(feature = "std")] { eprintln!("Connecting block using Block Connection Style: {:?}", *node.connect_style.borrow()); @@ -286,8 +319,6 @@ fn do_connect_block<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, block: Block, sk } } } - call_claimable_balances(node); - node.node.test_process_background_events(); for tx in &block.txdata { for input in &tx.input { @@ -352,7 +383,7 @@ pub struct TestChanMonCfg { pub persister: test_utils::TestPersister, pub logger: test_utils::TestLogger, pub keys_manager: test_utils::TestKeysInterface, - pub scorer: Mutex, + pub scorer: RwLock, } pub struct NodeCfg<'a> { @@ -360,6 +391,7 @@ pub struct NodeCfg<'a> { pub tx_broadcaster: &'a test_utils::TestBroadcaster, pub fee_estimator: &'a test_utils::TestFeeEstimator, pub router: test_utils::TestRouter<'a>, + pub message_router: test_utils::TestMessageRouter<'a>, pub chain_monitor: test_utils::TestChainMonitor<'a>, pub keys_manager: &'a test_utils::TestKeysInterface, pub logger: &'a test_utils::TestLogger, @@ -368,33 +400,75 @@ pub struct NodeCfg<'a> { pub override_init_features: Rc>>, } -type TestChannelManager<'a, 'b, 'c> = ChannelManager<&'b TestChainMonitor<'c>, &'c test_utils::TestBroadcaster, &'b test_utils::TestKeysInterface, &'b test_utils::TestKeysInterface, &'b test_utils::TestKeysInterface, &'c test_utils::TestFeeEstimator, &'b test_utils::TestRouter<'c>, &'c test_utils::TestLogger>; - -pub struct Node<'a, 'b: 'a, 'c: 'b> { - pub chain_source: &'c test_utils::TestChainSource, - pub tx_broadcaster: &'c test_utils::TestBroadcaster, - pub fee_estimator: &'c test_utils::TestFeeEstimator, - pub router: &'b test_utils::TestRouter<'c>, - pub chain_monitor: &'b test_utils::TestChainMonitor<'c>, - pub keys_manager: &'b test_utils::TestKeysInterface, - pub node: &'a TestChannelManager<'a, 'b, 'c>, - pub network_graph: &'a NetworkGraph<&'c test_utils::TestLogger>, - pub gossip_sync: P2PGossipSync<&'b NetworkGraph<&'c test_utils::TestLogger>, &'c test_utils::TestChainSource, &'c test_utils::TestLogger>, +type TestChannelManager<'node_cfg, 'chan_mon_cfg> = ChannelManager< + &'node_cfg TestChainMonitor<'chan_mon_cfg>, + &'chan_mon_cfg test_utils::TestBroadcaster, + &'node_cfg test_utils::TestKeysInterface, + &'node_cfg test_utils::TestKeysInterface, + &'node_cfg test_utils::TestKeysInterface, + &'chan_mon_cfg test_utils::TestFeeEstimator, + &'node_cfg test_utils::TestRouter<'chan_mon_cfg>, + &'chan_mon_cfg test_utils::TestLogger, +>; + +type TestOnionMessenger<'chan_man, 'node_cfg, 'chan_mon_cfg> = OnionMessenger< + DedicatedEntropy, + &'node_cfg test_utils::TestKeysInterface, + &'chan_mon_cfg test_utils::TestLogger, + &'node_cfg test_utils::TestMessageRouter<'chan_mon_cfg>, + &'chan_man TestChannelManager<'node_cfg, 'chan_mon_cfg>, + IgnoringMessageHandler, +>; + +/// For use with [`OnionMessenger`] otherwise `test_restored_packages_retry` will fail. This is +/// because that test uses older serialized data produced by calling [`EntropySource`] in a specific +/// manner. Using the same [`EntropySource`] with [`OnionMessenger`] would introduce another call, +/// causing the produced data to no longer match. +pub struct DedicatedEntropy(RandomBytes); + +impl Deref for DedicatedEntropy { + type Target = RandomBytes; + fn deref(&self) -> &Self::Target { &self.0 } +} + +pub struct Node<'chan_man, 'node_cfg: 'chan_man, 'chan_mon_cfg: 'node_cfg> { + pub chain_source: &'chan_mon_cfg test_utils::TestChainSource, + pub tx_broadcaster: &'chan_mon_cfg test_utils::TestBroadcaster, + pub fee_estimator: &'chan_mon_cfg test_utils::TestFeeEstimator, + pub router: &'node_cfg test_utils::TestRouter<'chan_mon_cfg>, + pub chain_monitor: &'node_cfg test_utils::TestChainMonitor<'chan_mon_cfg>, + pub keys_manager: &'chan_mon_cfg test_utils::TestKeysInterface, + pub node: &'chan_man TestChannelManager<'node_cfg, 'chan_mon_cfg>, + pub onion_messenger: TestOnionMessenger<'chan_man, 'node_cfg, 'chan_mon_cfg>, + pub network_graph: &'node_cfg NetworkGraph<&'chan_mon_cfg test_utils::TestLogger>, + pub gossip_sync: P2PGossipSync<&'node_cfg NetworkGraph<&'chan_mon_cfg test_utils::TestLogger>, &'chan_mon_cfg test_utils::TestChainSource, &'chan_mon_cfg test_utils::TestLogger>, pub node_seed: [u8; 32], pub network_payment_count: Rc>, pub network_chan_count: Rc>, - pub logger: &'c test_utils::TestLogger, + pub logger: &'chan_mon_cfg test_utils::TestLogger, pub blocks: Arc>>, pub connect_style: Rc>, pub override_init_features: Rc>>, pub wallet_source: Arc, pub bump_tx_handler: BumpTransactionEventHandler< - &'c test_utils::TestBroadcaster, - Arc, &'c test_utils::TestLogger>>, - &'b test_utils::TestKeysInterface, - &'c test_utils::TestLogger, + &'chan_mon_cfg test_utils::TestBroadcaster, + Arc, &'chan_mon_cfg test_utils::TestLogger>>, + &'chan_mon_cfg test_utils::TestKeysInterface, + &'chan_mon_cfg test_utils::TestLogger, >, } + +impl<'a, 'b, 'c> Node<'a, 'b, 'c> { + pub fn init_features(&self, peer_node_id: &PublicKey) -> InitFeatures { + self.override_init_features.borrow().clone() + .unwrap_or_else(|| self.node.init_features() | self.onion_messenger.provided_init_features(peer_node_id)) + } +} + +#[cfg(feature = "std")] +impl<'a, 'b, 'c> std::panic::UnwindSafe for Node<'a, 'b, 'c> {} +#[cfg(feature = "std")] +impl<'a, 'b, 'c> std::panic::RefUnwindSafe for Node<'a, 'b, 'c> {} impl<'a, 'b, 'c> Node<'a, 'b, 'c> { pub fn best_block_hash(&self) -> BlockHash { self.blocks.lock().unwrap().last().unwrap().0.block_hash() @@ -402,9 +476,28 @@ impl<'a, 'b, 'c> Node<'a, 'b, 'c> { pub fn best_block_info(&self) -> (BlockHash, u32) { self.blocks.lock().unwrap().last().map(|(a, b)| (a.block_hash(), *b)).unwrap() } - pub fn get_block_header(&self, height: u32) -> BlockHeader { + pub fn get_block_header(&self, height: u32) -> Header { self.blocks.lock().unwrap()[height as usize].0.header } + /// Changes the channel signer's availability for the specified peer and channel. + /// + /// When `available` is set to `true`, the channel signer will behave normally. When set to + /// `false`, the channel signer will act like an off-line remote signer and will return `Err` for + /// several of the signing methods. Currently, only `get_per_commitment_point` and + /// `release_commitment_secret` are affected by this setting. + #[cfg(test)] + pub fn set_channel_signer_available(&self, peer_id: &PublicKey, chan_id: &ChannelId, available: bool) { + let per_peer_state = self.node.per_peer_state.read().unwrap(); + let chan_lock = per_peer_state.get(peer_id).unwrap().lock().unwrap(); + let signer = (|| { + match chan_lock.channel_by_id.get(chan_id) { + Some(phase) => phase.context().get_signer(), + None => panic!("Couldn't find a channel with id {}", chan_id), + } + })(); + log_debug!(self.logger, "Setting channel signer for {} as available={}", chan_id, available); + signer.as_ecdsa().unwrap().set_available(available); + } } /// If we need an unsafe pointer to a `Node` (ie to reference it in a thread @@ -448,8 +541,8 @@ impl NodeHolder for &H { fn chain_monitor(&self) -> Option<&test_utils::TestChainMonitor> { (*self).chain_monitor() } } impl<'a, 'b: 'a, 'c: 'b> NodeHolder for Node<'a, 'b, 'c> { - type CM = TestChannelManager<'a, 'b, 'c>; - fn node(&self) -> &TestChannelManager<'a, 'b, 'c> { &self.node } + type CM = TestChannelManager<'b, 'c>; + fn node(&self) -> &TestChannelManager<'b, 'c> { &self.node } fn chain_monitor(&self) -> Option<&test_utils::TestChainMonitor> { Some(self.chain_monitor) } } @@ -508,10 +601,10 @@ impl<'a, 'b, 'c> Drop for Node<'a, 'b, 'c> { let feeest = test_utils::TestFeeEstimator { sat_per_kw: Mutex::new(253) }; let mut deserialized_monitors = Vec::new(); { - for outpoint in self.chain_monitor.chain_monitor.list_monitors() { + for (outpoint, _channel_id) in self.chain_monitor.chain_monitor.list_monitors() { let mut w = test_utils::TestVecWriter(Vec::new()); self.chain_monitor.chain_monitor.get_monitor(outpoint).unwrap().write(&mut w).unwrap(); - let (_, deserialized_monitor) = <(BlockHash, ChannelMonitor)>::read( + let (_, deserialized_monitor) = <(BlockHash, ChannelMonitor)>::read( &mut io::Cursor::new(&w.0), (self.keys_manager, self.keys_manager)).unwrap(); deserialized_monitors.push(deserialized_monitor); } @@ -530,7 +623,7 @@ impl<'a, 'b, 'c> Drop for Node<'a, 'b, 'c> { channel_monitors.insert(monitor.get_funding_txo().0, monitor); } - let scorer = Mutex::new(test_utils::TestScorer::new()); + let scorer = RwLock::new(test_utils::TestScorer::new()); let mut w = test_utils::TestVecWriter(Vec::new()); self.node.write(&mut w).unwrap(); <(BlockHash, ChannelManager<&test_utils::TestChainMonitor, &test_utils::TestBroadcaster, &test_utils::TestKeysInterface, &test_utils::TestKeysInterface, &test_utils::TestKeysInterface, &test_utils::TestFeeEstimator, &test_utils::TestRouter, &test_utils::TestLogger>)>::read(&mut io::Cursor::new(w.0), ChannelManagerReadArgs { @@ -539,7 +632,7 @@ impl<'a, 'b, 'c> Drop for Node<'a, 'b, 'c> { node_signer: self.keys_manager, signer_provider: self.keys_manager, fee_estimator: &test_utils::TestFeeEstimator { sat_per_kw: Mutex::new(253) }, - router: &test_utils::TestRouter::new(Arc::new(network_graph), &scorer), + router: &test_utils::TestRouter::new(Arc::new(network_graph), &self.logger, &scorer), chain_monitor: self.chain_monitor, tx_broadcaster: &broadcaster, logger: &self.logger, @@ -551,7 +644,8 @@ impl<'a, 'b, 'c> Drop for Node<'a, 'b, 'c> { let chain_source = test_utils::TestChainSource::new(Network::Testnet); let chain_monitor = test_utils::TestChainMonitor::new(Some(&chain_source), &broadcaster, &self.logger, &feeest, &persister, &self.keys_manager); for deserialized_monitor in deserialized_monitors.drain(..) { - if chain_monitor.watch_channel(deserialized_monitor.get_funding_txo().0, deserialized_monitor) != ChannelMonitorUpdateStatus::Completed { + let funding_outpoint = deserialized_monitor.get_funding_txo().0; + if chain_monitor.watch_channel(funding_outpoint, deserialized_monitor) != Ok(ChannelMonitorUpdateStatus::Completed) { panic!(); } } @@ -561,11 +655,11 @@ impl<'a, 'b, 'c> Drop for Node<'a, 'b, 'c> { } } -pub fn create_chan_between_nodes<'a, 'b, 'c, 'd>(node_a: &'a Node<'b, 'c, 'd>, node_b: &'a Node<'b, 'c, 'd>) -> (msgs::ChannelAnnouncement, msgs::ChannelUpdate, msgs::ChannelUpdate, [u8; 32], Transaction) { +pub fn create_chan_between_nodes<'a, 'b, 'c: 'd, 'd>(node_a: &'a Node<'b, 'c, 'd>, node_b: &'a Node<'b, 'c, 'd>) -> (msgs::ChannelAnnouncement, msgs::ChannelUpdate, msgs::ChannelUpdate, ChannelId, Transaction) { create_chan_between_nodes_with_value(node_a, node_b, 100000, 10001) } -pub fn create_chan_between_nodes_with_value<'a, 'b, 'c, 'd>(node_a: &'a Node<'b, 'c, 'd>, node_b: &'a Node<'b, 'c, 'd>, channel_value: u64, push_msat: u64) -> (msgs::ChannelAnnouncement, msgs::ChannelUpdate, msgs::ChannelUpdate, [u8; 32], Transaction) { +pub fn create_chan_between_nodes_with_value<'a, 'b, 'c: 'd, 'd>(node_a: &'a Node<'b, 'c, 'd>, node_b: &'a Node<'b, 'c, 'd>, channel_value: u64, push_msat: u64) -> (msgs::ChannelAnnouncement, msgs::ChannelUpdate, msgs::ChannelUpdate, ChannelId, Transaction) { let (channel_ready, channel_id, tx) = create_chan_between_nodes_with_value_a(node_a, node_b, channel_value, push_msat); let (announcement, as_update, bs_update) = create_chan_between_nodes_with_value_b(node_a, node_b, &channel_ready); (announcement, as_update, bs_update, channel_id, tx) @@ -634,6 +728,12 @@ pub fn get_err_msg(node: &Node, recipient: &PublicKey) -> msgs::ErrorMessage { assert_eq!(node_id, recipient); (*msg).clone() }, + MessageSendEvent::HandleError { + action: msgs::ErrorAction::DisconnectPeer { ref msg }, ref node_id + } => { + assert_eq!(node_id, recipient); + msg.as_ref().unwrap().clone() + }, _ => panic!("Unexpected event"), } } @@ -756,6 +856,18 @@ pub fn remove_first_msg_event_to_node(msg_node_id: &PublicKey, msg_events: &mut MessageSendEvent::SendOpenChannelV2 { node_id, .. } => { node_id == msg_node_id }, + MessageSendEvent::SendStfu { node_id, .. } => { + node_id == msg_node_id + }, + MessageSendEvent::SendSplice { node_id, .. } => { + node_id == msg_node_id + }, + MessageSendEvent::SendSpliceAck { node_id, .. } => { + node_id == msg_node_id + }, + MessageSendEvent::SendSpliceLocked { node_id, .. } => { + node_id == msg_node_id + }, MessageSendEvent::SendTxAddInput { node_id, .. } => { node_id == msg_node_id }, @@ -802,36 +914,14 @@ macro_rules! get_channel_ref { } } -#[cfg(test)] -macro_rules! get_outbound_v1_channel_ref { - ($node: expr, $counterparty_node: expr, $per_peer_state_lock: ident, $peer_state_lock: ident, $channel_id: expr) => { - { - $per_peer_state_lock = $node.node.per_peer_state.read().unwrap(); - $peer_state_lock = $per_peer_state_lock.get(&$counterparty_node.node.get_our_node_id()).unwrap().lock().unwrap(); - $peer_state_lock.outbound_v1_channel_by_id.get_mut(&$channel_id).unwrap() - } - } -} - -#[cfg(test)] -macro_rules! get_inbound_v1_channel_ref { - ($node: expr, $counterparty_node: expr, $per_peer_state_lock: ident, $peer_state_lock: ident, $channel_id: expr) => { - { - $per_peer_state_lock = $node.node.per_peer_state.read().unwrap(); - $peer_state_lock = $per_peer_state_lock.get(&$counterparty_node.node.get_our_node_id()).unwrap().lock().unwrap(); - $peer_state_lock.inbound_v1_channel_by_id.get_mut(&$channel_id).unwrap() - } - } -} - #[cfg(test)] macro_rules! get_feerate { ($node: expr, $counterparty_node: expr, $channel_id: expr) => { { let mut per_peer_state_lock; let mut peer_state_lock; - let chan = get_channel_ref!($node, $counterparty_node, per_peer_state_lock, peer_state_lock, $channel_id); - chan.context.get_feerate_sat_per_1000_weight() + let phase = get_channel_ref!($node, $counterparty_node, per_peer_state_lock, peer_state_lock, $channel_id); + phase.context().get_feerate_sat_per_1000_weight() } } } @@ -843,7 +933,7 @@ macro_rules! get_channel_type_features { let mut per_peer_state_lock; let mut peer_state_lock; let chan = get_channel_ref!($node, $counterparty_node, per_peer_state_lock, peer_state_lock, $channel_id); - chan.context.get_channel_type().clone() + chan.context().get_channel_type().clone() } } } @@ -859,7 +949,7 @@ macro_rules! get_monitor { for index in 0..2 { if let Ok(mon) = $node.chain_monitor.chain_monitor.get_monitor( $crate::chain::transaction::OutPoint { - txid: bitcoin::Txid::from_slice(&$channel_id[..]).unwrap(), index + txid: bitcoin::Txid::from_slice(&$channel_id.0[..]).unwrap(), index }) { monitor = Some(mon); @@ -900,7 +990,14 @@ macro_rules! unwrap_send_err { _ => panic!(), } }, - _ => panic!(), + &Err(PaymentSendFailure::PathParameterError(ref result)) if !$all_failed => { + assert_eq!(result.len(), 1); + match result[0] { + Err($type) => { $check }, + _ => panic!(), + } + }, + _ => {panic!()}, } } } @@ -909,7 +1006,8 @@ macro_rules! unwrap_send_err { pub fn check_added_monitors>(node: &H, count: usize) { if let Some(chain_monitor) = node.chain_monitor() { let mut added_monitors = chain_monitor.added_monitors.lock().unwrap(); - assert_eq!(added_monitors.len(), count); + let n = added_monitors.len(); + assert_eq!(n, count, "expected {} monitors to be added, not {}", count, n); added_monitors.clear(); } } @@ -924,11 +1022,26 @@ macro_rules! check_added_monitors { } } -pub fn _reload_node<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, default_config: UserConfig, chanman_encoded: &[u8], monitors_encoded: &[&[u8]]) -> ChannelManager<&'b TestChainMonitor<'c>, &'c test_utils::TestBroadcaster, &'b test_utils::TestKeysInterface, &'b test_utils::TestKeysInterface, &'b test_utils::TestKeysInterface, &'c test_utils::TestFeeEstimator, &'b test_utils::TestRouter<'c>, &'c test_utils::TestLogger> { +/// Checks whether the claimed HTLC for the specified path has the correct channel information. +/// +/// This will panic if the path is empty, if the HTLC's channel ID is not actually a channel that +/// connects the final two nodes in the path, or if the `user_channel_id` is incorrect. +pub fn check_claimed_htlc_channel<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, path: &[&Node<'a, 'b, 'c>], htlc: &ClaimedHTLC) { + let mut nodes = path.iter().rev(); + let dest = nodes.next().expect("path should have a destination").node; + let prev = nodes.next().unwrap_or(&origin_node).node; + let dest_channels = dest.list_channels(); + let ch = dest_channels.iter().find(|ch| ch.channel_id == htlc.channel_id) + .expect("HTLC's channel should be one of destination node's channels"); + assert_eq!(htlc.user_channel_id, ch.user_channel_id); + assert_eq!(ch.counterparty.node_id, prev.get_our_node_id()); +} + +pub fn _reload_node<'a, 'b, 'c>(node: &'a Node<'a, 'b, 'c>, default_config: UserConfig, chanman_encoded: &[u8], monitors_encoded: &[&[u8]]) -> TestChannelManager<'b, 'c> { let mut monitors_read = Vec::with_capacity(monitors_encoded.len()); for encoded in monitors_encoded { let mut monitor_read = &encoded[..]; - let (_, monitor) = <(BlockHash, ChannelMonitor)> + let (_, monitor) = <(BlockHash, ChannelMonitor)> ::read(&mut monitor_read, (node.keys_manager, node.keys_manager)).unwrap(); assert!(monitor_read.is_empty()); monitors_read.push(monitor); @@ -940,7 +1053,7 @@ pub fn _reload_node<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, default_config: for monitor in monitors_read.iter_mut() { assert!(channel_monitors.insert(monitor.get_funding_txo().0, monitor).is_none()); } - <(BlockHash, ChannelManager<&test_utils::TestChainMonitor, &test_utils::TestBroadcaster, &test_utils::TestKeysInterface, &test_utils::TestKeysInterface, &test_utils::TestKeysInterface, &test_utils::TestFeeEstimator, &test_utils::TestRouter, &test_utils::TestLogger>)>::read(&mut node_read, ChannelManagerReadArgs { + <(BlockHash, TestChannelManager<'b, 'c>)>::read(&mut node_read, ChannelManagerReadArgs { default_config, entropy_source: node.keys_manager, node_signer: node.keys_manager, @@ -956,8 +1069,9 @@ pub fn _reload_node<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, default_config: assert!(node_read.is_empty()); for monitor in monitors_read.drain(..) { - assert_eq!(node.chain_monitor.watch_channel(monitor.get_funding_txo().0, monitor), - ChannelMonitorUpdateStatus::Completed); + let funding_outpoint = monitor.get_funding_txo().0; + assert_eq!(node.chain_monitor.watch_channel(funding_outpoint, monitor), + Ok(ChannelMonitorUpdateStatus::Completed)); check_added_monitors!(node, 1); } @@ -975,13 +1089,30 @@ macro_rules! reload_node { $new_channelmanager = _reload_node(&$node, $new_config, &chanman_encoded, $monitors_encoded); $node.node = &$new_channelmanager; + $node.onion_messenger.set_offers_handler(&$new_channelmanager); }; ($node: expr, $chanman_encoded: expr, $monitors_encoded: expr, $persister: ident, $new_chain_monitor: ident, $new_channelmanager: ident) => { reload_node!($node, $crate::util::config::UserConfig::default(), $chanman_encoded, $monitors_encoded, $persister, $new_chain_monitor, $new_channelmanager); }; } -pub fn create_funding_transaction<'a, 'b, 'c>(node: &Node<'a, 'b, 'c>, expected_counterparty_node_id: &PublicKey, expected_chan_value: u64, expected_user_chan_id: u128) -> ([u8; 32], Transaction, OutPoint) { +pub fn create_funding_transaction<'a, 'b, 'c>(node: &Node<'a, 'b, 'c>, + expected_counterparty_node_id: &PublicKey, expected_chan_value: u64, expected_user_chan_id: u128) + -> (ChannelId, Transaction, OutPoint) +{ + internal_create_funding_transaction(node, expected_counterparty_node_id, expected_chan_value, expected_user_chan_id, false) +} + +pub fn create_coinbase_funding_transaction<'a, 'b, 'c>(node: &Node<'a, 'b, 'c>, + expected_counterparty_node_id: &PublicKey, expected_chan_value: u64, expected_user_chan_id: u128) + -> (ChannelId, Transaction, OutPoint) +{ + internal_create_funding_transaction(node, expected_counterparty_node_id, expected_chan_value, expected_user_chan_id, true) +} + +fn internal_create_funding_transaction<'a, 'b, 'c>(node: &Node<'a, 'b, 'c>, + expected_counterparty_node_id: &PublicKey, expected_chan_value: u64, expected_user_chan_id: u128, + coinbase: bool) -> (ChannelId, Transaction, OutPoint) { let chan_id = *node.network_chan_count.borrow(); let events = node.node.get_and_clear_pending_events(); @@ -992,7 +1123,16 @@ pub fn create_funding_transaction<'a, 'b, 'c>(node: &Node<'a, 'b, 'c>, expected_ assert_eq!(*channel_value_satoshis, expected_chan_value); assert_eq!(user_channel_id, expected_user_chan_id); - let tx = Transaction { version: chan_id as i32, lock_time: PackedLockTime::ZERO, input: Vec::new(), output: vec![TxOut { + let input = if coinbase { + vec![TxIn { + previous_output: bitcoin::OutPoint::null(), + ..Default::default() + }] + } else { + Vec::new() + }; + + let tx = Transaction { version: chan_id as i32, lock_time: LockTime::ZERO, input, output: vec![TxOut { value: *channel_value_satoshis, script_pubkey: output_script.clone(), }]}; let funding_outpoint = OutPoint { txid: tx.txid(), index: 0 }; @@ -1001,7 +1141,8 @@ pub fn create_funding_transaction<'a, 'b, 'c>(node: &Node<'a, 'b, 'c>, expected_ _ => panic!("Unexpected event"), } } -pub fn sign_funding_transaction<'a, 'b, 'c>(node_a: &Node<'a, 'b, 'c>, node_b: &Node<'a, 'b, 'c>, channel_value: u64, expected_temporary_channel_id: [u8; 32]) -> Transaction { + +pub fn sign_funding_transaction<'a, 'b, 'c>(node_a: &Node<'a, 'b, 'c>, node_b: &Node<'a, 'b, 'c>, channel_value: u64, expected_temporary_channel_id: ChannelId) -> Transaction { let (temporary_channel_id, tx, funding_output) = create_funding_transaction(node_a, &node_b.node.get_our_node_id(), channel_value, 42); assert_eq!(temporary_channel_id, expected_temporary_channel_id); @@ -1044,11 +1185,11 @@ pub fn sign_funding_transaction<'a, 'b, 'c>(node_a: &Node<'a, 'b, 'c>, node_b: & } // Receiver must have been initialized with manually_accept_inbound_channels set to true. -pub fn open_zero_conf_channel<'a, 'b, 'c, 'd>(initiator: &'a Node<'b, 'c, 'd>, receiver: &'a Node<'b, 'c, 'd>, initiator_config: Option) -> (bitcoin::Transaction, [u8; 32]) { +pub fn open_zero_conf_channel<'a, 'b, 'c, 'd>(initiator: &'a Node<'b, 'c, 'd>, receiver: &'a Node<'b, 'c, 'd>, initiator_config: Option) -> (bitcoin::Transaction, ChannelId) { let initiator_channels = initiator.node.list_usable_channels().len(); let receiver_channels = receiver.node.list_usable_channels().len(); - initiator.node.create_channel(receiver.node.get_our_node_id(), 100_000, 10_001, 42, initiator_config).unwrap(); + initiator.node.create_channel(receiver.node.get_our_node_id(), 100_000, 10_001, 42, None, initiator_config).unwrap(); let open_channel = get_event_msg!(initiator, MessageSendEvent::SendOpenChannel, receiver.node.get_our_node_id()); receiver.node.handle_open_channel(&initiator.node.get_our_node_id(), &open_channel); @@ -1113,8 +1254,8 @@ pub fn open_zero_conf_channel<'a, 'b, 'c, 'd>(initiator: &'a Node<'b, 'c, 'd>, r (tx, as_channel_ready.channel_id) } -pub fn create_chan_between_nodes_with_value_init<'a, 'b, 'c>(node_a: &Node<'a, 'b, 'c>, node_b: &Node<'a, 'b, 'c>, channel_value: u64, push_msat: u64) -> Transaction { - let create_chan_id = node_a.node.create_channel(node_b.node.get_our_node_id(), channel_value, push_msat, 42, None).unwrap(); +pub fn exchange_open_accept_chan<'a, 'b, 'c>(node_a: &Node<'a, 'b, 'c>, node_b: &Node<'a, 'b, 'c>, channel_value: u64, push_msat: u64) -> ChannelId { + let create_chan_id = node_a.node.create_channel(node_b.node.get_our_node_id(), channel_value, push_msat, 42, None, None).unwrap(); let open_channel_msg = get_event_msg!(node_a, MessageSendEvent::SendOpenChannel, node_b.node.get_our_node_id()); assert_eq!(open_channel_msg.temporary_channel_id, create_chan_id); assert_eq!(node_a.node.list_channels().iter().find(|channel| channel.channel_id == create_chan_id).unwrap().user_channel_id, 42); @@ -1133,6 +1274,11 @@ pub fn create_chan_between_nodes_with_value_init<'a, 'b, 'c>(node_a: &Node<'a, ' node_a.node.handle_accept_channel(&node_b.node.get_our_node_id(), &accept_channel_msg); assert_ne!(node_b.node.list_channels().iter().find(|channel| channel.channel_id == create_chan_id).unwrap().user_channel_id, 0); + create_chan_id +} + +pub fn create_chan_between_nodes_with_value_init<'a, 'b, 'c>(node_a: &Node<'a, 'b, 'c>, node_b: &Node<'a, 'b, 'c>, channel_value: u64, push_msat: u64) -> Transaction { + let create_chan_id = exchange_open_accept_chan(node_a, node_b, channel_value, push_msat); sign_funding_transaction(node_a, node_b, channel_value, create_chan_id) } @@ -1142,7 +1288,7 @@ pub fn create_chan_between_nodes_with_value_confirm_first<'a, 'b, 'c, 'd>(node_r node_recv.node.handle_channel_ready(&node_conf.node.get_our_node_id(), &get_event_msg!(node_conf, MessageSendEvent::SendChannelReady, node_recv.node.get_our_node_id())); } -pub fn create_chan_between_nodes_with_value_confirm_second<'a, 'b, 'c>(node_recv: &Node<'a, 'b, 'c>, node_conf: &Node<'a, 'b, 'c>) -> ((msgs::ChannelReady, msgs::AnnouncementSignatures), [u8; 32]) { +pub fn create_chan_between_nodes_with_value_confirm_second<'a, 'b, 'c>(node_recv: &Node<'a, 'b, 'c>, node_conf: &Node<'a, 'b, 'c>) -> ((msgs::ChannelReady, msgs::AnnouncementSignatures), ChannelId) { let channel_id; let events_6 = node_conf.node.get_and_clear_pending_msg_events(); assert_eq!(events_6.len(), 3); @@ -1169,7 +1315,7 @@ pub fn create_chan_between_nodes_with_value_confirm_second<'a, 'b, 'c>(node_recv }), channel_id) } -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::ChannelReady, msgs::AnnouncementSignatures), [u8; 32]) { +pub fn create_chan_between_nodes_with_value_confirm<'a, 'b, 'c: 'd, 'd>(node_a: &'a Node<'b, 'c, 'd>, node_b: &'a Node<'b, 'c, 'd>, tx: &Transaction) -> ((msgs::ChannelReady, msgs::AnnouncementSignatures), ChannelId) { let conf_height = core::cmp::max(node_a.best_block_info().1 + 1, node_b.best_block_info().1 + 1); create_chan_between_nodes_with_value_confirm_first(node_a, node_b, tx, conf_height); confirm_transaction_at(node_a, tx, conf_height); @@ -1178,7 +1324,7 @@ pub fn create_chan_between_nodes_with_value_confirm<'a, 'b, 'c, 'd>(node_a: &'a create_chan_between_nodes_with_value_confirm_second(node_b, node_a) } -pub fn create_chan_between_nodes_with_value_a<'a, 'b, 'c, 'd>(node_a: &'a Node<'b, 'c, 'd>, node_b: &'a Node<'b, 'c, 'd>, channel_value: u64, push_msat: u64) -> ((msgs::ChannelReady, msgs::AnnouncementSignatures), [u8; 32], Transaction) { +pub fn create_chan_between_nodes_with_value_a<'a, 'b, 'c: 'd, 'd>(node_a: &'a Node<'b, 'c, 'd>, node_b: &'a Node<'b, 'c, 'd>, channel_value: u64, push_msat: u64) -> ((msgs::ChannelReady, msgs::AnnouncementSignatures), ChannelId, Transaction) { let tx = create_chan_between_nodes_with_value_init(node_a, node_b, channel_value, push_msat); let (msgs, chan_id) = create_chan_between_nodes_with_value_confirm(node_a, node_b, &tx); (msgs, chan_id, tx) @@ -1218,11 +1364,11 @@ pub fn create_chan_between_nodes_with_value_b<'a, 'b, 'c>(node_a: &Node<'a, 'b, ((*announcement).clone(), as_update, bs_update) } -pub fn create_announced_chan_between_nodes<'a, 'b, 'c, 'd>(nodes: &'a Vec>, a: usize, b: usize) -> (msgs::ChannelUpdate, msgs::ChannelUpdate, [u8; 32], Transaction) { +pub fn create_announced_chan_between_nodes<'a, 'b, 'c: 'd, 'd>(nodes: &'a Vec>, a: usize, b: usize) -> (msgs::ChannelUpdate, msgs::ChannelUpdate, ChannelId, Transaction) { create_announced_chan_between_nodes_with_value(nodes, a, b, 100000, 10001) } -pub fn create_announced_chan_between_nodes_with_value<'a, 'b, 'c, 'd>(nodes: &'a Vec>, a: usize, b: usize, channel_value: u64, push_msat: u64) -> (msgs::ChannelUpdate, msgs::ChannelUpdate, [u8; 32], Transaction) { +pub fn create_announced_chan_between_nodes_with_value<'a, 'b, 'c: 'd, 'd>(nodes: &'a Vec>, a: usize, b: usize, channel_value: u64, push_msat: u64) -> (msgs::ChannelUpdate, msgs::ChannelUpdate, ChannelId, Transaction) { let chan_announcement = create_chan_between_nodes_with_value(&nodes[a], &nodes[b], channel_value, push_msat); update_nodes_with_chan_announce(nodes, a, b, &chan_announcement.0, &chan_announcement.1, &chan_announcement.2); (chan_announcement.1, chan_announcement.2, chan_announcement.3, chan_announcement.4) @@ -1231,7 +1377,7 @@ pub fn create_announced_chan_between_nodes_with_value<'a, 'b, 'c, 'd>(nodes: &'a pub fn create_unannounced_chan_between_nodes_with_value<'a, 'b, 'c, 'd>(nodes: &'a Vec>, a: usize, b: usize, channel_value: u64, push_msat: u64) -> (msgs::ChannelReady, Transaction) { let mut no_announce_cfg = test_default_channel_config(); no_announce_cfg.channel_handshake_config.announced_channel = false; - nodes[a].node.create_channel(nodes[b].node.get_our_node_id(), channel_value, push_msat, 42, Some(no_announce_cfg)).unwrap(); + nodes[a].node.create_channel(nodes[b].node.get_our_node_id(), channel_value, push_msat, 42, None, Some(no_announce_cfg)).unwrap(); let open_channel = get_event_msg!(nodes[a], MessageSendEvent::SendOpenChannel, nodes[b].node.get_our_node_id()); nodes[b].node.handle_open_channel(&nodes[a].node.get_our_node_id(), &open_channel); let accept_channel = get_event_msg!(nodes[b], MessageSendEvent::SendAcceptChannel, nodes[a].node.get_our_node_id()); @@ -1317,7 +1463,7 @@ pub fn do_check_spends Opti for output in tx.output.iter() { total_value_out += output.value; } - let min_fee = (tx.weight() as u64 + 3) / 4; // One sat per vbyte (ie per weight/4, rounded up) + let min_fee = (tx.weight().to_wu() as u64 + 3) / 4; // One sat per vbyte (ie per weight/4, rounded up) // Input amount - output amount = fee, so check that out + min_fee is smaller than input assert!(total_value_out + min_fee <= total_value_in); tx.verify(get_output).unwrap(); @@ -1396,10 +1542,15 @@ pub fn check_closed_broadcast(node: &Node, num_channels: usize, with_error_msg: assert_eq!(msg.contents.flags & 2, 2); None }, - MessageSendEvent::HandleError { action: msgs::ErrorAction::SendErrorMessage { ref msg }, node_id: _ } => { + MessageSendEvent::HandleError { action: msgs::ErrorAction::SendErrorMessage { msg }, node_id: _ } => { assert!(with_error_msg); // TODO: Check node_id - Some(msg.clone()) + Some(msg) + }, + MessageSendEvent::HandleError { action: msgs::ErrorAction::DisconnectPeer { msg }, node_id: _ } => { + assert!(with_error_msg); + // TODO: Check node_id + Some(msg.unwrap()) }, _ => panic!("Unexpected event"), } @@ -1417,23 +1568,87 @@ macro_rules! check_closed_broadcast { } } -/// Check that a channel's closing channel events has been issued -pub fn check_closed_event(node: &Node, events_count: usize, expected_reason: ClosureReason, is_check_discard_funding: bool) { - let events = node.node.get_and_clear_pending_events(); - assert_eq!(events.len(), events_count, "{:?}", events); - let mut issues_discard_funding = false; - for event in events { - match event { - Event::ChannelClosed { ref reason, .. } => { - assert_eq!(*reason, expected_reason); - }, - Event::DiscardFunding { .. } => { - issues_discard_funding = true; - } - _ => panic!("Unexpected event"), +#[derive(Default)] +pub struct ExpectedCloseEvent { + pub channel_capacity_sats: Option, + pub channel_id: Option, + pub counterparty_node_id: Option, + pub discard_funding: bool, + pub reason: Option, + pub channel_funding_txo: Option, + pub user_channel_id: Option, +} + +impl ExpectedCloseEvent { + pub fn from_id_reason(channel_id: ChannelId, discard_funding: bool, reason: ClosureReason) -> Self { + Self { + channel_capacity_sats: None, + channel_id: Some(channel_id), + counterparty_node_id: None, + discard_funding, + reason: Some(reason), + channel_funding_txo: None, + user_channel_id: None, } } - assert_eq!(is_check_discard_funding, issues_discard_funding); +} + +/// Check that multiple channel closing events have been issued. +pub fn check_closed_events(node: &Node, expected_close_events: &[ExpectedCloseEvent]) { + let closed_events_count = expected_close_events.len(); + let discard_events_count = expected_close_events.iter().filter(|e| e.discard_funding).count(); + let events = node.node.get_and_clear_pending_events(); + assert_eq!(events.len(), closed_events_count + discard_events_count, "{:?}", events); + for expected_event in expected_close_events { + assert!(events.iter().any(|e| matches!( + e, + Event::ChannelClosed { + channel_id, + reason, + counterparty_node_id, + channel_capacity_sats, + channel_funding_txo, + user_channel_id, + .. + } if ( + expected_event.channel_id.map(|expected| *channel_id == expected).unwrap_or(true) && + expected_event.reason.as_ref().map(|expected| reason == expected).unwrap_or(true) && + expected_event. + counterparty_node_id.map(|expected| *counterparty_node_id == Some(expected)).unwrap_or(true) && + expected_event.channel_capacity_sats + .map(|expected| *channel_capacity_sats == Some(expected)).unwrap_or(true) && + expected_event.channel_funding_txo + .map(|expected| *channel_funding_txo == Some(expected)).unwrap_or(true) && + expected_event.user_channel_id + .map(|expected| *user_channel_id == expected).unwrap_or(true) + ) + ))); + } + assert_eq!(events.iter().filter(|e| matches!( + e, + Event::DiscardFunding { .. }, + )).count(), discard_events_count); +} + +/// Check that a channel's closing channel events has been issued +pub fn check_closed_event(node: &Node, events_count: usize, expected_reason: ClosureReason, is_check_discard_funding: bool, + expected_counterparty_node_ids: &[PublicKey], expected_channel_capacity: u64) { + let expected_events_count = if is_check_discard_funding { + 2 * expected_counterparty_node_ids.len() + } else { + expected_counterparty_node_ids.len() + }; + assert_eq!(events_count, expected_events_count); + let expected_close_events = expected_counterparty_node_ids.iter().map(|node_id| ExpectedCloseEvent { + channel_capacity_sats: Some(expected_channel_capacity), + channel_id: None, + counterparty_node_id: Some(*node_id), + discard_funding: is_check_discard_funding, + reason: Some(expected_reason.clone()), + channel_funding_txo: None, + user_channel_id: None, + }).collect::>(); + check_closed_events(node, expected_close_events.as_slice()); } /// Check that a channel's closing channel events has been issued @@ -1441,15 +1656,31 @@ pub fn check_closed_event(node: &Node, events_count: usize, expected_reason: Clo /// Don't use this, use the identically-named function instead. #[macro_export] macro_rules! check_closed_event { - ($node: expr, $events: expr, $reason: expr) => { - check_closed_event!($node, $events, $reason, false); + ($node: expr, $events: expr, $reason: expr, $counterparty_node_ids: expr, $channel_capacity: expr) => { + check_closed_event!($node, $events, $reason, false, $counterparty_node_ids, $channel_capacity); }; - ($node: expr, $events: expr, $reason: expr, $is_check_discard_funding: expr) => { - $crate::ln::functional_test_utils::check_closed_event(&$node, $events, $reason, $is_check_discard_funding); + ($node: expr, $events: expr, $reason: expr, $is_check_discard_funding: expr, $counterparty_node_ids: expr, $channel_capacity: expr) => { + $crate::ln::functional_test_utils::check_closed_event(&$node, $events, $reason, + $is_check_discard_funding, &$counterparty_node_ids, $channel_capacity); + } +} + +pub fn handle_bump_htlc_event(node: &Node, count: usize) { + let events = node.chain_monitor.chain_monitor.get_and_clear_pending_events(); + assert_eq!(events.len(), count); + for event in events { + match event { + Event::BumpTransaction(bump_event) => { + if let BumpTransactionEvent::HTLCResolution { .. } = &bump_event {} + else { panic!(); } + node.bump_tx_handler.handle_event(&bump_event); + }, + _ => panic!(), + } } } -pub fn close_channel<'a, 'b, 'c>(outbound_node: &Node<'a, 'b, 'c>, inbound_node: &Node<'a, 'b, 'c>, channel_id: &[u8; 32], funding_tx: Transaction, close_inbound_first: bool) -> (msgs::ChannelUpdate, msgs::ChannelUpdate, Transaction) { +pub fn close_channel<'a, 'b, 'c>(outbound_node: &Node<'a, 'b, 'c>, inbound_node: &Node<'a, 'b, 'c>, channel_id: &ChannelId, funding_tx: Transaction, close_inbound_first: bool) -> (msgs::ChannelUpdate, msgs::ChannelUpdate, Transaction) { let (node_a, broadcaster_a, struct_a) = if close_inbound_first { (&inbound_node.node, &inbound_node.tx_broadcaster, inbound_node) } else { (&outbound_node.node, &outbound_node.tx_broadcaster, outbound_node) }; let (node_b, broadcaster_b, struct_b) = if close_inbound_first { (&outbound_node.node, &outbound_node.tx_broadcaster, outbound_node) } else { (&inbound_node.node, &inbound_node.tx_broadcaster, inbound_node) }; let (tx_a, tx_b); @@ -1670,8 +1901,8 @@ macro_rules! commitment_signed_dance { bs_revoke_and_ack } }; - ($node_a: expr, $node_b: expr, (), $fail_backwards: expr, true /* skip last step */, false /* no extra message */) => { - assert!($crate::ln::functional_test_utils::commitment_signed_dance_through_cp_raa(&$node_a, &$node_b, $fail_backwards).is_none()); + ($node_a: expr, $node_b: expr, (), $fail_backwards: expr, true /* skip last step */, false /* no extra message */, $incl_claim: expr) => { + assert!($crate::ln::functional_test_utils::commitment_signed_dance_through_cp_raa(&$node_a, &$node_b, $fail_backwards, $incl_claim).is_none()); }; ($node_a: expr, $node_b: expr, $commitment_signed: expr, $fail_backwards: expr) => { $crate::ln::functional_test_utils::do_commitment_signed_dance(&$node_a, &$node_b, &$commitment_signed, $fail_backwards, false); @@ -1682,11 +1913,16 @@ macro_rules! commitment_signed_dance { /// the initiator's `revoke_and_ack` response. i.e. [`do_main_commitment_signed_dance`] plus the /// `revoke_and_ack` response to it. /// +/// An HTLC claim on one channel blocks the RAA channel monitor update for the outbound edge +/// channel until the inbound edge channel preimage monitor update completes. Thus, when checking +/// for channel monitor updates, we need to know if an `update_fulfill_htlc` was included in the +/// the commitment we're exchanging. `includes_claim` provides that information. +/// /// Returns any additional message `node_b` generated in addition to the `revoke_and_ack` response. -pub fn commitment_signed_dance_through_cp_raa(node_a: &Node<'_, '_, '_>, node_b: &Node<'_, '_, '_>, fail_backwards: bool) -> Option { +pub fn commitment_signed_dance_through_cp_raa(node_a: &Node<'_, '_, '_>, node_b: &Node<'_, '_, '_>, fail_backwards: bool, includes_claim: bool) -> Option { let (extra_msg_option, bs_revoke_and_ack) = do_main_commitment_signed_dance(node_a, node_b, fail_backwards); node_a.node.handle_revoke_and_ack(&node_b.node.get_our_node_id(), &bs_revoke_and_ack); - check_added_monitors(node_a, 1); + check_added_monitors(node_a, if includes_claim { 0 } else { 1 }); extra_msg_option } @@ -1733,7 +1969,10 @@ pub fn do_commitment_signed_dance(node_a: &Node<'_, '_, '_>, node_b: &Node<'_, ' node_a.node.handle_commitment_signed(&node_b.node.get_our_node_id(), commitment_signed); check_added_monitors!(node_a, 1); - commitment_signed_dance!(node_a, node_b, (), fail_backwards, true, false); + // If this commitment signed dance was due to a claim, don't check for an RAA monitor update. + let got_claim = node_a.node.test_raa_monitor_updates_held(node_b.node.get_our_node_id(), commitment_signed.channel_id); + if fail_backwards { assert!(!got_claim); } + commitment_signed_dance!(node_a, node_b, (), fail_backwards, true, false, got_claim); if skip_last_step { return; } @@ -1766,7 +2005,7 @@ pub fn get_payment_preimage_hash(recipient: &Node, min_value_msat: Option, let mut payment_count = recipient.network_payment_count.borrow_mut(); let payment_preimage = PaymentPreimage([*payment_count; 32]); *payment_count += 1; - let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0[..]).into_inner()); + let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0[..]).to_byte_array()); let payment_secret = recipient.node.create_inbound_payment_for_hash(payment_hash, min_value_msat, 7200, min_final_cltv_expiry_delta).unwrap(); (payment_preimage, payment_hash, payment_secret) } @@ -1788,14 +2027,26 @@ macro_rules! get_payment_preimage_hash { } /// Gets a route from the given sender to the node described in `payment_params`. -pub fn get_route(send_node: &Node, payment_params: &PaymentParameters, recv_value: u64) -> Result { +pub fn get_route(send_node: &Node, route_params: &RouteParameters) -> Result { let scorer = TestScorer::new(); let keys_manager = TestKeysInterface::new(&[0u8; 32], bitcoin::network::constants::Network::Testnet); let random_seed_bytes = keys_manager.get_secure_random_bytes(); router::get_route( - &send_node.node.get_our_node_id(), payment_params, &send_node.network_graph.read_only(), + &send_node.node.get_our_node_id(), route_params, &send_node.network_graph.read_only(), + Some(&send_node.node.list_usable_channels().iter().collect::>()), + send_node.logger, &scorer, &Default::default(), &random_seed_bytes + ) +} + +/// Like `get_route` above, but adds a random CLTV offset to the final hop. +pub fn find_route(send_node: &Node, route_params: &RouteParameters) -> Result { + let scorer = TestScorer::new(); + let keys_manager = TestKeysInterface::new(&[0u8; 32], bitcoin::network::constants::Network::Testnet); + let random_seed_bytes = keys_manager.get_secure_random_bytes(); + router::find_route( + &send_node.node.get_our_node_id(), route_params, &send_node.network_graph, Some(&send_node.node.list_usable_channels().iter().collect::>()), - recv_value, send_node.logger, &scorer, &(), &random_seed_bytes + send_node.logger, &scorer, &Default::default(), &random_seed_bytes ) } @@ -1804,9 +2055,10 @@ pub fn get_route(send_node: &Node, payment_params: &PaymentParameters, recv_valu /// Don't use this, use the identically-named function instead. #[macro_export] macro_rules! get_route { - ($send_node: expr, $payment_params: expr, $recv_value: expr) => { - $crate::ln::functional_test_utils::get_route(&$send_node, &$payment_params, $recv_value) - } + ($send_node: expr, $payment_params: expr, $recv_value: expr) => {{ + let route_params = $crate::routing::router::RouteParameters::from_payment_params_and_value($payment_params, $recv_value); + $crate::ln::functional_test_utils::get_route(&$send_node, &route_params) + }} } #[cfg(test)] @@ -1814,13 +2066,18 @@ macro_rules! get_route { macro_rules! get_route_and_payment_hash { ($send_node: expr, $recv_node: expr, $recv_value: expr) => {{ let payment_params = $crate::routing::router::PaymentParameters::from_node_id($recv_node.node.get_our_node_id(), TEST_FINAL_CLTV) - .with_bolt11_features($recv_node.node.invoice_features()).unwrap(); + .with_bolt11_features($recv_node.node.bolt11_invoice_features()).unwrap(); $crate::get_route_and_payment_hash!($send_node, $recv_node, payment_params, $recv_value) }}; ($send_node: expr, $recv_node: expr, $payment_params: expr, $recv_value: expr) => {{ + $crate::get_route_and_payment_hash!($send_node, $recv_node, $payment_params, $recv_value, None) + }}; + ($send_node: expr, $recv_node: expr, $payment_params: expr, $recv_value: expr, $max_total_routing_fee_msat: expr) => {{ + let mut route_params = $crate::routing::router::RouteParameters::from_payment_params_and_value($payment_params, $recv_value); + route_params.max_total_routing_fee_msat = $max_total_routing_fee_msat; let (payment_preimage, payment_hash, payment_secret) = $crate::ln::functional_test_utils::get_payment_preimage_hash(&$recv_node, Some($recv_value), None); - let route = $crate::ln::functional_test_utils::get_route(&$send_node, &$payment_params, $recv_value); + let route = $crate::ln::functional_test_utils::get_route(&$send_node, &route_params); (route.unwrap(), payment_hash, payment_preimage, payment_secret) }} } @@ -1878,16 +2135,19 @@ macro_rules! expect_payment_claimed { pub fn expect_payment_sent>(node: &H, expected_payment_preimage: PaymentPreimage, expected_fee_msat_opt: Option>, - expect_per_path_claims: bool, + expect_per_path_claims: bool, expect_post_ev_mon_update: bool, ) { let events = node.node().get_and_clear_pending_events(); let expected_payment_hash = PaymentHash( - bitcoin::hashes::sha256::Hash::hash(&expected_payment_preimage.0).into_inner()); + bitcoin::hashes::sha256::Hash::hash(&expected_payment_preimage.0).to_byte_array()); if expect_per_path_claims { assert!(events.len() > 1); } else { assert_eq!(events.len(), 1); } + if expect_post_ev_mon_update { + check_added_monitors(node, 1); + } let expected_payment_id = match events[0] { Event::PaymentSent { ref payment_id, ref payment_preimage, ref payment_hash, ref fee_paid_msat } => { assert_eq!(expected_payment_preimage, *payment_preimage); @@ -1914,17 +2174,6 @@ pub fn expect_payment_sent>(node: &H, } } -#[cfg(test)] -#[macro_export] -macro_rules! expect_payment_sent_without_paths { - ($node: expr, $expected_payment_preimage: expr) => { - expect_payment_sent!($node, $expected_payment_preimage, None::, false); - }; - ($node: expr, $expected_payment_preimage: expr, $expected_fee_msat_opt: expr) => { - expect_payment_sent!($node, $expected_payment_preimage, $expected_fee_msat_opt, false); - } -} - #[macro_export] macro_rules! expect_payment_sent { ($node: expr, $expected_payment_preimage: expr) => { @@ -1935,7 +2184,7 @@ macro_rules! expect_payment_sent { }; ($node: expr, $expected_payment_preimage: expr, $expected_fee_msat_opt: expr, $expect_paths: expr) => { $crate::ln::functional_test_utils::expect_payment_sent(&$node, $expected_payment_preimage, - $expected_fee_msat_opt.map(|o| Some(o)), $expect_paths); + $expected_fee_msat_opt.map(|o| Some(o)), $expect_paths, true); } } @@ -1952,29 +2201,45 @@ macro_rules! expect_payment_path_successful { } } +pub fn expect_payment_forwarded>( + event: Event, node: &H, prev_node: &H, next_node: &H, expected_fee: Option, + expected_extra_fees_msat: Option, upstream_force_closed: bool, + downstream_force_closed: bool +) { + match event { + Event::PaymentForwarded { + total_fee_earned_msat, prev_channel_id, claim_from_onchain_tx, next_channel_id, + outbound_amount_forwarded_msat: _, skimmed_fee_msat + } => { + assert_eq!(total_fee_earned_msat, expected_fee); + + // Check that the (knowingly) withheld amount is always less or equal to the expected + // overpaid amount. + assert!(skimmed_fee_msat == expected_extra_fees_msat); + if !upstream_force_closed { + // Is the event prev_channel_id in one of the channels between the two nodes? + assert!(node.node().list_channels().iter().any(|x| x.counterparty.node_id == prev_node.node().get_our_node_id() && x.channel_id == prev_channel_id.unwrap())); + } + // We check for force closures since a force closed channel is removed from the + // node's channel list + if !downstream_force_closed { + assert!(node.node().list_channels().iter().any(|x| x.counterparty.node_id == next_node.node().get_our_node_id() && x.channel_id == next_channel_id.unwrap())); + } + assert_eq!(claim_from_onchain_tx, downstream_force_closed); + }, + _ => panic!("Unexpected event"), + } +} + +#[macro_export] macro_rules! expect_payment_forwarded { ($node: expr, $prev_node: expr, $next_node: expr, $expected_fee: expr, $upstream_force_closed: expr, $downstream_force_closed: expr) => { - let events = $node.node.get_and_clear_pending_events(); + let mut events = $node.node.get_and_clear_pending_events(); assert_eq!(events.len(), 1); - match events[0] { - Event::PaymentForwarded { - fee_earned_msat, prev_channel_id, claim_from_onchain_tx, next_channel_id, - outbound_amount_forwarded_msat: _ - } => { - assert_eq!(fee_earned_msat, $expected_fee); - if fee_earned_msat.is_some() { - // Is the event prev_channel_id in one of the channels between the two nodes? - assert!($node.node.list_channels().iter().any(|x| x.counterparty.node_id == $prev_node.node.get_our_node_id() && x.channel_id == prev_channel_id.unwrap())); - } - // We check for force closures since a force closed channel is removed from the - // node's channel list - if !$downstream_force_closed { - assert!($node.node.list_channels().iter().any(|x| x.counterparty.node_id == $next_node.node.get_our_node_id() && x.channel_id == next_channel_id.unwrap())); - } - assert_eq!(claim_from_onchain_tx, $downstream_force_closed); - }, - _ => panic!("Unexpected event"), - } + $crate::ln::functional_test_utils::expect_payment_forwarded( + events.pop().unwrap(), &$node, &$prev_node, &$next_node, $expected_fee, None, + $upstream_force_closed, $downstream_force_closed + ); } } @@ -1989,12 +2254,13 @@ macro_rules! expect_channel_shutdown_state { } #[cfg(any(test, ldk_bench, feature = "_test_utils"))] -pub fn expect_channel_pending_event<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, expected_counterparty_node_id: &PublicKey) { +pub fn expect_channel_pending_event<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, expected_counterparty_node_id: &PublicKey) -> ChannelId { let events = node.node.get_and_clear_pending_events(); assert_eq!(events.len(), 1); - match events[0] { - crate::events::Event::ChannelPending { ref counterparty_node_id, .. } => { + match &events[0] { + crate::events::Event::ChannelPending { channel_id, counterparty_node_id, .. } => { assert_eq!(*expected_counterparty_node_id, *counterparty_node_id); + *channel_id }, _ => panic!("Unexpected event"), } @@ -2012,6 +2278,26 @@ pub fn expect_channel_ready_event<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, ex } } +#[cfg(any(test, feature = "_test_utils"))] +pub fn expect_probe_successful_events(node: &Node, mut probe_results: Vec<(PaymentHash, PaymentId)>) { + let mut events = node.node.get_and_clear_pending_events(); + + for event in events.drain(..) { + match event { + Event::ProbeSuccessful { payment_hash: ev_ph, payment_id: ev_pid, ..} => { + let result_idx = probe_results.iter().position(|(payment_hash, payment_id)| *payment_hash == ev_ph && *payment_id == ev_pid); + assert!(result_idx.is_some()); + + probe_results.remove(result_idx.unwrap()); + }, + _ => panic!(), + } + }; + + // Ensure that we received a ProbeSuccessful event for each probe result. + assert!(probe_results.is_empty()); +} + pub struct PaymentFailedConditions<'a> { pub(crate) expected_htlc_error_data: Option<(u16, &'a [u8])>, pub(crate) expected_blamed_scid: Option, @@ -2149,21 +2435,41 @@ pub fn send_along_route_with_secret<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, payment_id } -pub fn do_pass_along_path<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_path: &[&Node<'a, 'b, 'c>], recv_value: u64, our_payment_hash: PaymentHash, our_payment_secret: Option, ev: MessageSendEvent, payment_claimable_expected: bool, clear_recipient_events: bool, expected_preimage: Option) -> Option { +fn fail_payment_along_path<'a, 'b, 'c>(expected_path: &[&Node<'a, 'b, 'c>]) { + let origin_node_id = expected_path[0].node.get_our_node_id(); + + // iterate from the receiving node to the origin node and handle update fail htlc. + for (&node, &prev_node) in expected_path.iter().rev().zip(expected_path.iter().rev().skip(1)) { + let updates = get_htlc_update_msgs!(node, prev_node.node.get_our_node_id()); + prev_node.node.handle_update_fail_htlc(&node.node.get_our_node_id(), &updates.update_fail_htlcs[0]); + check_added_monitors!(prev_node, 0); + + let is_first_hop = origin_node_id == prev_node.node.get_our_node_id(); + // We do not want to fail backwards on the first hop. All other hops should fail backwards. + commitment_signed_dance!(prev_node, node, updates.commitment_signed, !is_first_hop); + } +} + +pub fn do_pass_along_path<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_path: &[&Node<'a, 'b, 'c>], recv_value: u64, our_payment_hash: PaymentHash, our_payment_secret: Option, ev: MessageSendEvent, payment_claimable_expected: bool, clear_recipient_events: bool, expected_preimage: Option, is_probe: bool) -> Option { let mut payment_event = SendEvent::from_event(ev); let mut prev_node = origin_node; let mut event = None; for (idx, &node) in expected_path.iter().enumerate() { + let is_last_hop = idx == expected_path.len() - 1; assert_eq!(node.node.get_our_node_id(), payment_event.node_id); node.node.handle_update_add_htlc(&prev_node.node.get_our_node_id(), &payment_event.msgs[0]); check_added_monitors!(node, 0); - commitment_signed_dance!(node, prev_node, payment_event.commitment_msg, false); - expect_pending_htlcs_forwardable!(node); + if is_last_hop && is_probe { + commitment_signed_dance!(node, prev_node, payment_event.commitment_msg, true, true); + } else { + commitment_signed_dance!(node, prev_node, payment_event.commitment_msg, false); + expect_pending_htlcs_forwardable!(node); + } - if idx == expected_path.len() - 1 && clear_recipient_events { + if is_last_hop && clear_recipient_events { let events_2 = node.node.get_and_clear_pending_events(); if payment_claimable_expected { assert_eq!(events_2.len(), 1); @@ -2197,7 +2503,7 @@ pub fn do_pass_along_path<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_p } else { assert!(events_2.is_empty()); } - } else if idx != expected_path.len() - 1 { + } else if !is_last_hop { let mut events_2 = node.node.get_and_clear_pending_msg_events(); assert_eq!(events_2.len(), 1); check_added_monitors!(node, 1); @@ -2211,16 +2517,33 @@ pub fn do_pass_along_path<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_p } pub fn pass_along_path<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_path: &[&Node<'a, 'b, 'c>], recv_value: u64, our_payment_hash: PaymentHash, our_payment_secret: Option, ev: MessageSendEvent, payment_claimable_expected: bool, expected_preimage: Option) -> Option { - do_pass_along_path(origin_node, expected_path, recv_value, our_payment_hash, our_payment_secret, ev, payment_claimable_expected, true, expected_preimage) + do_pass_along_path(origin_node, expected_path, recv_value, our_payment_hash, our_payment_secret, ev, payment_claimable_expected, true, expected_preimage, false) +} + +pub fn send_probe_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&[&Node<'a, 'b, 'c>]]) { + let mut events = origin_node.node.get_and_clear_pending_msg_events(); + assert_eq!(events.len(), expected_route.len()); + + check_added_monitors!(origin_node, expected_route.len()); + + for path in expected_route.iter() { + let ev = remove_first_msg_event_to_node(&path[0].node.get_our_node_id(), &mut events); + + do_pass_along_path(origin_node, path, 0, PaymentHash([0_u8; 32]), None, ev, false, false, None, true); + let nodes_to_fail_payment: Vec<_> = vec![origin_node].into_iter().chain(path.iter().cloned()).collect(); + + fail_payment_along_path(nodes_to_fail_payment.as_slice()); + } } pub fn pass_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&[&Node<'a, 'b, 'c>]], recv_value: u64, our_payment_hash: PaymentHash, our_payment_secret: PaymentSecret) { let mut events = origin_node.node.get_and_clear_pending_msg_events(); assert_eq!(events.len(), expected_route.len()); + for (path_idx, expected_path) in expected_route.iter().enumerate() { let ev = remove_first_msg_event_to_node(&expected_path[0].node.get_our_node_id(), &mut events); // Once we've gotten through all the HTLCs, the last one should result in a - // PaymentClaimable (but each previous one should not!), . + // PaymentClaimable (but each previous one should not!). let expect_payment = path_idx == expected_route.len() - 1; pass_along_path(origin_node, expected_path, recv_value, our_payment_hash.clone(), Some(our_payment_secret), ev, expect_payment, None); } @@ -2236,29 +2559,85 @@ pub fn do_claim_payment_along_route<'a, 'b, 'c>( origin_node: &Node<'a, 'b, 'c>, expected_paths: &[&[&Node<'a, 'b, 'c>]], skip_last: bool, our_payment_preimage: PaymentPreimage ) -> u64 { - let extra_fees = vec![0; expected_paths.len()]; - do_claim_payment_along_route_with_extra_penultimate_hop_fees(origin_node, expected_paths, - &extra_fees[..], skip_last, our_payment_preimage) -} - -pub fn do_claim_payment_along_route_with_extra_penultimate_hop_fees<'a, 'b, 'c>( - origin_node: &Node<'a, 'b, 'c>, expected_paths: &[&[&Node<'a, 'b, 'c>]], expected_extra_fees: - &[u32], skip_last: bool, our_payment_preimage: PaymentPreimage -) -> u64 { - assert_eq!(expected_paths.len(), expected_extra_fees.len()); for path in expected_paths.iter() { assert_eq!(path.last().unwrap().node.get_our_node_id(), expected_paths[0].last().unwrap().node.get_our_node_id()); } expected_paths[0].last().unwrap().node.claim_funds(our_payment_preimage); + pass_claimed_payment_along_route( + ClaimAlongRouteArgs::new(origin_node, expected_paths, our_payment_preimage) + .skip_last(skip_last) + ) +} +pub struct ClaimAlongRouteArgs<'a, 'b, 'c, 'd> { + pub origin_node: &'a Node<'b, 'c, 'd>, + pub expected_paths: &'a [&'a [&'a Node<'b, 'c, 'd>]], + pub expected_extra_fees: Vec, + pub expected_min_htlc_overpay: Vec, + pub skip_last: bool, + pub payment_preimage: PaymentPreimage, +} + +impl<'a, 'b, 'c, 'd> ClaimAlongRouteArgs<'a, 'b, 'c, 'd> { + pub fn new( + origin_node: &'a Node<'b, 'c, 'd>, expected_paths: &'a [&'a [&'a Node<'b, 'c, 'd>]], + payment_preimage: PaymentPreimage, + ) -> Self { + Self { + origin_node, expected_paths, expected_extra_fees: vec![0; expected_paths.len()], + expected_min_htlc_overpay: vec![0; expected_paths.len()], skip_last: false, payment_preimage, + } + } + pub fn skip_last(mut self, skip_last: bool) -> Self { + self.skip_last = skip_last; + self + } + pub fn with_expected_extra_fees(mut self, extra_fees: Vec) -> Self { + self.expected_extra_fees = extra_fees; + self + } + pub fn with_expected_min_htlc_overpay(mut self, extra_fees: Vec) -> Self { + self.expected_min_htlc_overpay = extra_fees; + self + } +} + +pub fn pass_claimed_payment_along_route<'a, 'b, 'c, 'd>(args: ClaimAlongRouteArgs) -> u64 { + let ClaimAlongRouteArgs { + origin_node, expected_paths, expected_extra_fees, expected_min_htlc_overpay, skip_last, + payment_preimage: our_payment_preimage + } = args; let claim_event = expected_paths[0].last().unwrap().node.get_and_clear_pending_events(); assert_eq!(claim_event.len(), 1); match claim_event[0] { - Event::PaymentClaimed { purpose: PaymentPurpose::SpontaneousPayment(preimage), .. }| - Event::PaymentClaimed { purpose: PaymentPurpose::InvoicePayment { payment_preimage: Some(preimage), ..}, .. } => - assert_eq!(preimage, our_payment_preimage), - Event::PaymentClaimed { purpose: PaymentPurpose::InvoicePayment { .. }, payment_hash, .. } => - assert_eq!(&payment_hash.0, &Sha256::hash(&our_payment_preimage.0)[..]), + Event::PaymentClaimed { + purpose: PaymentPurpose::SpontaneousPayment(preimage), + amount_msat, + ref htlcs, + .. } + | Event::PaymentClaimed { + purpose: PaymentPurpose::InvoicePayment { payment_preimage: Some(preimage), ..}, + ref htlcs, + amount_msat, + .. + } => { + assert_eq!(preimage, our_payment_preimage); + assert_eq!(htlcs.len(), expected_paths.len()); // One per path. + assert_eq!(htlcs.iter().map(|h| h.value_msat).sum::(), amount_msat); + expected_paths.iter().zip(htlcs).for_each(|(path, htlc)| check_claimed_htlc_channel(origin_node, path, htlc)); + }, + Event::PaymentClaimed { + purpose: PaymentPurpose::InvoicePayment { .. }, + payment_hash, + amount_msat, + ref htlcs, + .. + } => { + assert_eq!(&payment_hash.0, &Sha256::hash(&our_payment_preimage.0)[..]); + assert_eq!(htlcs.len(), expected_paths.len()); // One per path. + assert_eq!(htlcs.iter().map(|h| h.value_msat).sum::(), amount_msat); + expected_paths.iter().zip(htlcs).for_each(|(path, htlc)| check_claimed_htlc_channel(origin_node, path, htlc)); + } _ => panic!(), } @@ -2318,14 +2697,23 @@ pub fn do_claim_payment_along_route_with_extra_penultimate_hop_fees<'a, 'b, 'c>( let peer_state = per_peer_state.get(&$prev_node.node.get_our_node_id()) .unwrap().lock().unwrap(); let channel = peer_state.channel_by_id.get(&next_msgs.as_ref().unwrap().0.channel_id).unwrap(); - if let Some(prev_config) = channel.context.prev_config() { + if let Some(prev_config) = channel.context().prev_config() { prev_config.forwarding_fee_base_msat } else { - channel.context.config().forwarding_fee_base_msat + channel.context().config().forwarding_fee_base_msat } }; - if $idx == 1 { fee += expected_extra_fees[i]; } - expect_payment_forwarded!($node, $next_node, $prev_node, Some(fee as u64), false, false); + + let mut expected_extra_fee = None; + if $idx == 1 { + fee += expected_extra_fees[i]; + fee += expected_min_htlc_overpay[i]; + expected_extra_fee = if expected_extra_fees[i] > 0 { Some(expected_extra_fees[i] as u64) } else { None }; + } + let mut events = $node.node.get_and_clear_pending_events(); + assert_eq!(events.len(), 1); + expect_payment_forwarded(events.pop().unwrap(), *$node, $next_node, $prev_node, + Some(fee as u64), expected_extra_fee, false, false); expected_total_fee_msat += fee as u64; check_added_monitors!($node, 1); let new_next_msgs = if $new_msgs { @@ -2393,10 +2781,11 @@ pub fn claim_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: pub const TEST_FINAL_CLTV: u32 = 70; -pub fn route_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) -> (PaymentPreimage, PaymentHash, PaymentSecret) { +pub fn route_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) -> (PaymentPreimage, PaymentHash, PaymentSecret, PaymentId) { let payment_params = PaymentParameters::from_node_id(expected_route.last().unwrap().node.get_our_node_id(), TEST_FINAL_CLTV) - .with_bolt11_features(expected_route.last().unwrap().node.invoice_features()).unwrap(); - let route = get_route(origin_node, &payment_params, recv_value).unwrap(); + .with_bolt11_features(expected_route.last().unwrap().node.bolt11_invoice_features()).unwrap(); + let route_params = RouteParameters::from_payment_params_and_value(payment_params, recv_value); + let route = get_route(origin_node, &route_params).unwrap(); assert_eq!(route.paths.len(), 1); assert_eq!(route.paths[0].hops.len(), expected_route.len()); for (node, hop) in expected_route.iter().zip(route.paths[0].hops.iter()) { @@ -2404,20 +2793,20 @@ pub fn route_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: } let res = send_along_route(origin_node, route, expected_route, recv_value); - (res.0, res.1, res.2) + (res.0, res.1, res.2, res.3) } pub fn route_over_limit<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) { let payment_params = PaymentParameters::from_node_id(expected_route.last().unwrap().node.get_our_node_id(), TEST_FINAL_CLTV) - .with_bolt11_features(expected_route.last().unwrap().node.invoice_features()).unwrap(); + .with_bolt11_features(expected_route.last().unwrap().node.bolt11_invoice_features()).unwrap(); + let route_params = RouteParameters::from_payment_params_and_value(payment_params, recv_value); let network_graph = origin_node.network_graph.read_only(); let scorer = test_utils::TestScorer::new(); let seed = [0u8; 32]; let keys_manager = test_utils::TestKeysInterface::new(&seed, Network::Testnet); let random_seed_bytes = keys_manager.get_secure_random_bytes(); - let route = router::get_route( - &origin_node.node.get_our_node_id(), &payment_params, &network_graph, - None, recv_value, origin_node.logger, &scorer, &(), &random_seed_bytes).unwrap(); + let route = router::get_route(&origin_node.node.get_our_node_id(), &route_params, &network_graph, + None, origin_node.logger, &scorer, &Default::default(), &random_seed_bytes).unwrap(); assert_eq!(route.paths.len(), 1); assert_eq!(route.paths[0].hops.len(), expected_route.len()); for (node, hop) in expected_route.iter().zip(route.paths[0].hops.iter()) { @@ -2431,7 +2820,7 @@ pub fn route_over_limit<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_rou assert!(err.contains("Cannot send value that would put us over the max HTLC value in flight our peer will accept"))); } -pub fn send_payment<'a, 'b, 'c>(origin: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) -> (PaymentPreimage, PaymentHash, PaymentSecret) { +pub fn send_payment<'a, 'b, 'c>(origin: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) -> (PaymentPreimage, PaymentHash, PaymentSecret, PaymentId) { let res = route_payment(&origin, expected_route, recv_value); claim_payment(&origin, expected_route, res.0); res @@ -2566,7 +2955,7 @@ pub fn create_chanmon_cfgs(node_count: usize) -> Vec { let persister = test_utils::TestPersister::new(); let seed = [i as u8; 32]; let keys_manager = test_utils::TestKeysInterface::new(&seed, Network::Testnet); - let scorer = Mutex::new(test_utils::TestScorer::new()); + let scorer = RwLock::new(test_utils::TestScorer::new()); chan_mon_cfgs.push(TestChanMonCfg { tx_broadcaster, fee_estimator, chain_source, logger, persister, keys_manager, scorer }); } @@ -2575,10 +2964,14 @@ pub fn create_chanmon_cfgs(node_count: usize) -> Vec { } pub fn create_node_cfgs<'a>(node_count: usize, chanmon_cfgs: &'a Vec) -> Vec> { + create_node_cfgs_with_persisters(node_count, chanmon_cfgs, chanmon_cfgs.iter().map(|c| &c.persister).collect()) +} + +pub fn create_node_cfgs_with_persisters<'a>(node_count: usize, chanmon_cfgs: &'a Vec, persisters: Vec<&'a impl Persist>) -> Vec> { let mut nodes = Vec::new(); for i in 0..node_count { - let chain_monitor = test_utils::TestChainMonitor::new(Some(&chanmon_cfgs[i].chain_source), &chanmon_cfgs[i].tx_broadcaster, &chanmon_cfgs[i].logger, &chanmon_cfgs[i].fee_estimator, &chanmon_cfgs[i].persister, &chanmon_cfgs[i].keys_manager); + let chain_monitor = test_utils::TestChainMonitor::new(Some(&chanmon_cfgs[i].chain_source), &chanmon_cfgs[i].tx_broadcaster, &chanmon_cfgs[i].logger, &chanmon_cfgs[i].fee_estimator, persisters[i], &chanmon_cfgs[i].keys_manager); let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &chanmon_cfgs[i].logger)); let seed = [i as u8; 32]; nodes.push(NodeCfg { @@ -2586,7 +2979,8 @@ pub fn create_node_cfgs<'a>(node_count: usize, chanmon_cfgs: &'a Vec(node_count: usize, cfgs: &'b Vec(node_count: usize, cfgs: &'b Vec(node_count: usize, cfgs: &'b Vec(node_count: usize, cfgs: &'b Vec(node: &Node<'a, 'b, 'c>, chan: &(msgs::ChannelUpdate, msgs::ChannelUpdate, [u8; 32], Transaction), commitment_tx: Option, has_htlc_tx: HTLCType) -> Vec { +pub fn test_txn_broadcast<'a, 'b, 'c>(node: &Node<'a, 'b, 'c>, chan: &(msgs::ChannelUpdate, msgs::ChannelUpdate, ChannelId, Transaction), commitment_tx: Option, has_htlc_tx: HTLCType) -> Vec { let mut node_txn = node.tx_broadcaster.txn_broadcasted.lock().unwrap(); let mut txn_seen = HashSet::new(); node_txn.retain(|tx| txn_seen.insert(tx.txid())); @@ -2721,9 +3129,9 @@ pub fn test_txn_broadcast<'a, 'b, 'c>(node: &Node<'a, 'b, 'c>, chan: &(msgs::Cha if tx.input.len() == 1 && tx.input[0].previous_output.txid == res[0].txid() { check_spends!(tx, res[0]); if has_htlc_tx == HTLCType::TIMEOUT { - assert!(tx.lock_time.0 != 0); + assert_ne!(tx.lock_time, LockTime::ZERO); } else { - assert!(tx.lock_time.0 == 0); + assert_eq!(tx.lock_time, LockTime::ZERO); } res.push(tx.clone()); false @@ -2804,6 +3212,13 @@ pub fn handle_announce_close_broadcast_events<'a, 'b, 'c>(nodes: &Vec { + assert_eq!(node_id, nodes[b].node.get_our_node_id()); + assert_eq!(msg.as_ref().unwrap().data, expected_error); + if needs_err_handle { + nodes[b].node.handle_error(&nodes[a].node.get_our_node_id(), msg.as_ref().unwrap()); + } + }, _ => panic!("Unexpected event"), } @@ -2821,6 +3236,10 @@ pub fn handle_announce_close_broadcast_events<'a, 'b, 'c>(nodes: &Vec { + assert_eq!(node_id, nodes[a].node.get_our_node_id()); + assert_eq!(msg.as_ref().unwrap().data, expected_error); + }, _ => panic!("Unexpected event"), } } @@ -2840,7 +3259,9 @@ macro_rules! get_channel_value_stat { ($node: expr, $counterparty_node: expr, $channel_id: expr) => {{ let peer_state_lock = $node.node.per_peer_state.read().unwrap(); let chan_lock = peer_state_lock.get(&$counterparty_node.node.get_our_node_id()).unwrap().lock().unwrap(); - let chan = chan_lock.channel_by_id.get(&$channel_id).unwrap(); + let chan = chan_lock.channel_by_id.get(&$channel_id).map( + |phase| if let ChannelPhase::Funded(chan) = phase { Some(chan) } else { None } + ).flatten().unwrap(); chan.get_value_stat() }} } @@ -2945,9 +3366,48 @@ macro_rules! handle_chan_reestablish_msgs { } } +pub struct ReconnectArgs<'a, 'b, 'c, 'd> { + pub node_a: &'a Node<'b, 'c, 'd>, + pub node_b: &'a Node<'b, 'c, 'd>, + pub send_channel_ready: (bool, bool), + pub pending_responding_commitment_signed: (bool, bool), + /// Indicates that the pending responding commitment signed will be a dup for the recipient, + /// and no monitor update is expected + pub pending_responding_commitment_signed_dup_monitor: (bool, bool), + pub pending_htlc_adds: (usize, usize), + pub pending_htlc_claims: (usize, usize), + pub pending_htlc_fails: (usize, usize), + pub pending_cell_htlc_claims: (usize, usize), + pub pending_cell_htlc_fails: (usize, usize), + pub pending_raa: (bool, bool), +} + +impl<'a, 'b, 'c, 'd> ReconnectArgs<'a, 'b, 'c, 'd> { + pub fn new(node_a: &'a Node<'b, 'c, 'd>, node_b: &'a Node<'b, 'c, 'd>) -> Self { + Self { + node_a, + node_b, + send_channel_ready: (false, false), + pending_responding_commitment_signed: (false, false), + pending_responding_commitment_signed_dup_monitor: (false, false), + pending_htlc_adds: (0, 0), + pending_htlc_claims: (0, 0), + pending_htlc_fails: (0, 0), + pending_cell_htlc_claims: (0, 0), + pending_cell_htlc_fails: (0, 0), + pending_raa: (false, false), + } + } +} + /// pending_htlc_adds includes both the holding cell and in-flight update_add_htlcs, whereas /// for claims/fails they are separated out. -pub fn reconnect_nodes<'a, 'b, 'c>(node_a: &Node<'a, 'b, 'c>, node_b: &Node<'a, 'b, 'c>, send_channel_ready: (bool, bool), pending_htlc_adds: (i64, i64), pending_htlc_claims: (usize, usize), pending_htlc_fails: (usize, usize), pending_cell_htlc_claims: (usize, usize), pending_cell_htlc_fails: (usize, usize), pending_raa: (bool, bool)) { +pub fn reconnect_nodes<'a, 'b, 'c, 'd>(args: ReconnectArgs<'a, 'b, 'c, 'd>) { + let ReconnectArgs { + node_a, node_b, send_channel_ready, pending_htlc_adds, pending_htlc_claims, pending_htlc_fails, + pending_cell_htlc_claims, pending_cell_htlc_fails, pending_raa, + pending_responding_commitment_signed, pending_responding_commitment_signed_dup_monitor, + } = args; node_a.node.peer_connected(&node_b.node.get_our_node_id(), &msgs::Init { features: node_b.node.init_features(), networks: None, remote_network_address: None }, true).unwrap(); @@ -2961,24 +3421,28 @@ pub fn reconnect_nodes<'a, 'b, 'c>(node_a: &Node<'a, 'b, 'c>, node_b: &Node<'a, // If a expects a channel_ready, it better not think it has received a revoke_and_ack // from b for reestablish in reestablish_1.iter() { - assert_eq!(reestablish.next_remote_commitment_number, 0); + let n = reestablish.next_remote_commitment_number; + assert_eq!(n, 0, "expected a->b next_remote_commitment_number to be 0, got {}", n); } } if send_channel_ready.1 { // If b expects a channel_ready, it better not think it has received a revoke_and_ack // from a for reestablish in reestablish_2.iter() { - assert_eq!(reestablish.next_remote_commitment_number, 0); + let n = reestablish.next_remote_commitment_number; + assert_eq!(n, 0, "expected b->a next_remote_commitment_number to be 0, got {}", n); } } if send_channel_ready.0 || send_channel_ready.1 { // If we expect any channel_ready's, both sides better have set // next_holder_commitment_number to 1 for reestablish in reestablish_1.iter() { - assert_eq!(reestablish.next_local_commitment_number, 1); + let n = reestablish.next_local_commitment_number; + assert_eq!(n, 1, "expected a->b next_local_commitment_number to be 1, got {}", n); } for reestablish in reestablish_2.iter() { - assert_eq!(reestablish.next_local_commitment_number, 1); + let n = reestablish.next_local_commitment_number; + assert_eq!(n, 1, "expected b->a next_local_commitment_number to be 1, got {}", n); } } @@ -3031,13 +3495,12 @@ pub fn reconnect_nodes<'a, 'b, 'c>(node_a: &Node<'a, 'b, 'c>, node_b: &Node<'a, } else { assert!(chan_msgs.1.is_none()); } - if pending_htlc_adds.0 != 0 || pending_htlc_claims.0 != 0 || pending_htlc_fails.0 != 0 || pending_cell_htlc_claims.0 != 0 || pending_cell_htlc_fails.0 != 0 { + if pending_htlc_adds.0 != 0 || pending_htlc_claims.0 != 0 || pending_htlc_fails.0 != 0 || + pending_cell_htlc_claims.0 != 0 || pending_cell_htlc_fails.0 != 0 || + pending_responding_commitment_signed.0 + { let commitment_update = chan_msgs.2.unwrap(); - if pending_htlc_adds.0 != -1 { // We use -1 to denote a response commitment_signed - assert_eq!(commitment_update.update_add_htlcs.len(), pending_htlc_adds.0 as usize); - } else { - assert!(commitment_update.update_add_htlcs.is_empty()); - } + assert_eq!(commitment_update.update_add_htlcs.len(), pending_htlc_adds.0); assert_eq!(commitment_update.update_fulfill_htlcs.len(), pending_htlc_claims.0 + pending_cell_htlc_claims.0); assert_eq!(commitment_update.update_fail_htlcs.len(), pending_htlc_fails.0 + pending_cell_htlc_fails.0); assert!(commitment_update.update_fail_malformed_htlcs.is_empty()); @@ -3051,7 +3514,7 @@ pub fn reconnect_nodes<'a, 'b, 'c>(node_a: &Node<'a, 'b, 'c>, node_b: &Node<'a, node_a.node.handle_update_fail_htlc(&node_b.node.get_our_node_id(), &update_fail); } - if pending_htlc_adds.0 != -1 { // We use -1 to denote a response commitment_signed + if !pending_responding_commitment_signed.0 { commitment_signed_dance!(node_a, node_b, commitment_update.commitment_signed, false); } else { node_a.node.handle_commitment_signed(&node_b.node.get_our_node_id(), &commitment_update.commitment_signed); @@ -3060,7 +3523,7 @@ pub fn reconnect_nodes<'a, 'b, 'c>(node_a: &Node<'a, 'b, 'c>, node_b: &Node<'a, // No commitment_signed so get_event_msg's assert(len == 1) passes node_b.node.handle_revoke_and_ack(&node_a.node.get_our_node_id(), &as_revoke_and_ack); assert!(node_b.node.get_and_clear_pending_msg_events().is_empty()); - check_added_monitors!(node_b, 1); + check_added_monitors!(node_b, if pending_responding_commitment_signed_dup_monitor.0 { 0 } else { 1 }); } } else { assert!(chan_msgs.2.is_none()); @@ -3090,11 +3553,12 @@ pub fn reconnect_nodes<'a, 'b, 'c>(node_a: &Node<'a, 'b, 'c>, node_b: &Node<'a, } else { assert!(chan_msgs.1.is_none()); } - if pending_htlc_adds.1 != 0 || pending_htlc_claims.1 != 0 || pending_htlc_fails.1 != 0 || pending_cell_htlc_claims.1 != 0 || pending_cell_htlc_fails.1 != 0 { + if pending_htlc_adds.1 != 0 || pending_htlc_claims.1 != 0 || pending_htlc_fails.1 != 0 || + pending_cell_htlc_claims.1 != 0 || pending_cell_htlc_fails.1 != 0 || + pending_responding_commitment_signed.1 + { let commitment_update = chan_msgs.2.unwrap(); - if pending_htlc_adds.1 != -1 { // We use -1 to denote a response commitment_signed - assert_eq!(commitment_update.update_add_htlcs.len(), pending_htlc_adds.1 as usize); - } + assert_eq!(commitment_update.update_add_htlcs.len(), pending_htlc_adds.1); assert_eq!(commitment_update.update_fulfill_htlcs.len(), pending_htlc_claims.1 + pending_cell_htlc_claims.1); assert_eq!(commitment_update.update_fail_htlcs.len(), pending_htlc_fails.1 + pending_cell_htlc_fails.1); assert!(commitment_update.update_fail_malformed_htlcs.is_empty()); @@ -3108,7 +3572,7 @@ pub fn reconnect_nodes<'a, 'b, 'c>(node_a: &Node<'a, 'b, 'c>, node_b: &Node<'a, node_b.node.handle_update_fail_htlc(&node_a.node.get_our_node_id(), &update_fail); } - if pending_htlc_adds.1 != -1 { // We use -1 to denote a response commitment_signed + if !pending_responding_commitment_signed.1 { commitment_signed_dance!(node_b, node_a, commitment_update.commitment_signed, false); } else { node_b.node.handle_commitment_signed(&node_a.node.get_our_node_id(), &commitment_update.commitment_signed); @@ -3117,10 +3581,84 @@ pub fn reconnect_nodes<'a, 'b, 'c>(node_a: &Node<'a, 'b, 'c>, node_b: &Node<'a, // No commitment_signed so get_event_msg's assert(len == 1) passes node_a.node.handle_revoke_and_ack(&node_b.node.get_our_node_id(), &bs_revoke_and_ack); assert!(node_a.node.get_and_clear_pending_msg_events().is_empty()); - check_added_monitors!(node_a, 1); + check_added_monitors!(node_a, if pending_responding_commitment_signed_dup_monitor.1 { 0 } else { 1 }); } } else { assert!(chan_msgs.2.is_none()); } } } + +/// Initiates channel opening and creates a single batch funding transaction. +/// This will go through the open_channel / accept_channel flow, and return the batch funding +/// transaction with corresponding funding_created messages. +pub fn create_batch_channel_funding<'a, 'b, 'c>( + funding_node: &Node<'a, 'b, 'c>, + params: &[(&Node<'a, 'b, 'c>, u64, u64, u128, Option)], +) -> (Transaction, Vec) { + let mut tx_outs = Vec::new(); + let mut temp_chan_ids = Vec::new(); + let mut funding_created_msgs = Vec::new(); + + for (other_node, channel_value_satoshis, push_msat, user_channel_id, override_config) in params { + // Initialize channel opening. + let temp_chan_id = funding_node.node.create_channel( + other_node.node.get_our_node_id(), *channel_value_satoshis, *push_msat, *user_channel_id, + None, + *override_config, + ).unwrap(); + let open_channel_msg = get_event_msg!(funding_node, MessageSendEvent::SendOpenChannel, other_node.node.get_our_node_id()); + other_node.node.handle_open_channel(&funding_node.node.get_our_node_id(), &open_channel_msg); + let accept_channel_msg = get_event_msg!(other_node, MessageSendEvent::SendAcceptChannel, funding_node.node.get_our_node_id()); + funding_node.node.handle_accept_channel(&other_node.node.get_our_node_id(), &accept_channel_msg); + + // Create the corresponding funding output. + let events = funding_node.node.get_and_clear_pending_events(); + assert_eq!(events.len(), 1); + match events[0] { + Event::FundingGenerationReady { + ref temporary_channel_id, + ref counterparty_node_id, + channel_value_satoshis: ref event_channel_value_satoshis, + ref output_script, + user_channel_id: ref event_user_channel_id + } => { + assert_eq!(temporary_channel_id, &temp_chan_id); + assert_eq!(counterparty_node_id, &other_node.node.get_our_node_id()); + assert_eq!(channel_value_satoshis, event_channel_value_satoshis); + assert_eq!(user_channel_id, event_user_channel_id); + tx_outs.push(TxOut { + value: *channel_value_satoshis, script_pubkey: output_script.clone(), + }); + }, + _ => panic!("Unexpected event"), + }; + temp_chan_ids.push((temp_chan_id, other_node.node.get_our_node_id())); + } + + // Compose the batch funding transaction and give it to the ChannelManager. + let tx = Transaction { + version: 2, + lock_time: LockTime::ZERO, + input: Vec::new(), + output: tx_outs, + }; + assert!(funding_node.node.batch_funding_transaction_generated( + temp_chan_ids.iter().map(|(a, b)| (a, b)).collect::>().as_slice(), + tx.clone(), + ).is_ok()); + check_added_monitors!(funding_node, 0); + let events = funding_node.node.get_and_clear_pending_msg_events(); + assert_eq!(events.len(), params.len()); + for (other_node, ..) in params { + let funding_created = events + .iter() + .find_map(|event| match event { + MessageSendEvent::SendFundingCreated { node_id, msg } if node_id == &other_node.node.get_our_node_id() => Some(msg.clone()), + _ => None, + }) + .unwrap(); + funding_created_msgs.push(funding_created); + } + return (tx, funding_created_msgs); +}