X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Ffunctional_test_utils.rs;h=bf3e192b252b3d7fd14fbc542d16c313304279ad;hb=04d0cca87293f5a47812d19920686755892f8446;hp=6824fb043a9031725f65d256e2522fb73a47cbb3;hpb=35573bb3d7bf8c7f7b9d8759afa555aec2735a44;p=rust-lightning diff --git a/lightning/src/ln/functional_test_utils.rs b/lightning/src/ln/functional_test_utils.rs index 6824fb04..bf3e192b 100644 --- a/lightning/src/ln/functional_test_utils.rs +++ b/lightning/src/ln/functional_test_utils.rs @@ -14,9 +14,9 @@ use chain::{BestBlock, Confirm, Listen, Watch}; use chain::channelmonitor::ChannelMonitor; use chain::transaction::OutPoint; use ln::{PaymentPreimage, PaymentHash, PaymentSecret}; -use ln::channelmanager::{ChainParameters, ChannelManager, ChannelManagerReadArgs, RAACommitmentOrder, PaymentSendFailure}; -use routing::router::{Route, get_route}; +use ln::channelmanager::{ChainParameters, ChannelManager, ChannelManagerReadArgs, RAACommitmentOrder, PaymentSendFailure, PaymentId}; use routing::network_graph::{NetGraphMsgHandler, NetworkGraph}; +use routing::router::{Payee, Route, get_route}; use ln::features::{InitFeatures, InvoiceFeatures}; use ln::msgs; use ln::msgs::{ChannelMessageHandler,RoutingMessageHandler}; @@ -189,6 +189,7 @@ pub struct TestChanMonCfg { pub persister: test_utils::TestPersister, pub logger: test_utils::TestLogger, pub keys_manager: test_utils::TestKeysInterface, + pub network_graph: NetworkGraph, } pub struct NodeCfg<'a> { @@ -198,6 +199,7 @@ pub struct NodeCfg<'a> { pub chain_monitor: test_utils::TestChainMonitor<'a>, pub keys_manager: &'a test_utils::TestKeysInterface, pub logger: &'a test_utils::TestLogger, + pub network_graph: &'a NetworkGraph, pub node_seed: [u8; 32], pub features: InitFeatures, } @@ -208,7 +210,8 @@ pub struct Node<'a, 'b: 'a, 'c: 'b> { pub chain_monitor: &'b test_utils::TestChainMonitor<'c>, pub keys_manager: &'b test_utils::TestKeysInterface, pub node: &'a ChannelManager, &'c test_utils::TestBroadcaster, &'b test_utils::TestKeysInterface, &'c test_utils::TestFeeEstimator, &'c test_utils::TestLogger>, - pub net_graph_msg_handler: NetGraphMsgHandler<&'c test_utils::TestChainSource, &'c test_utils::TestLogger>, + pub network_graph: &'c NetworkGraph, + pub net_graph_msg_handler: NetGraphMsgHandler<&'c NetworkGraph, &'c test_utils::TestChainSource, &'c test_utils::TestLogger>, pub node_seed: [u8; 32], pub network_payment_count: Rc>, pub network_chan_count: Rc>, @@ -239,12 +242,11 @@ impl<'a, 'b, 'c> Drop for Node<'a, 'b, 'c> { // Check that if we serialize the Router, we can deserialize it again. { let mut w = test_utils::TestVecWriter(Vec::new()); - let network_graph_ser = &self.net_graph_msg_handler.network_graph; - network_graph_ser.write(&mut w).unwrap(); + self.network_graph.write(&mut w).unwrap(); let network_graph_deser = ::read(&mut io::Cursor::new(&w.0)).unwrap(); - assert!(network_graph_deser == self.net_graph_msg_handler.network_graph); + assert!(network_graph_deser == *self.network_graph); let net_graph_msg_handler = NetGraphMsgHandler::new( - network_graph_deser, Some(self.chain_source), self.logger + &network_graph_deser, Some(self.chain_source), self.logger ); let mut chan_progress = 0; loop { @@ -274,10 +276,9 @@ 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(); { - let old_monitors = self.chain_monitor.chain_monitor.monitors.read().unwrap(); - for (_, old_monitor) in old_monitors.iter() { + for outpoint in self.chain_monitor.chain_monitor.list_monitors() { let mut w = test_utils::TestVecWriter(Vec::new()); - old_monitor.write(&mut w).unwrap(); + self.chain_monitor.chain_monitor.get_monitor(outpoint).unwrap().write(&mut w).unwrap(); let (_, deserialized_monitor) = <(BlockHash, ChannelMonitor)>::read( &mut io::Cursor::new(&w.0), self.keys_manager).unwrap(); deserialized_monitors.push(deserialized_monitor); @@ -437,20 +438,35 @@ macro_rules! get_feerate { } } -/// Returns any local commitment transactions for the channel. +/// Returns a channel monitor given a channel id, making some naive assumptions #[macro_export] -macro_rules! get_local_commitment_txn { +macro_rules! get_monitor { ($node: expr, $channel_id: expr) => { { - let monitors = $node.chain_monitor.chain_monitor.monitors.read().unwrap(); - let mut commitment_txn = None; - for (funding_txo, monitor) in monitors.iter() { - if funding_txo.to_channel_id() == $channel_id { - commitment_txn = Some(monitor.unsafe_get_latest_holder_commitment_txn(&$node.logger)); + use bitcoin::hashes::Hash; + let mut monitor = None; + // Assume funding vout is either 0 or 1 blindly + 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 + }) + { + monitor = Some(mon); break; } } - commitment_txn.unwrap() + monitor.unwrap() + } + } +} + +/// Returns any local commitment transactions for the channel. +#[macro_export] +macro_rules! get_local_commitment_txn { + ($node: expr, $channel_id: expr) => { + { + $crate::get_monitor!($node, $channel_id).unsafe_get_latest_holder_commitment_txn(&$node.logger) } } } @@ -467,9 +483,9 @@ macro_rules! unwrap_send_err { _ => panic!(), } }, - &Err(PaymentSendFailure::PartialFailure(ref fails)) if !$all_failed => { - assert_eq!(fails.len(), 1); - match fails[0] { + &Err(PaymentSendFailure::PartialFailure { ref results, .. }) if !$all_failed => { + assert_eq!(results.len(), 1); + match results[0] { Err($type) => { $check }, _ => panic!(), } @@ -512,11 +528,12 @@ pub fn create_funding_transaction<'a, 'b, 'c>(node: &Node<'a, 'b, 'c>, expected_ } 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, a_flags: InitFeatures, b_flags: InitFeatures) -> Transaction { - node_a.node.create_channel(node_b.node.get_our_node_id(), channel_value, push_msat, 42, None).unwrap(); + let create_chan_id = node_a.node.create_channel(node_b.node.get_our_node_id(), channel_value, push_msat, 42, None).unwrap(); node_b.node.handle_open_channel(&node_a.node.get_our_node_id(), a_flags, &get_event_msg!(node_a, MessageSendEvent::SendOpenChannel, node_b.node.get_our_node_id())); node_a.node.handle_accept_channel(&node_b.node.get_our_node_id(), b_flags, &get_event_msg!(node_b, MessageSendEvent::SendAcceptChannel, node_a.node.get_our_node_id())); let (temporary_channel_id, tx, funding_output) = create_funding_transaction(node_a, channel_value, 42); + assert_eq!(temporary_channel_id, create_chan_id); node_a.node.funding_transaction_generated(&temporary_channel_id, tx.clone()).unwrap(); check_added_monitors!(node_a, 0); @@ -690,6 +707,14 @@ pub fn update_nodes_with_chan_announce<'a, 'b, 'c, 'd>(nodes: &'a Vec { { + $( + for outp in $spends_txn.output.iter() { + assert!(outp.value >= outp.script_pubkey.dust_value().as_sat(), "Input tx output didn't meet dust limit"); + } + )* + for outp in $tx.output.iter() { + assert!(outp.value >= outp.script_pubkey.dust_value().as_sat(), "Spending tx output didn't meet dust limit"); + } let get_output = |out_point: &bitcoin::blockdata::transaction::OutPoint| { $( if out_point.txid == $spends_txn.txid() { @@ -743,16 +768,16 @@ macro_rules! get_closing_signed_broadcast { #[macro_export] macro_rules! check_closed_broadcast { ($node: expr, $with_error_msg: expr) => {{ - let events = $node.node.get_and_clear_pending_msg_events(); - assert_eq!(events.len(), if $with_error_msg { 2 } else { 1 }); - match events[0] { + let msg_events = $node.node.get_and_clear_pending_msg_events(); + assert_eq!(msg_events.len(), if $with_error_msg { 2 } else { 1 }); + match msg_events[0] { MessageSendEvent::BroadcastChannelUpdate { ref msg } => { assert_eq!(msg.contents.flags & 2, 2); }, _ => panic!("Unexpected event"), } if $with_error_msg { - match events[1] { + match msg_events[1] { MessageSendEvent::HandleError { action: ErrorAction::SendErrorMessage { ref msg }, node_id: _ } => { // TODO: Check node_id Some(msg.clone()) @@ -763,6 +788,32 @@ macro_rules! check_closed_broadcast { }} } +/// Check that a channel's closing channel events has been issued +#[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, $is_check_discard_funding: expr) => {{ + let events = $node.node.get_and_clear_pending_events(); + assert_eq!(events.len(), $events); + let expected_reason = $reason; + 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"), + } + } + assert_eq!($is_check_discard_funding, issues_discard_funding); + }} +} + 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) { 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) }; @@ -947,10 +998,16 @@ macro_rules! commitment_signed_dance { macro_rules! get_payment_preimage_hash { ($dest_node: expr) => { { - let payment_preimage = PaymentPreimage([*$dest_node.network_payment_count.borrow(); 32]); - *$dest_node.network_payment_count.borrow_mut() += 1; + get_payment_preimage_hash!($dest_node, None) + } + }; + ($dest_node: expr, $min_value_msat: expr) => { + { + let mut payment_count = $dest_node.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_secret = $dest_node.node.create_inbound_payment_for_hash(payment_hash, None, 7200, 0).unwrap(); + let payment_secret = $dest_node.node.create_inbound_payment_for_hash(payment_hash, $min_value_msat, 7200).unwrap(); (payment_preimage, payment_hash, payment_secret) } } @@ -959,11 +1016,19 @@ macro_rules! get_payment_preimage_hash { #[cfg(test)] macro_rules! get_route_and_payment_hash { ($send_node: expr, $recv_node: expr, $recv_value: expr) => {{ - let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash!($recv_node); - let net_graph_msg_handler = &$send_node.net_graph_msg_handler; - let route = get_route(&$send_node.node.get_our_node_id(), - &net_graph_msg_handler.network_graph, - &$recv_node.node.get_our_node_id(), None, None, &Vec::new(), $recv_value, TEST_FINAL_CLTV, $send_node.logger).unwrap(); + get_route_and_payment_hash!($send_node, $recv_node, vec![], $recv_value, TEST_FINAL_CLTV) + }}; + ($send_node: expr, $recv_node: expr, $last_hops: expr, $recv_value: expr, $cltv: expr) => {{ + let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash!($recv_node, Some($recv_value)); + let payee = $crate::routing::router::Payee::from_node_id($recv_node.node.get_our_node_id()) + .with_features($crate::ln::features::InvoiceFeatures::known()) + .with_route_hints($last_hops); + let scorer = ::util::test_utils::TestScorer::with_fixed_penalty(0); + let route = ::routing::router::get_route( + &$send_node.node.get_our_node_id(), &payee, $send_node.network_graph, + Some(&$send_node.node.list_usable_channels().iter().collect::>()), + $recv_value, $cltv, $send_node.logger, &scorer + ).unwrap(); (route, payment_hash, payment_preimage, payment_secret) }} } @@ -986,6 +1051,20 @@ macro_rules! expect_pending_htlcs_forwardable { }} } +#[cfg(test)] +macro_rules! expect_pending_htlcs_forwardable_from_events { + ($node: expr, $events: expr, $ignore: expr) => {{ + assert_eq!($events.len(), 1); + match $events[0] { + Event::PendingHTLCsForwardable { .. } => { }, + _ => panic!("Unexpected event"), + }; + if $ignore { + $node.node.process_pending_htlc_forwards(); + } + }} +} + #[cfg(any(test, feature = "unstable"))] macro_rules! expect_payment_received { ($node: expr, $expected_payment_hash: expr, $expected_payment_secret: expr, $expected_recv_value: expr) => { @@ -1008,15 +1087,65 @@ macro_rules! expect_payment_received { } } +#[cfg(test)] +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_rules! expect_payment_sent { ($node: expr, $expected_payment_preimage: expr) => { + expect_payment_sent!($node, $expected_payment_preimage, None::, true); + }; + ($node: expr, $expected_payment_preimage: expr, $expected_fee_msat_opt: expr) => { + expect_payment_sent!($node, $expected_payment_preimage, $expected_fee_msat_opt, true); + }; + ($node: expr, $expected_payment_preimage: expr, $expected_fee_msat_opt: expr, $expect_paths: expr) => { let events = $node.node.get_and_clear_pending_events(); - assert_eq!(events.len(), 1); - match events[0] { - Event::PaymentSent { ref payment_preimage } => { + let expected_payment_hash = PaymentHash(Sha256::hash(&$expected_payment_preimage.0).into_inner()); + if $expect_paths { + assert!(events.len() > 1); + } else { + assert_eq!(events.len(), 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); + assert_eq!(expected_payment_hash, *payment_hash); + assert!(fee_paid_msat.is_some()); + if $expected_fee_msat_opt.is_some() { + assert_eq!(*fee_paid_msat, $expected_fee_msat_opt); + } + payment_id.unwrap() }, _ => panic!("Unexpected event"), + }; + if $expect_paths { + for i in 1..events.len() { + match events[i] { + Event::PaymentPathSuccessful { payment_id, payment_hash, .. } => { + assert_eq!(payment_id, expected_payment_id); + assert_eq!(payment_hash, Some(expected_payment_hash)); + }, + _ => panic!("Unexpected event"), + } + } + } + } +} + +#[cfg(test)] +macro_rules! expect_payment_path_successful { + ($node: expr) => { + let events = $node.node.get_and_clear_pending_events(); + assert_eq!(events.len(), 1); + match events[0] { + Event::PaymentPathSuccessful { .. } => {}, + _ => panic!("Unexpected event"), } } } @@ -1041,9 +1170,12 @@ macro_rules! expect_payment_failed_with_update { 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 network_update, ref error_code, ref error_data } => { + Event::PaymentPathFailed { ref payment_hash, rejected_by_dest, ref network_update, ref error_code, ref error_data, ref path, ref retry, .. } => { assert_eq!(*payment_hash, $expected_payment_hash, "unexpected payment_hash"); assert_eq!(rejected_by_dest, $rejected_by_dest, "unexpected rejected_by_dest value"); + assert!(retry.is_some(), "expected retry.is_some()"); + assert_eq!(retry.as_ref().unwrap().final_value_msat, path.last().unwrap().fee_msat, "Retry amount should match last hop in path"); + assert_eq!(retry.as_ref().unwrap().payee.pubkey, path.last().unwrap().pubkey, "Retry payee node_id should match last hop in path"); assert!(error_code.is_some(), "expected error_code.is_some() = true"); assert!(error_data.is_some(), "expected error_data.is_some() = true"); match network_update { @@ -1070,9 +1202,12 @@ macro_rules! expect_payment_failed { 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, network_update: _, ref error_code, ref error_data } => { + Event::PaymentPathFailed { ref payment_hash, rejected_by_dest, network_update: _, ref error_code, ref error_data, ref path, ref retry, .. } => { assert_eq!(*payment_hash, $expected_payment_hash, "unexpected payment_hash"); assert_eq!(rejected_by_dest, $rejected_by_dest, "unexpected rejected_by_dest value"); + assert!(retry.is_some(), "expected retry.is_some()"); + assert_eq!(retry.as_ref().unwrap().final_value_msat, path.last().unwrap().fee_msat, "Retry amount should match last hop in path"); + assert_eq!(retry.as_ref().unwrap().payee.pubkey, path.last().unwrap().pubkey, "Retry payee node_id should match last hop in path"); assert!(error_code.is_some(), "expected error_code.is_some() = true"); assert!(error_data.is_some(), "expected error_data.is_some() = true"); $( @@ -1085,10 +1220,11 @@ macro_rules! expect_payment_failed { } } -pub fn send_along_route_with_secret<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, route: Route, expected_paths: &[&[&Node<'a, 'b, 'c>]], recv_value: u64, our_payment_hash: PaymentHash, our_payment_secret: PaymentSecret) { - origin_node.node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap(); +pub fn send_along_route_with_secret<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, route: Route, expected_paths: &[&[&Node<'a, 'b, 'c>]], recv_value: u64, our_payment_hash: PaymentHash, our_payment_secret: PaymentSecret) -> PaymentId { + let payment_id = origin_node.node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap(); check_added_monitors!(origin_node, expected_paths.len()); pass_along_route(origin_node, expected_paths, recv_value, our_payment_hash, our_payment_secret); + payment_id } 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_received_expected: bool, expected_preimage: Option) { @@ -1151,19 +1287,21 @@ pub fn pass_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_rou } } -pub fn send_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, route: Route, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) -> (PaymentPreimage, PaymentHash, PaymentSecret) { +pub fn send_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, route: Route, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) -> (PaymentPreimage, PaymentHash, PaymentSecret, PaymentId) { let (our_payment_preimage, our_payment_hash, our_payment_secret) = get_payment_preimage_hash!(expected_route.last().unwrap()); - send_along_route_with_secret(origin_node, route, &[expected_route], recv_value, our_payment_hash, our_payment_secret); - (our_payment_preimage, our_payment_hash, our_payment_secret) + let payment_id = send_along_route_with_secret(origin_node, route, &[expected_route], recv_value, our_payment_hash, our_payment_secret); + (our_payment_preimage, our_payment_hash, our_payment_secret, payment_id) } -pub fn 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) { +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 { 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()); } assert!(expected_paths[0].last().unwrap().node.claim_funds(our_payment_preimage)); check_added_monitors!(expected_paths[0].last().unwrap(), expected_paths.len()); + let mut expected_total_fee_msat = 0; + macro_rules! msgs_from_ev { ($ev: expr) => { match $ev { @@ -1206,6 +1344,7 @@ pub fn claim_payment_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, exp $node.node.handle_update_fulfill_htlc(&$prev_node.node.get_our_node_id(), &next_msgs.as_ref().unwrap().0); let fee = $node.node.channel_state.lock().unwrap().by_id.get(&next_msgs.as_ref().unwrap().0.channel_id).unwrap().config.forwarding_fee_base_msat; expect_payment_forwarded!($node, Some(fee as u64), false); + expected_total_fee_msat += fee as u64; check_added_monitors!($node, 1); let new_next_msgs = if $new_msgs { let events = $node.node.get_and_clear_pending_msg_events(); @@ -1242,9 +1381,15 @@ pub fn claim_payment_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, exp if !skip_last { last_update_fulfill_dance!(origin_node, expected_route.first().unwrap()); - expect_payment_sent!(origin_node, our_payment_preimage); } } + expected_total_fee_msat +} +pub fn 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) { + let expected_total_fee_msat = do_claim_payment_along_route(origin_node, expected_paths, skip_last, our_payment_preimage); + if !skip_last { + expect_payment_sent!(origin_node, our_payment_preimage, Some(expected_total_fee_msat)); + } } pub fn claim_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], our_payment_preimage: PaymentPreimage) { @@ -1254,23 +1399,28 @@ 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) { - let net_graph_msg_handler = &origin_node.net_graph_msg_handler; - let route = get_route(&origin_node.node.get_our_node_id(), &net_graph_msg_handler.network_graph, - &expected_route.last().unwrap().node.get_our_node_id(), Some(InvoiceFeatures::known()), - Some(&origin_node.node.list_usable_channels().iter().collect::>()), &[], - recv_value, TEST_FINAL_CLTV, origin_node.logger).unwrap(); + let payee = Payee::from_node_id(expected_route.last().unwrap().node.get_our_node_id()) + .with_features(InvoiceFeatures::known()); + let scorer = test_utils::TestScorer::with_fixed_penalty(0); + let route = get_route( + &origin_node.node.get_our_node_id(), &payee, &origin_node.network_graph, + Some(&origin_node.node.list_usable_channels().iter().collect::>()), + recv_value, TEST_FINAL_CLTV, origin_node.logger, &scorer).unwrap(); assert_eq!(route.paths.len(), 1); assert_eq!(route.paths[0].len(), expected_route.len()); for (node, hop) in expected_route.iter().zip(route.paths[0].iter()) { assert_eq!(hop.pubkey, node.node.get_our_node_id()); } - send_along_route(origin_node, route, expected_route, recv_value) + let res = send_along_route(origin_node, route, expected_route, recv_value); + (res.0, res.1, res.2) } pub fn route_over_limit<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) { - let net_graph_msg_handler = &origin_node.net_graph_msg_handler; - let route = get_route(&origin_node.node.get_our_node_id(), &net_graph_msg_handler.network_graph, &expected_route.last().unwrap().node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), recv_value, TEST_FINAL_CLTV, origin_node.logger).unwrap(); + let payee = Payee::from_node_id(expected_route.last().unwrap().node.get_our_node_id()) + .with_features(InvoiceFeatures::known()); + let scorer = test_utils::TestScorer::with_fixed_penalty(0); + let route = get_route(&origin_node.node.get_our_node_id(), &payee, origin_node.network_graph, None, recv_value, TEST_FINAL_CLTV, origin_node.logger, &scorer).unwrap(); assert_eq!(route.paths.len(), 1); assert_eq!(route.paths[0].len(), expected_route.len()); for (node, hop) in expected_route.iter().zip(route.paths[0].iter()) { @@ -1287,77 +1437,100 @@ pub fn send_payment<'a, 'b, 'c>(origin: &Node<'a, 'b, 'c>, expected_route: &[&No claim_payment(&origin, expected_route, our_payment_preimage); } -pub fn fail_payment_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], skip_last: bool, our_payment_hash: PaymentHash) { - assert!(expected_route.last().unwrap().node.fail_htlc_backwards(&our_payment_hash)); - expect_pending_htlcs_forwardable!(expected_route.last().unwrap()); - check_added_monitors!(expected_route.last().unwrap(), 1); +pub fn fail_payment_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_paths_slice: &[&[&Node<'a, 'b, 'c>]], skip_last: bool, our_payment_hash: PaymentHash) { + let mut expected_paths: Vec<_> = expected_paths_slice.iter().collect(); + 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()); + } + assert!(expected_paths[0].last().unwrap().node.fail_htlc_backwards(&our_payment_hash)); + expect_pending_htlcs_forwardable!(expected_paths[0].last().unwrap()); + check_added_monitors!(expected_paths[0].last().unwrap(), expected_paths.len()); - let mut next_msgs: Option<(msgs::UpdateFailHTLC, msgs::CommitmentSigned)> = None; - macro_rules! update_fail_dance { - ($node: expr, $prev_node: expr, $last_node: expr) => { - { - $node.node.handle_update_fail_htlc(&$prev_node.node.get_our_node_id(), &next_msgs.as_ref().unwrap().0); - commitment_signed_dance!($node, $prev_node, next_msgs.as_ref().unwrap().1, !$last_node); - if skip_last && $last_node { - expect_pending_htlcs_forwardable!($node); + let mut per_path_msgs: Vec<((msgs::UpdateFailHTLC, msgs::CommitmentSigned), PublicKey)> = Vec::with_capacity(expected_paths.len()); + let events = expected_paths[0].last().unwrap().node.get_and_clear_pending_msg_events(); + assert_eq!(events.len(), expected_paths.len()); + for ev in events.iter() { + let (update_fail, commitment_signed, node_id) = match ev { + &MessageSendEvent::UpdateHTLCs { ref node_id, updates: msgs::CommitmentUpdate { ref update_add_htlcs, ref update_fulfill_htlcs, ref update_fail_htlcs, ref update_fail_malformed_htlcs, ref update_fee, ref commitment_signed } } => { + assert!(update_add_htlcs.is_empty()); + assert!(update_fulfill_htlcs.is_empty()); + assert_eq!(update_fail_htlcs.len(), 1); + assert!(update_fail_malformed_htlcs.is_empty()); + assert!(update_fee.is_none()); + (update_fail_htlcs[0].clone(), commitment_signed.clone(), node_id.clone()) + }, + _ => panic!("Unexpected event"), + }; + per_path_msgs.push(((update_fail, commitment_signed), node_id)); + } + per_path_msgs.sort_unstable_by(|(_, node_id_a), (_, node_id_b)| node_id_a.cmp(node_id_b)); + expected_paths.sort_unstable_by(|path_a, path_b| path_a[path_a.len() - 2].node.get_our_node_id().cmp(&path_b[path_b.len() - 2].node.get_our_node_id())); + + for (i, (expected_route, (path_msgs, next_hop))) in expected_paths.iter().zip(per_path_msgs.drain(..)).enumerate() { + let mut next_msgs = Some(path_msgs); + let mut expected_next_node = next_hop; + let mut prev_node = expected_route.last().unwrap(); + + for (idx, node) in expected_route.iter().rev().enumerate().skip(1) { + assert_eq!(expected_next_node, node.node.get_our_node_id()); + let update_next_node = !skip_last || idx != expected_route.len() - 1; + if next_msgs.is_some() { + node.node.handle_update_fail_htlc(&prev_node.node.get_our_node_id(), &next_msgs.as_ref().unwrap().0); + commitment_signed_dance!(node, prev_node, next_msgs.as_ref().unwrap().1, update_next_node); + if !update_next_node { + expect_pending_htlcs_forwardable!(node); } } - } - } + let events = node.node.get_and_clear_pending_msg_events(); + if update_next_node { + assert_eq!(events.len(), 1); + match events[0] { + MessageSendEvent::UpdateHTLCs { ref node_id, updates: msgs::CommitmentUpdate { ref update_add_htlcs, ref update_fulfill_htlcs, ref update_fail_htlcs, ref update_fail_malformed_htlcs, ref update_fee, ref commitment_signed } } => { + assert!(update_add_htlcs.is_empty()); + assert!(update_fulfill_htlcs.is_empty()); + assert_eq!(update_fail_htlcs.len(), 1); + assert!(update_fail_malformed_htlcs.is_empty()); + assert!(update_fee.is_none()); + expected_next_node = node_id.clone(); + next_msgs = Some((update_fail_htlcs[0].clone(), commitment_signed.clone())); + }, + _ => panic!("Unexpected event"), + } + } else { + assert!(events.is_empty()); + } + if !skip_last && idx == expected_route.len() - 1 { + assert_eq!(expected_next_node, origin_node.node.get_our_node_id()); + } - let mut expected_next_node = expected_route.last().unwrap().node.get_our_node_id(); - let mut prev_node = expected_route.last().unwrap(); - for (idx, node) in expected_route.iter().rev().enumerate() { - assert_eq!(expected_next_node, node.node.get_our_node_id()); - if next_msgs.is_some() { - // We may be the "last node" for the purpose of the commitment dance if we're - // skipping the last node (implying it is disconnected) and we're the - // second-to-last node! - update_fail_dance!(node, prev_node, skip_last && idx == expected_route.len() - 1); + prev_node = node; } - let events = node.node.get_and_clear_pending_msg_events(); - if !skip_last || idx != expected_route.len() - 1 { + if !skip_last { + let prev_node = expected_route.first().unwrap(); + origin_node.node.handle_update_fail_htlc(&prev_node.node.get_our_node_id(), &next_msgs.as_ref().unwrap().0); + check_added_monitors!(origin_node, 0); + assert!(origin_node.node.get_and_clear_pending_msg_events().is_empty()); + commitment_signed_dance!(origin_node, prev_node, next_msgs.as_ref().unwrap().1, false); + let events = origin_node.node.get_and_clear_pending_events(); assert_eq!(events.len(), 1); match events[0] { - MessageSendEvent::UpdateHTLCs { ref node_id, updates: msgs::CommitmentUpdate { ref update_add_htlcs, ref update_fulfill_htlcs, ref update_fail_htlcs, ref update_fail_malformed_htlcs, ref update_fee, ref commitment_signed } } => { - assert!(update_add_htlcs.is_empty()); - assert!(update_fulfill_htlcs.is_empty()); - assert_eq!(update_fail_htlcs.len(), 1); - assert!(update_fail_malformed_htlcs.is_empty()); - assert!(update_fee.is_none()); - expected_next_node = node_id.clone(); - next_msgs = Some((update_fail_htlcs[0].clone(), commitment_signed.clone())); + Event::PaymentPathFailed { payment_hash, rejected_by_dest, all_paths_failed, ref path, .. } => { + assert_eq!(payment_hash, our_payment_hash); + assert!(rejected_by_dest); + assert_eq!(all_paths_failed, i == expected_paths.len() - 1); + for (idx, hop) in expected_route.iter().enumerate() { + assert_eq!(hop.node.get_our_node_id(), path[idx].pubkey); + } }, _ => panic!("Unexpected event"), } - } else { - assert!(events.is_empty()); - } - if !skip_last && idx == expected_route.len() - 1 { - assert_eq!(expected_next_node, origin_node.node.get_our_node_id()); - } - - prev_node = node; - } - - if !skip_last { - update_fail_dance!(origin_node, expected_route.first().unwrap(), true); - - let events = origin_node.node.get_and_clear_pending_events(); - assert_eq!(events.len(), 1); - match events[0] { - Event::PaymentFailed { payment_hash, rejected_by_dest, .. } => { - assert_eq!(payment_hash, our_payment_hash); - assert!(rejected_by_dest); - }, - _ => panic!("Unexpected event"), } } } -pub fn fail_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], our_payment_hash: PaymentHash) { - fail_payment_along_route(origin_node, expected_route, false, our_payment_hash); +pub fn fail_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_path: &[&Node<'a, 'b, 'c>], our_payment_hash: PaymentHash) { + fail_payment_along_route(origin_node, &[&expected_path[..]], false, our_payment_hash); } pub fn create_chanmon_cfgs(node_count: usize) -> Vec { @@ -1373,8 +1546,9 @@ 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 network_graph = NetworkGraph::new(chain_source.genesis_hash); - chan_mon_cfgs.push(TestChanMonCfg{ tx_broadcaster, fee_estimator, chain_source, logger, persister, keys_manager }); + chan_mon_cfgs.push(TestChanMonCfg{ tx_broadcaster, fee_estimator, chain_source, logger, persister, keys_manager, network_graph }); } chan_mon_cfgs @@ -1395,6 +1569,7 @@ pub fn create_node_cfgs<'a>(node_count: usize, chanmon_cfgs: &'a Vec(node_count: usize, cfgs: &'b Vec(nodes: &Vec(nodes: &Vec>, a: usize, b: usize) { - handle_announce_close_broadcast_events(nodes, a, b, false, "Commitment or closing transaction was confirmed on chain."); + handle_announce_close_broadcast_events(nodes, a, b, false, "Channel closed because commitment or closing transaction was confirmed on chain."); } #[cfg(test)]