From: Elias Rohrer Date: Tue, 25 Oct 2022 16:48:34 +0000 (+0200) Subject: Expose the channel via which we received a payment X-Git-Tag: v0.0.113~25^2~1 X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=commitdiff_plain;h=0edb0e2f8442e1e1267f19c99c705faa1ce36930;hp=-c;p=rust-lightning Expose the channel via which we received a payment We expose the `channel_id` and `user_channel_id` via which we received a payment in the `PaymentReceived` event. --- 0edb0e2f8442e1e1267f19c99c705faa1ce36930 diff --git a/lightning/src/ln/chanmon_update_fail_tests.rs b/lightning/src/ln/chanmon_update_fail_tests.rs index 2ee55a6a..b1b6b797 100644 --- a/lightning/src/ln/chanmon_update_fail_tests.rs +++ b/lightning/src/ln/chanmon_update_fail_tests.rs @@ -200,10 +200,11 @@ fn do_test_simple_monitor_temporary_update_fail(disconnect: bool) { let events_3 = nodes[1].node.get_and_clear_pending_events(); assert_eq!(events_3.len(), 1); match events_3[0] { - Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id } => { + Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id: _ } => { assert_eq!(payment_hash_1, *payment_hash); assert_eq!(amount_msat, 1_000_000); assert_eq!(receiver_node_id.unwrap(), nodes[1].node.get_our_node_id()); + assert_eq!(via_channel_id, Some(channel_id)); match &purpose { PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => { assert!(payment_preimage.is_none()); @@ -568,10 +569,11 @@ fn do_test_monitor_temporary_update_fail(disconnect_count: usize) { let events_5 = nodes[1].node.get_and_clear_pending_events(); assert_eq!(events_5.len(), 1); match events_5[0] { - Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id } => { + Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id: _ } => { assert_eq!(payment_hash_2, *payment_hash); assert_eq!(amount_msat, 1_000_000); assert_eq!(receiver_node_id.unwrap(), nodes[1].node.get_our_node_id()); + assert_eq!(via_channel_id, Some(channel_id)); match &purpose { PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => { assert!(payment_preimage.is_none()); @@ -684,10 +686,11 @@ fn test_monitor_update_fail_cs() { let events = nodes[1].node.get_and_clear_pending_events(); assert_eq!(events.len(), 1); match events[0] { - Event::PaymentReceived { payment_hash, ref purpose, amount_msat, receiver_node_id } => { + Event::PaymentReceived { payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id: _ } => { assert_eq!(payment_hash, our_payment_hash); assert_eq!(amount_msat, 1_000_000); assert_eq!(receiver_node_id.unwrap(), nodes[1].node.get_our_node_id()); + assert_eq!(via_channel_id, Some(channel_id)); match &purpose { PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => { assert!(payment_preimage.is_none()); @@ -1627,7 +1630,8 @@ fn test_monitor_update_fail_claim() { commitment_signed_dance!(nodes[1], nodes[2], payment_event.commitment_msg, false, true); // Now restore monitor updating on the 0<->1 channel and claim the funds on B. - let (outpoint, latest_update, _) = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&chan_1.2).unwrap().clone(); + let channel_id = chan_1.2; + let (outpoint, latest_update, _) = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone(); nodes[1].chain_monitor.chain_monitor.force_channel_monitor_updated(outpoint, latest_update); check_added_monitors!(nodes[1], 0); @@ -1648,10 +1652,12 @@ fn test_monitor_update_fail_claim() { let events = nodes[0].node.get_and_clear_pending_events(); assert_eq!(events.len(), 2); match events[0] { - Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id } => { + Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id } => { assert_eq!(payment_hash_2, *payment_hash); assert_eq!(1_000_000, amount_msat); assert_eq!(receiver_node_id.unwrap(), nodes[0].node.get_our_node_id()); + assert_eq!(via_channel_id, Some(channel_id)); + assert_eq!(via_user_channel_id, Some(42)); match &purpose { PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => { assert!(payment_preimage.is_none()); @@ -1663,10 +1669,11 @@ fn test_monitor_update_fail_claim() { _ => panic!("Unexpected event"), } match events[1] { - Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id } => { + Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id: _ } => { assert_eq!(payment_hash_3, *payment_hash); assert_eq!(1_000_000, amount_msat); assert_eq!(receiver_node_id.unwrap(), nodes[0].node.get_our_node_id()); + assert_eq!(via_channel_id, Some(channel_id)); match &purpose { PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => { assert!(payment_preimage.is_none()); diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index cb790b14..c6bb961d 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -142,6 +142,7 @@ pub(super) struct PendingAddHTLCInfo { prev_short_channel_id: u64, prev_htlc_id: u64, prev_funding_outpoint: OutPoint, + prev_user_channel_id: u128, } pub(super) enum HTLCForwardInfo { @@ -3025,7 +3026,7 @@ impl ChannelManager)> = Vec::new(); + let mut phantom_receives: Vec<(u64, OutPoint, u128, Vec<(PendingHTLCInfo, u64)>)> = Vec::new(); let mut handle_errors = Vec::new(); { let mut forward_htlcs = HashMap::new(); @@ -3038,7 +3039,7 @@ impl ChannelManager ChannelManager { match self.construct_recv_pending_htlc_info(hop_data, incoming_shared_secret, payment_hash, outgoing_amt_msat, outgoing_cltv_value, Some(phantom_shared_secret)) { - Ok(info) => phantom_receives.push((prev_short_channel_id, prev_funding_outpoint, vec![(info, prev_htlc_id)])), + Ok(info) => phantom_receives.push((prev_short_channel_id, prev_funding_outpoint, prev_user_channel_id, vec![(info, prev_htlc_id)])), Err(ReceiveError { err_code, err_data, msg }) => failed_payment!(msg, err_code, err_data, Some(phantom_shared_secret)) } }, @@ -3147,7 +3148,7 @@ impl ChannelManager ChannelManager ChannelManager ChannelManager { let purpose = events::PaymentPurpose::SpontaneousPayment(preimage); e.insert((purpose.clone(), vec![claimable_htlc])); + let prev_channel_id = prev_funding_outpoint.to_channel_id(); new_events.push(events::Event::PaymentReceived { receiver_node_id: Some(receiver_node_id), payment_hash, amount_msat: outgoing_amt_msat, purpose, + via_channel_id: Some(prev_channel_id), + via_user_channel_id: Some(prev_user_channel_id), }); }, hash_map::Entry::Occupied(_) => { @@ -4389,13 +4396,13 @@ impl ChannelManager, order: RAACommitmentOrder, pending_forwards: Vec<(PendingHTLCInfo, u64)>, funding_broadcastable: Option, channel_ready: Option, announcement_sigs: Option) - -> Option<(u64, OutPoint, Vec<(PendingHTLCInfo, u64)>)> { + -> Option<(u64, OutPoint, u128, Vec<(PendingHTLCInfo, u64)>)> { let mut htlc_forwards = None; let counterparty_node_id = channel.get_counterparty_node_id(); if !pending_forwards.is_empty() { htlc_forwards = Some((channel.get_short_channel_id().unwrap_or(channel.outbound_scid_alias()), - channel.get_funding_txo().unwrap(), pending_forwards)); + channel.get_funding_txo().unwrap(), channel.get_user_id(), pending_forwards)); } if let Some(msg) = channel_ready { @@ -5056,8 +5063,8 @@ impl ChannelManager)]) { - for &mut (prev_short_channel_id, prev_funding_outpoint, ref mut pending_forwards) in per_source_pending_forwards { + fn forward_htlcs(&self, per_source_pending_forwards: &mut [(u64, OutPoint, u128, Vec<(PendingHTLCInfo, u64)>)]) { + for &mut (prev_short_channel_id, prev_funding_outpoint, prev_user_channel_id, ref mut pending_forwards) in per_source_pending_forwards { let mut forward_event = None; if !pending_forwards.is_empty() { let mut forward_htlcs = self.forward_htlcs.lock().unwrap(); @@ -5072,11 +5079,11 @@ impl ChannelManager { entry.get_mut().push(HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo { - prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, forward_info })); + prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, prev_user_channel_id, forward_info })); }, hash_map::Entry::Vacant(entry) => { entry.insert(vec!(HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo { - prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, forward_info }))); + prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, prev_user_channel_id, forward_info }))); } } } @@ -5135,7 +5142,8 @@ impl ChannelManager break Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel".to_owned(), msg.channel_id)) } @@ -5143,13 +5151,13 @@ impl ChannelManager + short_channel_id, channel_outpoint, user_channel_id)) => { for failure in pending_failures.drain(..) { let receiver = HTLCDestination::NextHopChannel { node_id: Some(*counterparty_node_id), channel_id: channel_outpoint.to_channel_id() }; self.fail_htlc_backwards_internal(failure.0, &failure.1, failure.2, receiver); } - self.forward_htlcs(&mut [(short_channel_id, channel_outpoint, pending_forwards)]); + self.forward_htlcs(&mut [(short_channel_id, channel_outpoint, user_channel_id, pending_forwards)]); self.finalize_claims(finalized_claim_htlcs); Ok(()) }, @@ -6127,7 +6135,7 @@ where } } -impl +impl ChannelMessageHandler for ChannelManager where M::Target: chain::Watch<::Signer>, T::Target: BroadcasterInterface, @@ -6799,6 +6807,7 @@ impl_writeable_tlv_based_enum!(HTLCFailReason, impl_writeable_tlv_based!(PendingAddHTLCInfo, { (0, forward_info, required), + (1, prev_user_channel_id, (default_value, 0)), (2, prev_short_channel_id, required), (4, prev_htlc_id, required), (6, prev_funding_outpoint, required), diff --git a/lightning/src/ln/functional_test_utils.rs b/lightning/src/ln/functional_test_utils.rs index cd3041be..9ca5bbab 100644 --- a/lightning/src/ln/functional_test_utils.rs +++ b/lightning/src/ln/functional_test_utils.rs @@ -1479,7 +1479,7 @@ macro_rules! expect_payment_received { let events = $node.node.get_and_clear_pending_events(); assert_eq!(events.len(), 1); match events[0] { - $crate::util::events::Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id } => { + $crate::util::events::Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id: _, via_user_channel_id: _ } => { assert_eq!($expected_payment_hash, *payment_hash); assert_eq!($expected_recv_value, amount_msat); assert_eq!($expected_receiver_node_id, receiver_node_id.unwrap()); @@ -1774,7 +1774,7 @@ pub fn do_pass_along_path<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_p if payment_received_expected { assert_eq!(events_2.len(), 1); match events_2[0] { - Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id } => { + Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id, ref via_channel_id, ref via_user_channel_id } => { assert_eq!(our_payment_hash, *payment_hash); assert_eq!(node.node.get_our_node_id(), receiver_node_id.unwrap()); match &purpose { @@ -1788,6 +1788,8 @@ pub fn do_pass_along_path<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_p }, } assert_eq!(amount_msat, recv_value); + assert!(node.node.list_channels().iter().any(|details| details.channel_id == via_channel_id.unwrap())); + assert!(node.node.list_channels().iter().any(|details| details.user_channel_id == via_user_channel_id.unwrap())); }, _ => panic!("Unexpected event"), } diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index 232e4704..d8dcb013 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -1956,10 +1956,11 @@ fn test_channel_reserve_holding_cell_htlcs() { let events = nodes[2].node.get_and_clear_pending_events(); assert_eq!(events.len(), 2); match events[0] { - Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id } => { + Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id: _ } => { assert_eq!(our_payment_hash_21, *payment_hash); assert_eq!(recv_value_21, amount_msat); assert_eq!(nodes[2].node.get_our_node_id(), receiver_node_id.unwrap()); + assert_eq!(via_channel_id, Some(chan_2.2)); match &purpose { PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => { assert!(payment_preimage.is_none()); @@ -1971,10 +1972,11 @@ fn test_channel_reserve_holding_cell_htlcs() { _ => panic!("Unexpected event"), } match events[1] { - Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id } => { + Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id: _ } => { assert_eq!(our_payment_hash_22, *payment_hash); assert_eq!(recv_value_22, amount_msat); assert_eq!(nodes[2].node.get_our_node_id(), receiver_node_id.unwrap()); + assert_eq!(via_channel_id, Some(chan_2.2)); match &purpose { PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => { assert!(payment_preimage.is_none()); @@ -3625,15 +3627,16 @@ fn do_test_drop_messages_peer_disconnect(messages_delivered: u8, simulate_broken let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs); let mut as_channel_ready = None; - if messages_delivered == 0 { - let (channel_ready, _, _) = create_chan_between_nodes_with_value_a(&nodes[0], &nodes[1], 100000, 10001, channelmanager::provided_init_features(), channelmanager::provided_init_features()); + let channel_id = if messages_delivered == 0 { + let (channel_ready, chan_id, _) = create_chan_between_nodes_with_value_a(&nodes[0], &nodes[1], 100000, 10001, channelmanager::provided_init_features(), channelmanager::provided_init_features()); as_channel_ready = Some(channel_ready); // nodes[1] doesn't receive the channel_ready message (it'll be re-sent on reconnect) // Note that we store it so that if we're running with `simulate_broken_lnd` we can deliver // it before the channel_reestablish message. + chan_id } else { - create_announced_chan_between_nodes(&nodes, 0, 1, channelmanager::provided_init_features(), channelmanager::provided_init_features()); - } + create_announced_chan_between_nodes(&nodes, 0, 1, channelmanager::provided_init_features(), channelmanager::provided_init_features()).2 + }; let (route, payment_hash_1, payment_preimage_1, payment_secret_1) = get_route_and_payment_hash!(nodes[0], nodes[1], 1_000_000); @@ -3736,10 +3739,11 @@ fn do_test_drop_messages_peer_disconnect(messages_delivered: u8, simulate_broken let events_2 = nodes[1].node.get_and_clear_pending_events(); assert_eq!(events_2.len(), 1); match events_2[0] { - Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id } => { + Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id: _ } => { assert_eq!(payment_hash_1, *payment_hash); assert_eq!(amount_msat, 1_000_000); assert_eq!(receiver_node_id.unwrap(), nodes[1].node.get_our_node_id()); + assert_eq!(via_channel_id, Some(channel_id)); match &purpose { PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => { assert!(payment_preimage.is_none()); diff --git a/lightning/src/util/events.rs b/lightning/src/util/events.rs index 3032aec7..61813400 100644 --- a/lightning/src/util/events.rs +++ b/lightning/src/util/events.rs @@ -357,6 +357,10 @@ pub enum Event { /// Information for claiming this received payment, based on whether the purpose of the /// payment is to pay an invoice or to send a spontaneous payment. purpose: PaymentPurpose, + /// The `channel_id` indicating over which channel we received the payment. + via_channel_id: Option<[u8; 32]>, + /// The `user_channel_id` indicating over which channel we received the payment. + via_user_channel_id: Option, }, /// Indicates a payment has been claimed and we've received money! /// @@ -753,7 +757,7 @@ impl Writeable for Event { // We never write out FundingGenerationReady events as, upon disconnection, peers // drop any channels which have not yet exchanged funding_signed. }, - &Event::PaymentReceived { ref payment_hash, ref amount_msat, ref purpose, ref receiver_node_id } => { + &Event::PaymentReceived { ref payment_hash, ref amount_msat, ref purpose, ref receiver_node_id, ref via_channel_id, ref via_user_channel_id } => { 1u8.write(writer)?; let mut payment_secret = None; let payment_preimage; @@ -770,7 +774,9 @@ impl Writeable for Event { (0, payment_hash, required), (1, receiver_node_id, option), (2, payment_secret, option), + (3, via_channel_id, option), (4, amount_msat, required), + (5, via_user_channel_id, option), (6, 0u64, required), // user_payment_id required for compatibility with 0.0.103 and earlier (8, payment_preimage, option), }); @@ -941,11 +947,15 @@ impl MaybeReadable for Event { let mut amount_msat = 0; let mut receiver_node_id = None; let mut _user_payment_id = None::; // For compatibility with 0.0.103 and earlier + let mut via_channel_id = None; + let mut via_user_channel_id = None; read_tlv_fields!(reader, { (0, payment_hash, required), (1, receiver_node_id, option), (2, payment_secret, option), + (3, via_channel_id, option), (4, amount_msat, required), + (5, via_user_channel_id, option), (6, _user_payment_id, option), (8, payment_preimage, option), }); @@ -962,6 +972,8 @@ impl MaybeReadable for Event { payment_hash, amount_msat, purpose, + via_channel_id, + via_user_channel_id, })) }; f() diff --git a/pending_changelog/1856.txt b/pending_changelog/1856.txt new file mode 100644 index 00000000..d45cff6c --- /dev/null +++ b/pending_changelog/1856.txt @@ -0,0 +1,9 @@ +## API Updates +- `PaymentReceived` events now have `via_channel_id` and `via_user_channel_id` + fields exposing the hop over which we received an inbound payment. Also, + `ChannelDetails` now expose the currently observed number of `confirmations` + on the funding transaction. + +## Backwards Compatibilty +- Inbound payments with HTLCs pending on update to 0.0.113 will result + in a `PaymentReceived` event with `user_channel_id` 0.