From 4a0170a9417aaa3adac25e1d790031789d0de545 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Mon, 18 Dec 2023 14:15:48 -0500 Subject: [PATCH 1/9] Support forwarding blinded HTLCs as non-intro node. Error handling will be completed in upcoming commit(s). --- lightning/src/ln/msgs.rs | 4 ++-- lightning/src/ln/onion_payment.rs | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index 1794aefe7ef..bc914ce4975 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -1714,7 +1714,7 @@ mod fuzzy_internal_msgs { payment_relay: PaymentRelay, payment_constraints: PaymentConstraints, features: BlindedHopFeatures, - intro_node_blinding_point: PublicKey, + intro_node_blinding_point: Option, }, BlindedReceive { sender_intended_htlc_amt_msat: u64, @@ -2394,7 +2394,7 @@ impl ReadableArgs<(Option, &NS)> for InboundOnionPayload w payment_relay, payment_constraints, features, - intro_node_blinding_point: intro_node_blinding_point.ok_or(DecodeError::InvalidValue)?, + intro_node_blinding_point, }) }, ChaChaPolyReadAdapter { readable: BlindedPaymentTlvs::Receive(ReceiveTlvs { diff --git a/lightning/src/ln/onion_payment.rs b/lightning/src/ln/onion_payment.rs index c552bf13b8e..06dc7719b2e 100644 --- a/lightning/src/ln/onion_payment.rs +++ b/lightning/src/ln/onion_payment.rs @@ -73,7 +73,7 @@ pub(super) fn create_fwd_pending_htlc_info( }; let ( - short_channel_id, amt_to_forward, outgoing_cltv_value, inbound_blinding_point + short_channel_id, amt_to_forward, outgoing_cltv_value, intro_node_blinding_point ) = match hop_data { msgs::InboundOnionPayload::Forward { short_channel_id, amt_to_forward, outgoing_cltv_value } => (short_channel_id, amt_to_forward, outgoing_cltv_value, None), @@ -91,7 +91,7 @@ pub(super) fn create_fwd_pending_htlc_info( err_data: vec![0; 32], } })?; - (short_channel_id, amt_to_forward, outgoing_cltv_value, Some(intro_node_blinding_point)) + (short_channel_id, amt_to_forward, outgoing_cltv_value, intro_node_blinding_point) }, msgs::InboundOnionPayload::Receive { .. } | msgs::InboundOnionPayload::BlindedReceive { .. } => return Err(InboundHTLCErr { @@ -105,7 +105,8 @@ pub(super) fn create_fwd_pending_htlc_info( routing: PendingHTLCRouting::Forward { onion_packet: outgoing_packet, short_channel_id, - blinded: inbound_blinding_point.map(|bp| BlindedForward { inbound_blinding_point: bp }), + blinded: intro_node_blinding_point.or(msg.blinding_point) + .map(|bp| BlindedForward { inbound_blinding_point: bp }), }, payment_hash: msg.payment_hash, incoming_shared_secret: shared_secret, From f1e4645edffe1c65b8ac5224ec2750d3c7fae6d6 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Tue, 19 Dec 2023 19:16:37 -0500 Subject: [PATCH 2/9] Fix final blinded hop CLTV expiry on send. Previously, we were setting the final blinded hop's CLTV expiry height to best_block_height + total_blinded_path_cltv_delta + shadow_cltv_offset. This is incorrect, it should instead be set to best_block_height + shadow_cltv_offset only -- it doesn't make sense to include the delta for the other blinded hops in the final hop's expiry. The reason this too-high final cltv value didn't cause test failures previously is because of a 2nd bug that is fixed in an upcoming commit where the sender adds the shadow offset twice to the total path CLTV expiry. This 2nd offset meant that intermediate nodes had some buffer CLTV to subtract their delta from while still (usually) have enough leftover to meet the expiry in the final hop's onion. --- lightning/src/ln/onion_utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lightning/src/ln/onion_utils.rs b/lightning/src/ln/onion_utils.rs index ac0bb6189c6..2d75edd30d0 100644 --- a/lightning/src/ln/onion_utils.rs +++ b/lightning/src/ln/onion_utils.rs @@ -192,7 +192,7 @@ pub(super) fn build_onion_payloads(path: &Path, total_msat: u64, mut recipient_o res.push(msgs::OutboundOnionPayload::BlindedReceive { sender_intended_htlc_amt_msat: *final_value_msat, total_msat, - cltv_expiry_height: cltv, + cltv_expiry_height: cur_cltv, encrypted_tlvs: blinded_hop.encrypted_payload.clone(), intro_node_blinding_point: blinding_point.take(), }); From 8ad292eea67a56fdc231a2b0bbe689fc7dd8ff2e Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Mon, 18 Dec 2023 14:16:02 -0500 Subject: [PATCH 3/9] Test successful payment to 3-hop blinded path. --- lightning/src/ln/blinded_payment_tests.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/lightning/src/ln/blinded_payment_tests.rs b/lightning/src/ln/blinded_payment_tests.rs index 9b580d1fa75..51a286849dd 100644 --- a/lightning/src/ln/blinded_payment_tests.rs +++ b/lightning/src/ln/blinded_payment_tests.rs @@ -490,6 +490,29 @@ fn two_hop_blinded_path_success() { claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], payment_preimage); } +#[test] +fn three_hop_blinded_path_success() { + let chanmon_cfgs = create_chanmon_cfgs(5); + let node_cfgs = create_node_cfgs(5, &chanmon_cfgs); + let node_chanmgrs = create_node_chanmgrs(5, &node_cfgs, &[None, None, None, None, None]); + let mut nodes = create_network(5, &node_cfgs, &node_chanmgrs); + create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0); + create_announced_chan_between_nodes_with_value(&nodes, 1, 2, 1_000_000, 0); + let chan_upd_2_3 = create_announced_chan_between_nodes_with_value(&nodes, 2, 3, 1_000_000, 0).0.contents; + let chan_upd_3_4 = create_announced_chan_between_nodes_with_value(&nodes, 3, 4, 1_000_000, 0).0.contents; + + let amt_msat = 5000; + let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[4], Some(amt_msat), None); + let route_params = get_blinded_route_parameters(amt_msat, payment_secret, + nodes.iter().skip(2).map(|n| n.node.get_our_node_id()).collect(), + &[&chan_upd_2_3, &chan_upd_3_4], &chanmon_cfgs[4].keys_manager); + + nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap(); + check_added_monitors(&nodes[0], 1); + pass_along_route(&nodes[0], &[&[&nodes[1], &nodes[2], &nodes[3], &nodes[4]]], amt_msat, payment_hash, payment_secret); + claim_payment(&nodes[0], &[&nodes[1], &nodes[2], &nodes[3], &nodes[4]], payment_preimage); +} + #[derive(PartialEq)] enum ReceiveCheckFail { // The recipient fails the payment upon `PaymentClaimable`. From f09ac1931f77ee8ccefff50c060878aca3a9eb8b Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Mon, 18 Dec 2023 14:27:12 -0500 Subject: [PATCH 4/9] Make BlindedFailure enum pub. Necessary to include it in the public PendingHTLCInfo struct in the next commit. --- lightning/src/ln/channelmanager.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 9f3a3f425fb..9a13d19f3b8 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -305,10 +305,15 @@ pub(super) enum HTLCForwardInfo { }, } -// Used for failing blinded HTLCs backwards correctly. +/// Whether this blinded HTLC is being failed backwards by the introduction node or a blinded node, +/// which determines the failure message that should be used. #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] -enum BlindedFailure { +pub enum BlindedFailure { + /// This HTLC is being failed backwards by the introduction node, and thus should be failed with + /// [`msgs::UpdateFailHTLC`] and error code `0x8000|0x4000|24`. FromIntroductionNode, + /// This HTLC is being failed backwards by a blinded node within the path, and thus should be + /// failed with [`msgs::UpdateFailMalformedHTLC`] and error code `0x8000|0x4000|24`. FromBlindedNode, } From c37d10944ef580d31c78684e1766b3e6b040d06f Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Mon, 18 Dec 2023 14:36:57 -0500 Subject: [PATCH 5/9] Add failure mode info to BlindedForward struct. See added docs. --- lightning/src/ln/channelmanager.rs | 5 ++++- lightning/src/ln/onion_payment.rs | 9 +++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 9a13d19f3b8..fb49fe568ce 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -202,7 +202,9 @@ pub struct BlindedForward { /// onion payload if we're the introduction node. Useful for calculating the next hop's /// [`msgs::UpdateAddHTLC::blinding_point`]. pub inbound_blinding_point: PublicKey, - // Another field will be added here when we support forwarding as a non-intro node. + /// If needed, this determines how this HTLC should be failed backwards, based on whether we are + /// the introduction node. + pub failure: BlindedFailure, } impl PendingHTLCRouting { @@ -9500,6 +9502,7 @@ impl_writeable_tlv_based!(PhantomRouteHints, { impl_writeable_tlv_based!(BlindedForward, { (0, inbound_blinding_point, required), + (1, failure, (default_value, BlindedFailure::FromIntroductionNode)), }); impl_writeable_tlv_based_enum!(PendingHTLCRouting, diff --git a/lightning/src/ln/onion_payment.rs b/lightning/src/ln/onion_payment.rs index 06dc7719b2e..00843d5e4e9 100644 --- a/lightning/src/ln/onion_payment.rs +++ b/lightning/src/ln/onion_payment.rs @@ -12,7 +12,7 @@ use crate::blinded_path; use crate::blinded_path::payment::{PaymentConstraints, PaymentRelay}; use crate::chain::channelmonitor::{HTLC_FAIL_BACK_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS}; use crate::ln::PaymentHash; -use crate::ln::channelmanager::{BlindedForward, CLTV_FAR_FAR_AWAY, HTLCFailureMsg, MIN_CLTV_EXPIRY_DELTA, PendingHTLCInfo, PendingHTLCRouting}; +use crate::ln::channelmanager::{BlindedFailure, BlindedForward, CLTV_FAR_FAR_AWAY, HTLCFailureMsg, MIN_CLTV_EXPIRY_DELTA, PendingHTLCInfo, PendingHTLCRouting}; use crate::ln::features::BlindedHopFeatures; use crate::ln::msgs; use crate::ln::onion_utils; @@ -106,7 +106,12 @@ pub(super) fn create_fwd_pending_htlc_info( onion_packet: outgoing_packet, short_channel_id, blinded: intro_node_blinding_point.or(msg.blinding_point) - .map(|bp| BlindedForward { inbound_blinding_point: bp }), + .map(|bp| BlindedForward { + inbound_blinding_point: bp, + failure: intro_node_blinding_point + .map(|_| BlindedFailure::FromIntroductionNode) + .unwrap_or(BlindedFailure::FromBlindedNode), + }), }, payment_hash: msg.payment_hash, incoming_shared_secret: shared_secret, From a76bb51a3a78899f67404c6c202140f5e9d7d76d Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Mon, 18 Dec 2023 14:42:07 -0500 Subject: [PATCH 6/9] Complete remaining TODOs for failing blinded non-intro forwards. --- lightning/src/ln/channelmanager.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index fb49fe568ce..fc0948932ac 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -210,9 +210,8 @@ pub struct BlindedForward { impl PendingHTLCRouting { // Used to override the onion failure code and data if the HTLC is blinded. fn blinded_failure(&self) -> Option { - // TODO: needs update when we support forwarding blinded HTLCs as non-intro node match self { - Self::Forward { blinded: Some(_), .. } => Some(BlindedFailure::FromIntroductionNode), + Self::Forward { blinded: Some(BlindedForward { failure, .. }), .. } => Some(*failure), Self::Receive { requires_blinded_error: true, .. } => Some(BlindedFailure::FromBlindedNode), _ => None, } @@ -3031,8 +3030,9 @@ where let is_intro_node_forward = match next_hop { onion_utils::Hop::Forward { - // TODO: update this when we support blinded forwarding as non-intro node - next_hop_data: msgs::InboundOnionPayload::BlindedForward { .. }, .. + next_hop_data: msgs::InboundOnionPayload::BlindedForward { + intro_node_blinding_point: Some(_), .. + }, .. } => true, _ => false, }; @@ -4377,7 +4377,7 @@ where incoming_packet_shared_secret: incoming_shared_secret, // Phantom payments are only PendingHTLCRouting::Receive. phantom_shared_secret: None, - blinded_failure: blinded.map(|_| BlindedFailure::FromIntroductionNode), + blinded_failure: blinded.map(|b| b.failure), }); let next_blinding_point = blinded.and_then(|b| { let encrypted_tlvs_ss = self.node_signer.ecdh( From 6e6bd44138e0674cda1c0034b0e1d4b48e54204d Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Wed, 20 Dec 2023 14:00:17 -0500 Subject: [PATCH 7/9] Rename test var to be more descriptive. --- lightning/src/ln/blinded_payment_tests.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lightning/src/ln/blinded_payment_tests.rs b/lightning/src/ln/blinded_payment_tests.rs index 51a286849dd..b2094af1d6e 100644 --- a/lightning/src/ln/blinded_payment_tests.rs +++ b/lightning/src/ln/blinded_payment_tests.rs @@ -560,11 +560,11 @@ fn do_multi_hop_receiver_fail(check: ReceiveCheckFail) { }; let amt_msat = 5000; - let final_cltv_delta = if check == ReceiveCheckFail::ProcessPendingHTLCsCheck { + let excess_final_cltv_delta_opt = if check == ReceiveCheckFail::ProcessPendingHTLCsCheck { // Set the final CLTV expiry too low to trigger the failure in process_pending_htlc_forwards. Some(TEST_FINAL_CLTV as u16 - 2) } else { None }; - let (_, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[2], Some(amt_msat), final_cltv_delta); + let (_, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[2], Some(amt_msat), excess_final_cltv_delta_opt); let mut route_params = get_blinded_route_parameters(amt_msat, payment_secret, nodes.iter().skip(1).map(|n| n.node.get_our_node_id()).collect(), &[&chan_upd_1_2], &chanmon_cfgs[2].keys_manager); @@ -572,7 +572,7 @@ fn do_multi_hop_receiver_fail(check: ReceiveCheckFail) { let route = if check == ReceiveCheckFail::ProcessPendingHTLCsCheck { let mut route = get_route(&nodes[0], &route_params).unwrap(); // Set the final CLTV expiry too low to trigger the failure in process_pending_htlc_forwards. - route.paths[0].blinded_tail.as_mut().map(|bt| bt.excess_final_cltv_expiry_delta = TEST_FINAL_CLTV - 2); + route.paths[0].blinded_tail.as_mut().map(|bt| bt.excess_final_cltv_expiry_delta = excess_final_cltv_delta_opt.unwrap() as u32); route } else if check == ReceiveCheckFail::PaymentConstraints { // Create a blinded path where the receiver's encrypted payload has an htlc_minimum_msat that is From 027aef2b64b9f3e1bb9520e607e00d48a69e7f05 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Wed, 20 Dec 2023 14:04:04 -0500 Subject: [PATCH 8/9] Fix sender double-including shadow offset in CLTV expiry height. The excess delta is included in the final RouteHop::cltv_expiry_delta, so by adding it explicitly to cur_cltv we were erroneously including it twice in the total cltv expiry. This could've add up to an extra MAX_SHADOW_CLTV_DELTA_OFFSET (432) blocks to the total cltv expiry. --- lightning/src/ln/blinded_payment_tests.rs | 2 ++ lightning/src/ln/onion_utils.rs | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lightning/src/ln/blinded_payment_tests.rs b/lightning/src/ln/blinded_payment_tests.rs index b2094af1d6e..a3126b3b537 100644 --- a/lightning/src/ln/blinded_payment_tests.rs +++ b/lightning/src/ln/blinded_payment_tests.rs @@ -572,6 +572,7 @@ fn do_multi_hop_receiver_fail(check: ReceiveCheckFail) { let route = if check == ReceiveCheckFail::ProcessPendingHTLCsCheck { let mut route = get_route(&nodes[0], &route_params).unwrap(); // Set the final CLTV expiry too low to trigger the failure in process_pending_htlc_forwards. + route.paths[0].hops.last_mut().map(|h| h.cltv_expiry_delta += excess_final_cltv_delta_opt.unwrap() as u32); route.paths[0].blinded_tail.as_mut().map(|bt| bt.excess_final_cltv_expiry_delta = excess_final_cltv_delta_opt.unwrap() as u32); route } else if check == ReceiveCheckFail::PaymentConstraints { @@ -680,6 +681,7 @@ fn do_multi_hop_receiver_fail(check: ReceiveCheckFail) { commitment_signed_dance!(nodes[2], nodes[1], (), false, true, false, false); }, ReceiveCheckFail::ProcessPendingHTLCsCheck => { + assert_eq!(payment_event_1_2.msgs[0].cltv_expiry, nodes[0].best_block_info().1 + 1 + excess_final_cltv_delta_opt.unwrap() as u32); nodes[2].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &payment_event_1_2.msgs[0]); check_added_monitors!(nodes[2], 0); do_commitment_signed_dance(&nodes[2], &nodes[1], &payment_event_1_2.commitment_msg, true, true); diff --git a/lightning/src/ln/onion_utils.rs b/lightning/src/ln/onion_utils.rs index 2d75edd30d0..137699521f4 100644 --- a/lightning/src/ln/onion_utils.rs +++ b/lightning/src/ln/onion_utils.rs @@ -188,11 +188,10 @@ pub(super) fn build_onion_payloads(path: &Path, total_msat: u64, mut recipient_o for (i, blinded_hop) in hops.iter().enumerate() { if i == hops.len() - 1 { cur_value_msat += final_value_msat; - cur_cltv += excess_final_cltv_expiry_delta; res.push(msgs::OutboundOnionPayload::BlindedReceive { sender_intended_htlc_amt_msat: *final_value_msat, total_msat, - cltv_expiry_height: cur_cltv, + cltv_expiry_height: cur_cltv + excess_final_cltv_expiry_delta, encrypted_tlvs: blinded_hop.encrypted_payload.clone(), intro_node_blinding_point: blinding_point.take(), }); From aae39b4090f6b4a62e2deea874be59d9f4c3e217 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Thu, 11 Jan 2024 13:47:17 -0500 Subject: [PATCH 9/9] Advertise route blinding feature as supported. Now that we fully support forwarding blinded payments, we should advertise support so nodes on the network can include us in their blinded paths. --- lightning/src/ln/channelmanager.rs | 1 + lightning/src/ln/peer_handler.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index fc0948932ac..34cce867e61 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -9357,6 +9357,7 @@ pub fn provided_init_features(config: &UserConfig) -> InitFeatures { features.set_channel_type_optional(); features.set_scid_privacy_optional(); features.set_zero_conf_optional(); + features.set_route_blinding_optional(); if config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx { features.set_anchors_zero_fee_htlc_tx_optional(); } diff --git a/lightning/src/ln/peer_handler.rs b/lightning/src/ln/peer_handler.rs index 6ffffec4dd0..003e9564509 100644 --- a/lightning/src/ln/peer_handler.rs +++ b/lightning/src/ln/peer_handler.rs @@ -305,6 +305,7 @@ impl ChannelMessageHandler for ErroringMessageHandler { features.set_channel_type_optional(); features.set_scid_privacy_optional(); features.set_zero_conf_optional(); + features.set_route_blinding_optional(); features }