X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Ffunctional_test_utils.rs;h=f48c9d099e7e3eb3b03d4cf5f92cfca1d37f4b4b;hb=ea15f0f448137311d95f645606a3bff99bcfca0d;hp=6c3c6e76d422cb9967cc694d8a640d50cdd46de2;hpb=35bb0f467645b3c756c0b998bb7ea052bac57581;p=rust-lightning diff --git a/lightning/src/ln/functional_test_utils.rs b/lightning/src/ln/functional_test_utils.rs index 6c3c6e76..f48c9d09 100644 --- a/lightning/src/ln/functional_test_utils.rs +++ b/lightning/src/ln/functional_test_utils.rs @@ -20,6 +20,7 @@ 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::events::ClosureReason; use crate::util::enforcing_trait_impls::EnforcingSigner; use crate::util::scid_utils; use crate::util::test_utils; @@ -62,9 +63,12 @@ pub fn confirm_transaction<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, tx: &Tran scid } /// Mine a single block containing the given transaction -pub fn mine_transaction<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, tx: &Transaction) { +/// +/// Returns the SCID a channel confirmed in the given transaction will have, assuming the funding +/// output is the 1st output in the transaction. +pub fn mine_transaction<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, tx: &Transaction) -> u64 { let height = node.best_block_info().1 + 1; - confirm_transaction_at(node, tx, height); + confirm_transaction_at(node, tx, height) } /// Mine a single block containing the given transactions pub fn mine_transactions<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, txn: &[&Transaction]) { @@ -576,21 +580,26 @@ macro_rules! get_event { } } +/// Gets an UpdateHTLCs MessageSendEvent +pub fn get_htlc_update_msgs(node: &Node, recipient: &PublicKey) -> msgs::CommitmentUpdate { + let events = node.node.get_and_clear_pending_msg_events(); + assert_eq!(events.len(), 1); + match events[0] { + MessageSendEvent::UpdateHTLCs { ref node_id, ref updates } => { + assert_eq!(node_id, recipient); + (*updates).clone() + }, + _ => panic!("Unexpected event"), + } +} + #[macro_export] /// Gets an UpdateHTLCs MessageSendEvent +/// +/// Don't use this, use the identically-named function instead. macro_rules! get_htlc_update_msgs { ($node: expr, $node_id: expr) => { - { - let events = $node.node.get_and_clear_pending_msg_events(); - assert_eq!(events.len(), 1); - match events[0] { - $crate::util::events::MessageSendEvent::UpdateHTLCs { ref node_id, ref updates } => { - assert_eq!(*node_id, $node_id); - (*updates).clone() - }, - _ => panic!("Unexpected event"), - } - } + $crate::ln::functional_test_utils::get_htlc_update_msgs(&$node, &$node_id) } } @@ -691,7 +700,7 @@ macro_rules! get_feerate { 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.get_feerate() + chan.get_feerate_sat_per_1000_weight() } } } @@ -1092,6 +1101,10 @@ pub fn create_unannounced_chan_between_nodes_with_value<'a, 'b, 'c, 'd>(nodes: & nodes[a].node.handle_funding_signed(&nodes[b].node.get_our_node_id(), &cs_funding_signed); check_added_monitors!(nodes[a], 1); + assert_eq!(nodes[a].tx_broadcaster.txn_broadcasted.lock().unwrap().len(), 1); + assert_eq!(nodes[a].tx_broadcaster.txn_broadcasted.lock().unwrap()[0], tx); + nodes[a].tx_broadcaster.txn_broadcasted.lock().unwrap().clear(); + let conf_height = core::cmp::max(nodes[a].best_block_info().1 + 1, nodes[b].best_block_info().1 + 1); confirm_transaction_at(&nodes[a], &tx, conf_height); connect_blocks(&nodes[a], CHAN_CONFIRM_DEPTH - 1); @@ -1144,6 +1157,24 @@ pub fn update_nodes_with_chan_announce<'a, 'b, 'c, 'd>(nodes: &'a Vec Option>(tx: &Transaction, get_output: F) { + for outp in tx.output.iter() { + assert!(outp.value >= outp.script_pubkey.dust_value().to_sat(), "Spending tx output didn't meet dust limit"); + } + let mut total_value_in = 0; + for input in tx.input.iter() { + total_value_in += get_output(&input.previous_output).unwrap().value; + } + let mut total_value_out = 0; + 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) + // 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(); +} + #[macro_export] macro_rules! check_spends { ($tx: expr, $($spends_txn: expr),*) => { @@ -1153,9 +1184,6 @@ macro_rules! check_spends { assert!(outp.value >= outp.script_pubkey.dust_value().to_sat(), "Input tx output didn't meet dust limit"); } )* - for outp in $tx.output.iter() { - assert!(outp.value >= outp.script_pubkey.dust_value().to_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() { @@ -1164,18 +1192,7 @@ macro_rules! check_spends { )* None }; - let mut total_value_in = 0; - for input in $tx.input.iter() { - total_value_in += get_output(&input.previous_output).unwrap().value; - } - let mut total_value_out = 0; - 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) - // 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(); + $crate::ln::functional_test_utils::do_check_spends(&$tx, get_output); } } } @@ -1222,58 +1239,67 @@ macro_rules! check_warn_msg { /// Check that a channel's closing channel update has been broadcasted, and optionally /// check whether an error message event has occurred. +pub fn check_closed_broadcast(node: &Node, with_error_msg: bool) -> Option { + 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 msg_events[1] { + MessageSendEvent::HandleError { action: msgs::ErrorAction::SendErrorMessage { ref msg }, node_id: _ } => { + // TODO: Check node_id + Some(msg.clone()) + }, + _ => panic!("Unexpected event"), + } + } else { None } +} + +/// Check that a channel's closing channel update has been broadcasted, and optionally +/// check whether an error message event has occurred. +/// +/// Don't use this, use the identically-named function instead. #[macro_export] macro_rules! check_closed_broadcast { - ($node: expr, $with_error_msg: expr) => {{ - use $crate::util::events::MessageSendEvent; - use $crate::ln::msgs::ErrorAction; + ($node: expr, $with_error_msg: expr) => { + $crate::ln::functional_test_utils::check_closed_broadcast(&$node, $with_error_msg) + } +} - 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); +/// 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"), } - if $with_error_msg { - match msg_events[1] { - MessageSendEvent::HandleError { action: ErrorAction::SendErrorMessage { ref msg }, node_id: _ } => { - // TODO: Check node_id - Some(msg.clone()) - }, - _ => panic!("Unexpected event"), - } - } else { None } - }} + } + assert_eq!(is_check_discard_funding, issues_discard_funding); } /// Check that a channel's closing channel events has been issued +/// +/// 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, $is_check_discard_funding: expr) => {{ - use $crate::util::events::Event; - - let events = $node.node.get_and_clear_pending_events(); - assert_eq!(events.len(), $events, "{:?}", 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); - }} + ($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); + } } 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) { @@ -1375,22 +1401,11 @@ impl SendEvent { } #[macro_export] +/// Don't use this, use the identically-named function instead. macro_rules! expect_pending_htlcs_forwardable_conditions { - ($node: expr, $expected_failures: expr) => {{ - let expected_failures = $expected_failures; - let events = $node.node.get_and_clear_pending_events(); - match events[0] { - $crate::util::events::Event::PendingHTLCsForwardable { .. } => { }, - _ => panic!("Unexpected event {:?}", events), - }; - - let count = expected_failures.len() + 1; - assert_eq!(events.len(), count); - - if expected_failures.len() > 0 { - expect_htlc_handling_failed_destinations!(events, expected_failures) - } - }} + ($node: expr, $expected_failures: expr) => { + $crate::ln::functional_test_utils::expect_pending_htlcs_forwardable_conditions($node.node.get_and_clear_pending_events(), &$expected_failures); + } } #[macro_export] @@ -1408,27 +1423,49 @@ macro_rules! expect_htlc_handling_failed_destinations { }} } +/// Checks that an [`Event::PendingHTLCsForwardable`] is available in the given events and, if +/// there are any [`Event::HTLCHandlingFailed`] events their [`HTLCDestination`] is included in the +/// `expected_failures` set. +pub fn expect_pending_htlcs_forwardable_conditions(events: Vec, expected_failures: &[HTLCDestination]) { + match events[0] { + Event::PendingHTLCsForwardable { .. } => { }, + _ => panic!("Unexpected event {:?}", events), + }; + + let count = expected_failures.len() + 1; + assert_eq!(events.len(), count); + + if expected_failures.len() > 0 { + expect_htlc_handling_failed_destinations!(events, expected_failures) + } +} + #[macro_export] /// Clears (and ignores) a PendingHTLCsForwardable event +/// +/// Don't use this, call [`expect_pending_htlcs_forwardable_conditions()`] with an empty failure +/// set instead. macro_rules! expect_pending_htlcs_forwardable_ignore { - ($node: expr) => {{ - expect_pending_htlcs_forwardable_conditions!($node, vec![]); - }}; + ($node: expr) => { + $crate::ln::functional_test_utils::expect_pending_htlcs_forwardable_conditions($node.node.get_and_clear_pending_events(), &[]); + } } #[macro_export] /// Clears (and ignores) PendingHTLCsForwardable and HTLCHandlingFailed events +/// +/// Don't use this, call [`expect_pending_htlcs_forwardable_conditions()`] instead. macro_rules! expect_pending_htlcs_forwardable_and_htlc_handling_failed_ignore { - ($node: expr, $expected_failures: expr) => {{ - expect_pending_htlcs_forwardable_conditions!($node, $expected_failures); - }}; + ($node: expr, $expected_failures: expr) => { + $crate::ln::functional_test_utils::expect_pending_htlcs_forwardable_conditions($node.node.get_and_clear_pending_events(), &$expected_failures); + } } #[macro_export] /// Handles a PendingHTLCsForwardable event macro_rules! expect_pending_htlcs_forwardable { ($node: expr) => {{ - expect_pending_htlcs_forwardable_ignore!($node); + $crate::ln::functional_test_utils::expect_pending_htlcs_forwardable_conditions($node.node.get_and_clear_pending_events(), &[]); $node.node.process_pending_htlc_forwards(); // Ensure process_pending_htlc_forwards is idempotent. @@ -1440,7 +1477,7 @@ macro_rules! expect_pending_htlcs_forwardable { /// Handles a PendingHTLCsForwardable and HTLCHandlingFailed event macro_rules! expect_pending_htlcs_forwardable_and_htlc_handling_failed { ($node: expr, $expected_failures: expr) => {{ - expect_pending_htlcs_forwardable_and_htlc_handling_failed_ignore!($node, $expected_failures); + $crate::ln::functional_test_utils::expect_pending_htlcs_forwardable_conditions($node.node.get_and_clear_pending_events(), &$expected_failures); $node.node.process_pending_htlc_forwards(); // Ensure process_pending_htlc_forwards is idempotent. @@ -1838,20 +1875,13 @@ pub fn expect_payment_failed_conditions_event<'a, 'b, 'c, 'd, 'e>( ) { if conditions.expected_mpp_parts_remain { assert_eq!(payment_failed_events.len(), 1); } else { assert_eq!(payment_failed_events.len(), 2); } let expected_payment_id = match &payment_failed_events[0] { - Event::PaymentPathFailed { payment_hash, payment_failed_permanently, path, retry, payment_id, failure, short_channel_id, + Event::PaymentPathFailed { payment_hash, payment_failed_permanently, payment_id, failure, #[cfg(test)] error_code, #[cfg(test)] error_data, .. } => { assert_eq!(*payment_hash, expected_payment_hash, "unexpected payment_hash"); assert_eq!(*payment_failed_permanently, expected_payment_failed_permanently, "unexpected payment_failed_permanently 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().payment_params.payee_pubkey, path.last().unwrap().pubkey, "Retry payee node_id should match last hop in path"); - if let Some(scid) = short_channel_id { - assert!(retry.as_ref().unwrap().payment_params.previously_failed_channels.contains(&scid)); - } - #[cfg(test)] { assert!(error_code.is_some(), "expected error_code.is_some() = true");