//! A bunch of useful utilities for building networks of nodes and exchanging messages between
//! nodes for functional tests.
-use chain::Watch;
+use chain::{Listen, Watch};
use chain::channelmonitor::ChannelMonitor;
use chain::transaction::OutPoint;
-use ln::channelmanager::{ChainParameters, ChannelManager, ChannelManagerReadArgs, RAACommitmentOrder, PaymentPreimage, PaymentHash, PaymentSecret, PaymentSendFailure};
+use ln::channelmanager::{BestBlock, ChainParameters, ChannelManager, ChannelManagerReadArgs, RAACommitmentOrder, PaymentPreimage, PaymentHash, PaymentSecret, PaymentSendFailure};
use routing::router::{Route, get_route};
use routing::network_graph::{NetGraphMsgHandler, NetworkGraph};
use ln::features::InitFeatures;
use std::mem;
use std::collections::HashMap;
-pub const CHAN_CONFIRM_DEPTH: u32 = 100;
+pub const CHAN_CONFIRM_DEPTH: u32 = 10;
+/// Mine the given transaction in the next block and then mine CHAN_CONFIRM_DEPTH - 1 blocks on
+/// top, giving the given transaction CHAN_CONFIRM_DEPTH confirmations.
pub fn confirm_transaction<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, tx: &Transaction) {
- let dummy_tx = Transaction { version: 0, lock_time: 0, input: Vec::new(), output: Vec::new() };
- let dummy_tx_count = tx.version as usize;
+ confirm_transaction_at(node, tx, node.best_block_info().1 + 1);
+ connect_blocks(node, CHAN_CONFIRM_DEPTH - 1);
+}
+/// Mine a signle block containing the given transaction
+pub fn mine_transaction<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, tx: &Transaction) {
+ let height = node.best_block_info().1 + 1;
+ confirm_transaction_at(node, tx, height);
+}
+/// Mine the given transaction at the given height, mining blocks as required to build to that
+/// height
+pub fn confirm_transaction_at<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, tx: &Transaction, conf_height: u32) {
+ let first_connect_height = node.best_block_info().1 + 1;
+ assert!(first_connect_height <= conf_height);
+ if conf_height > first_connect_height {
+ connect_blocks(node, conf_height - first_connect_height);
+ }
let mut block = Block {
- header: BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 },
- txdata: vec![dummy_tx; dummy_tx_count],
+ header: BlockHeader { version: 0x20000000, prev_blockhash: node.best_block_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 },
+ txdata: Vec::new(),
};
- block.txdata.push(tx.clone());
- connect_block(node, &block, 1);
- for i in 2..CHAN_CONFIRM_DEPTH {
- block = Block {
- header: BlockHeader { version: 0x20000000, prev_blockhash: block.header.block_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 },
- txdata: vec![],
- };
- connect_block(node, &block, i);
+ 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: 0, input: Vec::new(), output: Vec::new() });
}
-}
+ block.txdata.push(tx.clone());
+ connect_block(node, &block);
+}
+
+/// The possible ways we may notify a ChannelManager of a new block
+#[derive(Clone, Copy, PartialEq)]
+pub enum ConnectStyle {
+ /// Calls update_best_block first, detecting transactions in the block only after receiving the
+ /// header and height information.
+ BestBlockFirst,
+ /// The same as BestBlockFirst, however when we have multiple blocks to connect, we only
+ /// make a single update_best_block call.
+ BestBlockFirstSkippingBlocks,
+ /// Calls transactions_confirmed first, detecting transactions in the block before updating the
+ /// header and height information.
+ TransactionsFirst,
+ /// The same as TransactionsFirst, however when we have multiple blocks to connect, we only
+ /// make a single update_best_block call.
+ TransactionsFirstSkippingBlocks,
+ /// Provides the full block via the chain::Listen interface. In the current code this is
+ /// equivalent to TransactionsFirst with some additional assertions.
+ FullBlockViaListen,
+}
+
+pub fn connect_blocks<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, depth: u32) -> BlockHash {
+ let skip_intermediaries = match *node.connect_style.borrow() {
+ ConnectStyle::BestBlockFirstSkippingBlocks|ConnectStyle::TransactionsFirstSkippingBlocks => true,
+ _ => false,
+ };
-pub fn connect_blocks<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, depth: u32, height: u32, parent: bool, prev_blockhash: BlockHash) -> BlockHash {
let mut block = Block {
- header: BlockHeader { version: 0x2000000, prev_blockhash: if parent { prev_blockhash } else { Default::default() }, merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 },
+ header: BlockHeader { version: 0x2000000, prev_blockhash: node.best_block_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 },
txdata: vec![],
};
- connect_block(node, &block, height + 1);
- for i in 2..depth + 1 {
+ assert!(depth >= 1);
+ for _ in 0..depth - 1 {
+ do_connect_block(node, &block, skip_intermediaries);
block = Block {
header: BlockHeader { version: 0x20000000, prev_blockhash: block.header.block_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 },
txdata: vec![],
};
- connect_block(node, &block, height + i);
}
+ connect_block(node, &block);
block.header.block_hash()
}
-pub fn connect_block<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, block: &Block, height: u32) {
- let txdata: Vec<_> = block.txdata.iter().enumerate().collect();
- node.chain_monitor.chain_monitor.block_connected(&block.header, &txdata, height);
- node.node.block_connected(&block.header, &txdata, height);
- node.node.test_process_background_events();
+pub fn connect_block<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, block: &Block) {
+ do_connect_block(node, block, false);
}
-pub fn disconnect_block<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, header: &BlockHeader, height: u32) {
- node.chain_monitor.chain_monitor.block_disconnected(header, height);
- node.node.block_disconnected(header);
+fn do_connect_block<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, block: &Block, skip_intermediaries: bool) {
+ let height = node.best_block_info().1 + 1;
+ if !skip_intermediaries {
+ let txdata: Vec<_> = block.txdata.iter().enumerate().collect();
+ match *node.connect_style.borrow() {
+ ConnectStyle::BestBlockFirst|ConnectStyle::BestBlockFirstSkippingBlocks => {
+ node.chain_monitor.chain_monitor.update_best_block(&block.header, height);
+ node.chain_monitor.chain_monitor.transactions_confirmed(&block.header, &txdata, height);
+ node.node.update_best_block(&block.header, height);
+ node.node.transactions_confirmed(&block.header, height, &txdata);
+ },
+ ConnectStyle::TransactionsFirst|ConnectStyle::TransactionsFirstSkippingBlocks => {
+ node.chain_monitor.chain_monitor.transactions_confirmed(&block.header, &txdata, height);
+ node.chain_monitor.chain_monitor.update_best_block(&block.header, height);
+ node.node.transactions_confirmed(&block.header, height, &txdata);
+ node.node.update_best_block(&block.header, height);
+ },
+ ConnectStyle::FullBlockViaListen => {
+ node.chain_monitor.chain_monitor.block_connected(&block.header, &txdata, height);
+ Listen::block_connected(node.node, &block, height);
+ }
+ }
+ }
node.node.test_process_background_events();
+ node.blocks.borrow_mut().push((block.header, height));
+}
+
+pub fn disconnect_blocks<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, count: u32) {
+ for i in 0..count {
+ let orig_header = node.blocks.borrow_mut().pop().unwrap();
+ assert!(orig_header.1 > 0); // Cannot disconnect genesis
+ let prev_header = node.blocks.borrow().last().unwrap().clone();
+
+ match *node.connect_style.borrow() {
+ ConnectStyle::FullBlockViaListen => {
+ node.chain_monitor.chain_monitor.block_disconnected(&orig_header.0, orig_header.1);
+ Listen::block_disconnected(node.node, &orig_header.0, orig_header.1);
+ },
+ ConnectStyle::BestBlockFirstSkippingBlocks|ConnectStyle::TransactionsFirstSkippingBlocks => {
+ if i == count - 1 {
+ node.chain_monitor.chain_monitor.update_best_block(&prev_header.0, prev_header.1);
+ node.node.update_best_block(&prev_header.0, prev_header.1);
+ }
+ },
+ _ => {
+ node.chain_monitor.chain_monitor.update_best_block(&prev_header.0, prev_header.1);
+ node.node.update_best_block(&prev_header.0, prev_header.1);
+ },
+ }
+ }
+}
+
+pub fn disconnect_all_blocks<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>) {
+ let count = node.blocks.borrow_mut().len() as u32 - 1;
+ disconnect_blocks(node, count);
}
pub struct TestChanMonCfg {
pub network_payment_count: Rc<RefCell<u8>>,
pub network_chan_count: Rc<RefCell<u32>>,
pub logger: &'c test_utils::TestLogger,
+ pub blocks: RefCell<Vec<(BlockHeader, u32)>>,
+ pub connect_style: Rc<RefCell<ConnectStyle>>,
+}
+impl<'a, 'b, 'c> Node<'a, 'b, 'c> {
+ pub fn best_block_hash(&self) -> BlockHash {
+ self.blocks.borrow_mut().last().unwrap().0.block_hash()
+ }
+ pub fn best_block_info(&self) -> (BlockHash, u32) {
+ self.blocks.borrow_mut().last().map(|(a, b)| (a.block_hash(), *b)).unwrap()
+ }
}
impl<'a, 'b, 'c> Drop for Node<'a, 'b, 'c> {
let mut w = test_utils::TestVecWriter(Vec::new());
self.node.write(&mut w).unwrap();
<(BlockHash, ChannelManager<EnforcingSigner, &test_utils::TestChainMonitor, &test_utils::TestBroadcaster, &test_utils::TestKeysInterface, &test_utils::TestFeeEstimator, &test_utils::TestLogger>)>::read(&mut ::std::io::Cursor::new(w.0), ChannelManagerReadArgs {
- default_config: UserConfig::default(),
+ default_config: *self.node.get_current_default_configuration(),
keys_manager: self.keys_manager,
fee_estimator: &test_utils::TestFeeEstimator { sat_per_kw: 253 },
chain_monitor: self.chain_monitor,
}
}
+/// Get a specific event from the pending events queue.
+#[macro_export]
+macro_rules! get_event {
+ ($node: expr, $event_type: path) => {
+ {
+ let mut events = $node.node.get_and_clear_pending_events();
+ assert_eq!(events.len(), 1);
+ let ev = events.pop().unwrap();
+ match ev {
+ $event_type { .. } => {
+ ev
+ },
+ _ => panic!("Unexpected event"),
+ }
+ }
+ }
+}
+
#[cfg(test)]
macro_rules! get_htlc_update_msgs {
($node: expr, $node_id: expr) => {
}
}
-#[cfg(test)]
+/// Returns any local commitment transactions for the channel.
+#[macro_export]
macro_rules! get_local_commitment_txn {
($node: expr, $channel_id: expr) => {
{
let (temporary_channel_id, tx, funding_output) = create_funding_transaction(node_a, channel_value, 42);
- node_a.node.funding_transaction_generated(&temporary_channel_id, funding_output);
+ node_a.node.funding_transaction_generated(&temporary_channel_id, tx.clone()).unwrap();
check_added_monitors!(node_a, 0);
node_b.node.handle_funding_created(&node_a.node.get_our_node_id(), &get_event_msg!(node_a, MessageSendEvent::SendFundingCreated, node_b.node.get_our_node_id()));
}
let events_4 = node_a.node.get_and_clear_pending_events();
- assert_eq!(events_4.len(), 1);
- match events_4[0] {
- Event::FundingBroadcastSafe { ref funding_txo, user_channel_id } => {
- assert_eq!(user_channel_id, 42);
- assert_eq!(*funding_txo, funding_output);
- },
- _ => panic!("Unexpected event"),
- };
+ assert_eq!(events_4.len(), 0);
+
+ assert_eq!(node_a.tx_broadcaster.txn_broadcasted.lock().unwrap().len(), 1);
+ assert_eq!(node_a.tx_broadcaster.txn_broadcasted.lock().unwrap()[0], tx);
+ node_a.tx_broadcaster.txn_broadcasted.lock().unwrap().clear();
tx
}
-pub fn create_chan_between_nodes_with_value_confirm_first<'a, 'b, 'c, 'd>(node_recv: &'a Node<'b, 'c, 'c>, node_conf: &'a Node<'b, 'c, 'd>, tx: &Transaction) {
- confirm_transaction(node_conf, tx);
+pub fn create_chan_between_nodes_with_value_confirm_first<'a, 'b, 'c, 'd>(node_recv: &'a Node<'b, 'c, 'c>, node_conf: &'a Node<'b, 'c, 'd>, tx: &Transaction, conf_height: u32) {
+ confirm_transaction_at(node_conf, tx, conf_height);
+ connect_blocks(node_conf, CHAN_CONFIRM_DEPTH - 1);
node_recv.node.handle_funding_locked(&node_conf.node.get_our_node_id(), &get_event_msg!(node_conf, MessageSendEvent::SendFundingLocked, node_recv.node.get_our_node_id()));
}
}
pub fn create_chan_between_nodes_with_value_confirm<'a, 'b, 'c, 'd>(node_a: &'a Node<'b, 'c, 'd>, node_b: &'a Node<'b, 'c, 'd>, tx: &Transaction) -> ((msgs::FundingLocked, msgs::AnnouncementSignatures), [u8; 32]) {
- create_chan_between_nodes_with_value_confirm_first(node_a, node_b, tx);
- confirm_transaction(node_a, tx);
+ let conf_height = std::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);
+ connect_blocks(node_a, CHAN_CONFIRM_DEPTH - 1);
create_chan_between_nodes_with_value_confirm_second(node_b, node_a)
}
}}
}
-#[cfg(test)]
+#[cfg(any(test, feature = "unstable"))]
macro_rules! expect_payment_received {
($node: expr, $expected_payment_hash: expr, $expected_recv_value: expr) => {
let events = $node.node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
match events[0] {
Event::PaymentFailed { ref payment_hash, rejected_by_dest, ref error_code, ref error_data } => {
- assert_eq!(*payment_hash, $expected_payment_hash);
- assert_eq!(rejected_by_dest, $rejected_by_dest);
- assert!(error_code.is_some());
- assert!(error_data.is_some());
+ assert_eq!(*payment_hash, $expected_payment_hash, "unexpected payment_hash");
+ assert_eq!(rejected_by_dest, $rejected_by_dest, "unexpected rejected_by_dest value");
+ assert!(error_code.is_some(), "expected error_code.is_some() = true");
+ assert!(error_data.is_some(), "expected error_data.is_some() = true");
$(
- assert_eq!(error_code.unwrap(), $expected_error_code);
- assert_eq!(&error_data.as_ref().unwrap()[..], $expected_error_data);
+ assert_eq!(error_code.unwrap(), $expected_error_code, "unexpected error code");
+ assert_eq!(&error_data.as_ref().unwrap()[..], $expected_error_data, "unexpected error data");
)*
},
_ => panic!("Unexpected event"),
claim_payment_along_route(origin_node, expected_route, false, our_payment_preimage, expected_amount);
}
-pub const TEST_FINAL_CLTV: u32 = 32;
+pub const TEST_FINAL_CLTV: u32 = 50;
pub fn route_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) -> (PaymentPreimage, PaymentHash) {
let net_graph_msg_handler = &origin_node.net_graph_msg_handler;
let mut chanmgrs = Vec::new();
for i in 0..node_count {
let mut default_config = UserConfig::default();
+ // Set cltv_expiry_delta slightly lower to keep the final CLTV values inside one byte in our
+ // tests so that our script-length checks don't fail (see ACCEPTED_HTLC_SCRIPT_WEIGHT).
+ default_config.channel_options.cltv_expiry_delta = 6*6;
default_config.channel_options.announced_channel = true;
default_config.peer_channel_config_limits.force_announced_channel_preference = false;
default_config.own_channel_config.our_htlc_minimum_msat = 1000; // sanitization being done by the sender, to exerce receiver logic we need to lift of limit
let network = Network::Testnet;
let params = ChainParameters {
network,
- latest_hash: genesis_block(network).header.block_hash(),
- latest_height: 0,
+ best_block: BestBlock::from_genesis(network),
};
let node = ChannelManager::new(cfgs[i].fee_estimator, &cfgs[i].chain_monitor, cfgs[i].tx_broadcaster, cfgs[i].logger, cfgs[i].keys_manager, if node_config[i].is_some() { node_config[i].clone().unwrap() } else { default_config }, params);
chanmgrs.push(node);
let mut nodes = Vec::new();
let chan_count = Rc::new(RefCell::new(0));
let payment_count = Rc::new(RefCell::new(0));
+ let connect_style = Rc::new(RefCell::new(ConnectStyle::FullBlockViaListen));
for i in 0..node_count {
let net_graph_msg_handler = NetGraphMsgHandler::new(cfgs[i].chain_source.genesis_hash, None, cfgs[i].logger);
keys_manager: &cfgs[i].keys_manager, node: &chan_mgrs[i], net_graph_msg_handler,
node_seed: cfgs[i].node_seed, network_chan_count: chan_count.clone(),
network_payment_count: payment_count.clone(), logger: cfgs[i].logger,
+ blocks: RefCell::new(vec![(genesis_block(Network::Testnet).header, 0)]),
+ connect_style: Rc::clone(&connect_style),
})
}
nodes
}
-pub const ACCEPTED_HTLC_SCRIPT_WEIGHT: usize = 138; //Here we have a diff due to HTLC CLTV expiry being < 2^15 in test
+// Note that the following only works for CLTV values up to 128
+pub const ACCEPTED_HTLC_SCRIPT_WEIGHT: usize = 137; //Here we have a diff due to HTLC CLTV expiry being < 2^15 in test
pub const OFFERED_HTLC_SCRIPT_WEIGHT: usize = 133;
#[derive(PartialEq)]
res
}
-pub fn get_announce_close_broadcast_events<'a, 'b, 'c>(nodes: &Vec<Node<'a, 'b, 'c>>, a: usize, b: usize) {
+pub fn handle_announce_close_broadcast_events<'a, 'b, 'c>(nodes: &Vec<Node<'a, 'b, 'c>>, a: usize, b: usize, needs_err_handle: bool, expected_error: &str) {
let events_1 = nodes[a].node.get_and_clear_pending_msg_events();
- assert_eq!(events_1.len(), 1);
+ assert_eq!(events_1.len(), 2);
let as_update = match events_1[0] {
MessageSendEvent::BroadcastChannelUpdate { ref msg } => {
msg.clone()
},
_ => panic!("Unexpected event"),
};
+ match events_1[1] {
+ MessageSendEvent::HandleError { node_id, action: msgs::ErrorAction::SendErrorMessage { ref msg } } => {
+ assert_eq!(node_id, nodes[b].node.get_our_node_id());
+ assert_eq!(msg.data, expected_error);
+ if needs_err_handle {
+ nodes[b].node.handle_error(&nodes[a].node.get_our_node_id(), msg);
+ }
+ },
+ _ => panic!("Unexpected event"),
+ }
let events_2 = nodes[b].node.get_and_clear_pending_msg_events();
- assert_eq!(events_2.len(), 1);
+ assert_eq!(events_2.len(), if needs_err_handle { 1 } else { 2 });
let bs_update = match events_2[0] {
MessageSendEvent::BroadcastChannelUpdate { ref msg } => {
msg.clone()
},
_ => panic!("Unexpected event"),
};
+ if !needs_err_handle {
+ match events_2[1] {
+ MessageSendEvent::HandleError { node_id, action: msgs::ErrorAction::SendErrorMessage { ref msg } } => {
+ assert_eq!(node_id, nodes[a].node.get_our_node_id());
+ assert_eq!(msg.data, expected_error);
+ },
+ _ => panic!("Unexpected event"),
+ }
+ }
for node in nodes {
node.net_graph_msg_handler.handle_channel_update(&as_update).unwrap();
}
}
+pub fn get_announce_close_broadcast_events<'a, 'b, 'c>(nodes: &Vec<Node<'a, 'b, 'c>>, a: usize, b: usize) {
+ handle_announce_close_broadcast_events(nodes, a, b, false, "Commitment or closing transaction was confirmed on chain.");
+}
+
#[cfg(test)]
macro_rules! get_channel_value_stat {
($node: expr, $channel_id: expr) => {{