X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;ds=sidebyside;f=lightning%2Fsrc%2Fln%2Ffunctional_test_utils.rs;h=cf52d946e34e0832e640b9d175dc1e98fff34cf4;hb=54ca54d1917ce5fea627f9144bcacb9cdca4c095;hp=5246737b5c747c29cb3068ff9d2163a710f547b9;hpb=7b85d0145fea96fdc5804fed6aa6bb3d9237e243;p=rust-lightning diff --git a/lightning/src/ln/functional_test_utils.rs b/lightning/src/ln/functional_test_utils.rs index 5246737b..cf52d946 100644 --- a/lightning/src/ln/functional_test_utils.rs +++ b/lightning/src/ln/functional_test_utils.rs @@ -415,6 +415,7 @@ type TestOnionMessenger<'chan_man, 'node_cfg, 'chan_mon_cfg> = OnionMessenger< DedicatedEntropy, &'node_cfg test_utils::TestKeysInterface, &'chan_mon_cfg test_utils::TestLogger, + &'chan_man TestChannelManager<'node_cfg, 'chan_mon_cfg>, &'node_cfg test_utils::TestMessageRouter<'chan_mon_cfg>, &'chan_man TestChannelManager<'node_cfg, 'chan_mon_cfg>, IgnoringMessageHandler, @@ -1553,11 +1554,29 @@ macro_rules! check_warn_msg { }} } +/// Checks if at least one peer is connected. +fn is_any_peer_connected(node: &Node) -> bool { + let peer_state = node.node.per_peer_state.read().unwrap(); + for (_, peer_mutex) in peer_state.iter() { + let peer = peer_mutex.lock().unwrap(); + if peer.is_connected { return true; } + } + false +} + /// 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, num_channels: usize, with_error_msg: bool) -> Vec { + let mut dummy_connected = false; + if !is_any_peer_connected(node) { + connect_dummy_node(&node); + dummy_connected = true; + } let msg_events = node.node.get_and_clear_pending_msg_events(); assert_eq!(msg_events.len(), if with_error_msg { num_channels * 2 } else { num_channels }); + if dummy_connected { + disconnect_dummy_node(&node); + } msg_events.into_iter().filter_map(|msg_event| { match msg_event { MessageSendEvent::BroadcastChannelUpdate { ref msg } => { @@ -1827,14 +1846,9 @@ macro_rules! expect_htlc_handling_failed_destinations { /// 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); - + assert!(events.iter().find(|event| matches!(event, Event::PendingHTLCsForwardable { .. })).is_some()); if expected_failures.len() > 0 { expect_htlc_handling_failed_destinations!(events, expected_failures) } @@ -2115,7 +2129,15 @@ pub fn check_payment_claimable( assert_eq!(expected_recv_value, *amount_msat); assert_eq!(expected_receiver_node_id, receiver_node_id.unwrap()); match purpose { - PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => { + PaymentPurpose::Bolt11InvoicePayment { payment_preimage, payment_secret, .. } => { + assert_eq!(&expected_payment_preimage, payment_preimage); + assert_eq!(expected_payment_secret, *payment_secret); + }, + PaymentPurpose::Bolt12OfferPayment { payment_preimage, payment_secret, .. } => { + assert_eq!(&expected_payment_preimage, payment_preimage); + assert_eq!(expected_payment_secret, *payment_secret); + }, + PaymentPurpose::Bolt12RefundPayment { payment_preimage, payment_secret, .. } => { assert_eq!(&expected_payment_preimage, payment_preimage); assert_eq!(expected_payment_secret, *payment_secret); }, @@ -2223,31 +2245,60 @@ macro_rules! expect_payment_path_successful { } } +/// Returns the total fee earned by this HTLC forward, in msat. 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 -) { + downstream_force_closed: bool, allow_1_msat_fee_overpay: bool, +) -> Option { 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 + prev_channel_id, next_channel_id, prev_user_channel_id, next_user_channel_id, + total_fee_earned_msat, skimmed_fee_msat, claim_from_onchain_tx, .. } => { - assert_eq!(total_fee_earned_msat, expected_fee); + if allow_1_msat_fee_overpay { + // Aggregating fees for blinded paths may result in a rounding error, causing slight + // overpayment in fees. + let actual_fee = total_fee_earned_msat.unwrap(); + let expected_fee = expected_fee.unwrap(); + assert!(actual_fee == expected_fee || actual_fee == expected_fee + 1); + } else { + 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())); + 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() && + x.user_channel_id == prev_user_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())); + // As documented, `next_user_channel_id` will only be `Some` if we didn't settle via an + // onchain transaction, just as the `total_fee_earned_msat` field. Rather than + // introducing yet another variable, we use the latter's state as a flag to detect + // this and only check if it's `Some`. + if total_fee_earned_msat.is_none() { + 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() + )); + } else { + 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() && + x.user_channel_id == next_user_channel_id.unwrap() + )); + } } assert_eq!(claim_from_onchain_tx, downstream_force_closed); + total_fee_earned_msat }, _ => panic!("Unexpected event"), } @@ -2260,7 +2311,7 @@ macro_rules! expect_payment_forwarded { assert_eq!(events.len(), 1); $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 + $upstream_force_closed, $downstream_force_closed, false ); } } @@ -2483,6 +2534,7 @@ pub struct PassAlongPathArgs<'a, 'b, 'c, 'd> { pub clear_recipient_events: bool, pub expected_preimage: Option, pub is_probe: bool, + pub custom_tlvs: Vec<(u64, Vec)>, } impl<'a, 'b, 'c, 'd> PassAlongPathArgs<'a, 'b, 'c, 'd> { @@ -2493,7 +2545,7 @@ impl<'a, 'b, 'c, 'd> PassAlongPathArgs<'a, 'b, 'c, 'd> { Self { origin_node, expected_path, recv_value, payment_hash, payment_secret: None, event, payment_claimable_expected: true, clear_recipient_events: true, expected_preimage: None, - is_probe: false, + is_probe: false, custom_tlvs: Vec::new(), } } pub fn without_clearing_recipient_events(mut self) -> Self { @@ -2517,13 +2569,17 @@ impl<'a, 'b, 'c, 'd> PassAlongPathArgs<'a, 'b, 'c, 'd> { self.expected_preimage = Some(payment_preimage); self } + pub fn with_custom_tlvs(mut self, custom_tlvs: Vec<(u64, Vec)>) -> Self { + self.custom_tlvs = custom_tlvs; + self + } } pub fn do_pass_along_path<'a, 'b, 'c>(args: PassAlongPathArgs) -> Option { let PassAlongPathArgs { origin_node, expected_path, recv_value, payment_hash: our_payment_hash, payment_secret: our_payment_secret, event: ev, payment_claimable_expected, - clear_recipient_events, expected_preimage, is_probe + clear_recipient_events, expected_preimage, is_probe, custom_tlvs } = args; let mut payment_event = SendEvent::from_event(ev); @@ -2556,8 +2612,19 @@ pub fn do_pass_along_path<'a, 'b, 'c>(args: PassAlongPathArgs) -> Option assert_eq!(our_payment_hash, *payment_hash); assert_eq!(node.node.get_our_node_id(), receiver_node_id.unwrap()); assert!(onion_fields.is_some()); + assert_eq!(onion_fields.as_ref().unwrap().custom_tlvs, custom_tlvs); match &purpose { - PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => { + PaymentPurpose::Bolt11InvoicePayment { payment_preimage, payment_secret, .. } => { + assert_eq!(expected_preimage, *payment_preimage); + assert_eq!(our_payment_secret.unwrap(), *payment_secret); + assert_eq!(Some(*payment_secret), onion_fields.as_ref().unwrap().payment_secret); + }, + PaymentPurpose::Bolt12OfferPayment { payment_preimage, payment_secret, .. } => { + assert_eq!(expected_preimage, *payment_preimage); + assert_eq!(our_payment_secret.unwrap(), *payment_secret); + assert_eq!(Some(*payment_secret), onion_fields.as_ref().unwrap().payment_secret); + }, + PaymentPurpose::Bolt12RefundPayment { payment_preimage, payment_secret, .. } => { assert_eq!(expected_preimage, *payment_preimage); assert_eq!(our_payment_secret.unwrap(), *payment_secret); assert_eq!(Some(*payment_secret), onion_fields.as_ref().unwrap().payment_secret); @@ -2664,6 +2731,14 @@ pub struct ClaimAlongRouteArgs<'a, 'b, 'c, 'd> { pub expected_min_htlc_overpay: Vec, pub skip_last: bool, pub payment_preimage: PaymentPreimage, + // Allow forwarding nodes to have taken 1 msat more fee than expected based on the downstream + // fulfill amount. + // + // Necessary because our test utils calculate the expected fee for an intermediate node based on + // the amount was claimed in their downstream peer's fulfill, but blinded intermediate nodes + // calculate their fee based on the inbound amount from their upstream peer, causing a difference + // in rounding. + pub allow_1_msat_fee_overpay: bool, } impl<'a, 'b, 'c, 'd> ClaimAlongRouteArgs<'a, 'b, 'c, 'd> { @@ -2674,6 +2749,7 @@ impl<'a, 'b, 'c, 'd> ClaimAlongRouteArgs<'a, 'b, 'c, 'd> { 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, + allow_1_msat_fee_overpay: false, } } pub fn skip_last(mut self, skip_last: bool) -> Self { @@ -2688,12 +2764,16 @@ impl<'a, 'b, 'c, 'd> ClaimAlongRouteArgs<'a, 'b, 'c, 'd> { self.expected_min_htlc_overpay = extra_fees; self } + pub fn allow_1_msat_fee_overpay(mut self) -> Self { + self.allow_1_msat_fee_overpay = true; + 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 + payment_preimage: our_payment_preimage, allow_1_msat_fee_overpay, } = args; let claim_event = expected_paths[0].last().unwrap().node.get_and_clear_pending_events(); assert_eq!(claim_event.len(), 1); @@ -2701,14 +2781,12 @@ pub fn pass_claimed_payment_along_route<'a, 'b, 'c, 'd>(args: ClaimAlongRouteArg let mut fwd_amt_msat = 0; match claim_event[0] { Event::PaymentClaimed { - purpose: PaymentPurpose::SpontaneousPayment(preimage), + purpose: PaymentPurpose::SpontaneousPayment(preimage) + | PaymentPurpose::Bolt11InvoicePayment { payment_preimage: Some(preimage), .. } + | PaymentPurpose::Bolt12OfferPayment { payment_preimage: Some(preimage), .. } + | PaymentPurpose::Bolt12RefundPayment { payment_preimage: Some(preimage), .. }, amount_msat, ref htlcs, - .. } - | Event::PaymentClaimed { - purpose: PaymentPurpose::InvoicePayment { payment_preimage: Some(preimage), ..}, - ref htlcs, - amount_msat, .. } => { assert_eq!(preimage, our_payment_preimage); @@ -2718,7 +2796,9 @@ pub fn pass_claimed_payment_along_route<'a, 'b, 'c, 'd>(args: ClaimAlongRouteArg fwd_amt_msat = amount_msat; }, Event::PaymentClaimed { - purpose: PaymentPurpose::InvoicePayment { .. }, + purpose: PaymentPurpose::Bolt11InvoicePayment { .. } + | PaymentPurpose::Bolt12OfferPayment { .. } + | PaymentPurpose::Bolt12RefundPayment { .. }, payment_hash, amount_msat, ref htlcs, @@ -2813,10 +2893,10 @@ pub fn pass_claimed_payment_along_route<'a, 'b, 'c, 'd>(args: ClaimAlongRouteArg } 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; - fwd_amt_msat += fee as u64; + let actual_fee = expect_payment_forwarded(events.pop().unwrap(), *$node, $next_node, $prev_node, + Some(fee as u64), expected_extra_fee, false, false, allow_1_msat_fee_overpay); + expected_total_fee_msat += actual_fee.unwrap(); + fwd_amt_msat += actual_fee.unwrap(); check_added_monitors!($node, 1); let new_next_msgs = if $new_msgs { let events = $node.node.get_and_clear_pending_msg_events(); @@ -3138,8 +3218,8 @@ pub fn create_network<'a, 'b: 'a, 'c: 'b>(node_count: usize, cfgs: &'b Vec(node_count: usize, cfgs: &'b Vec(node: &Node<'a, 'b, 'c>) { + let node_id_dummy = PublicKey::from_slice(&[2; 33]).unwrap(); + + let mut dummy_init_features = InitFeatures::empty(); + dummy_init_features.set_static_remote_key_required(); + + let init_dummy = msgs::Init { + features: dummy_init_features, + networks: None, + remote_network_address: None + }; + + node.node.peer_connected(&node_id_dummy, &init_dummy, true).unwrap(); + node.onion_messenger.peer_connected(&node_id_dummy, &init_dummy, true).unwrap(); +} + +pub fn disconnect_dummy_node<'a, 'b: 'a, 'c: 'b>(node: &Node<'a, 'b, 'c>) { + let node_id_dummy = PublicKey::from_slice(&[2; 33]).unwrap(); + node.node.peer_disconnected(&node_id_dummy); + node.onion_messenger.peer_disconnected(&node_id_dummy); +} + // 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 ACCEPTED_HTLC_SCRIPT_WEIGHT_ANCHORS: usize = 140; // Here we have a diff due to HTLC CLTV expiry being < 2^15 in test @@ -3298,15 +3400,21 @@ pub fn check_preimage_claim<'a, 'b, 'c>(node: &Node<'a, 'b, 'c>, prev_txn: &Vec< } pub fn handle_announce_close_broadcast_events<'a, 'b, 'c>(nodes: &Vec>, a: usize, b: usize, needs_err_handle: bool, expected_error: &str) { + let mut dummy_connected = false; + if !is_any_peer_connected(&nodes[a]) { + connect_dummy_node(&nodes[a]); + dummy_connected = true + } + let events_1 = nodes[a].node.get_and_clear_pending_msg_events(); assert_eq!(events_1.len(), 2); - let as_update = match events_1[0] { + let as_update = match events_1[1] { MessageSendEvent::BroadcastChannelUpdate { ref msg } => { msg.clone() }, _ => panic!("Unexpected event"), }; - match events_1[1] { + match events_1[0] { 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); @@ -3323,17 +3431,24 @@ pub fn handle_announce_close_broadcast_events<'a, 'b, 'c>(nodes: &Vec panic!("Unexpected event"), } - + if dummy_connected { + disconnect_dummy_node(&nodes[a]); + dummy_connected = false; + } + if !is_any_peer_connected(&nodes[b]) { + connect_dummy_node(&nodes[b]); + dummy_connected = true; + } let events_2 = nodes[b].node.get_and_clear_pending_msg_events(); assert_eq!(events_2.len(), if needs_err_handle { 1 } else { 2 }); - let bs_update = match events_2[0] { + let bs_update = match events_2.last().unwrap() { MessageSendEvent::BroadcastChannelUpdate { ref msg } => { msg.clone() }, _ => panic!("Unexpected event"), }; if !needs_err_handle { - match events_2[1] { + match events_2[0] { 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); @@ -3345,7 +3460,9 @@ pub fn handle_announce_close_broadcast_events<'a, 'b, 'c>(nodes: &Vec panic!("Unexpected event"), } } - + if dummy_connected { + disconnect_dummy_node(&nodes[b]); + } for node in nodes { node.gossip_sync.handle_channel_update(&as_update).unwrap(); node.gossip_sync.handle_channel_update(&bs_update).unwrap();