Support forwarding blinded HTLCs as intro node.
authorValentine Wallace <vwallace@protonmail.com>
Thu, 26 Oct 2023 18:37:13 +0000 (14:37 -0400)
committerValentine Wallace <vwallace@protonmail.com>
Thu, 30 Nov 2023 03:43:11 +0000 (22:43 -0500)
Error handling will be completed in upcoming commits.

lightning/src/blinded_path/payment.rs
lightning/src/ln/onion_payment.rs

index 4edfb7d8de05bf0e0187941cc91bf4f646fe5557..3e475893153096ecfef6107ebe9a90fcd0e31028 100644 (file)
@@ -187,7 +187,7 @@ pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
 }
 
 /// `None` if underflow occurs.
-fn amt_to_forward_msat(inbound_amt_msat: u64, payment_relay: &PaymentRelay) -> Option<u64> {
+pub(crate) fn amt_to_forward_msat(inbound_amt_msat: u64, payment_relay: &PaymentRelay) -> Option<u64> {
        let inbound_amt = inbound_amt_msat as u128;
        let base = payment_relay.fee_base_msat as u128;
        let prop = payment_relay.fee_proportional_millionths as u128;
index 3c30a4458f5e21634651eae84c3f26bd7e75a26d..ca15a37c072d89625a0a235fb226d170f758cdb4 100644 (file)
@@ -6,9 +6,12 @@ use bitcoin::hashes::Hash;
 use bitcoin::hashes::sha256::Hash as Sha256;
 use bitcoin::secp256k1::{self, Secp256k1, PublicKey};
 
+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::{CLTV_FAR_FAR_AWAY, HTLCFailureMsg, MIN_CLTV_EXPIRY_DELTA, PendingHTLCInfo, PendingHTLCRouting};
+use crate::ln::channelmanager::{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;
 use crate::ln::onion_utils::{HTLCFailReason, INVALID_ONION_BLINDING};
@@ -28,6 +31,23 @@ pub struct InboundOnionErr {
        pub msg: &'static str,
 }
 
+fn check_blinded_forward(
+       inbound_amt_msat: u64, inbound_cltv_expiry: u32, payment_relay: &PaymentRelay,
+       payment_constraints: &PaymentConstraints, features: &BlindedHopFeatures
+) -> Result<(u64, u32), ()> {
+       let amt_to_forward = blinded_path::payment::amt_to_forward_msat(
+               inbound_amt_msat, payment_relay
+       ).ok_or(())?;
+       let outgoing_cltv_value = inbound_cltv_expiry.checked_sub(
+               payment_relay.cltv_expiry_delta as u32
+       ).ok_or(())?;
+       if inbound_amt_msat < payment_constraints.htlc_minimum_msat ||
+               outgoing_cltv_value > payment_constraints.max_cltv_expiry
+               { return Err(()) }
+       if features.requires_unknown_bits_from(&BlindedHopFeatures::empty()) { return Err(()) }
+       Ok((amt_to_forward, outgoing_cltv_value))
+}
+
 pub(super) fn create_fwd_pending_htlc_info(
        msg: &msgs::UpdateAddHTLC, hop_data: msgs::InboundOnionPayload, hop_hmac: [u8; 32],
        new_packet_bytes: [u8; onion_utils::ONION_DATA_LEN], shared_secret: [u8; 32],
@@ -41,10 +61,27 @@ pub(super) fn create_fwd_pending_htlc_info(
                hmac: hop_hmac,
        };
 
-       let (short_channel_id, amt_to_forward, outgoing_cltv_value) = match hop_data {
+       let (
+               short_channel_id, amt_to_forward, outgoing_cltv_value, inbound_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),
-               msgs::InboundOnionPayload::BlindedForward { .. } => todo!(),
+                       (short_channel_id, amt_to_forward, outgoing_cltv_value, None),
+               msgs::InboundOnionPayload::BlindedForward {
+                       short_channel_id, payment_relay, payment_constraints, intro_node_blinding_point, features,
+               } => {
+                       let (amt_to_forward, outgoing_cltv_value) = check_blinded_forward(
+                               msg.amount_msat, msg.cltv_expiry, &payment_relay, &payment_constraints, &features
+                       ).map_err(|()| {
+                               // We should be returning malformed here if `msg.blinding_point` is set, but this is
+                               // unreachable right now since we checked it in `decode_update_add_htlc_onion`.
+                               InboundOnionErr {
+                                       msg: "Underflow calculating outbound amount or cltv value for blinded forward",
+                                       err_code: INVALID_ONION_BLINDING,
+                                       err_data: vec![0; 32],
+                               }
+                       })?;
+                       (short_channel_id, amt_to_forward, outgoing_cltv_value, Some(intro_node_blinding_point))
+               },
                msgs::InboundOnionPayload::Receive { .. } | msgs::InboundOnionPayload::BlindedReceive { .. } =>
                        return Err(InboundOnionErr {
                                msg: "Final Node OnionHopData provided for us as an intermediary node",
@@ -57,7 +94,7 @@ pub(super) fn create_fwd_pending_htlc_info(
                routing: PendingHTLCRouting::Forward {
                        onion_packet: outgoing_packet,
                        short_channel_id,
-                       blinded: None,
+                       blinded: inbound_blinding_point.map(|bp| BlindedForward { inbound_blinding_point: bp }),
                },
                payment_hash: msg.payment_hash,
                incoming_shared_secret: shared_secret,
@@ -336,9 +373,25 @@ where
                        }
                },
                onion_utils::Hop::Forward {
-                       next_hop_data: msgs::InboundOnionPayload::BlindedForward { .. }, ..
+                       next_hop_data: msgs::InboundOnionPayload::BlindedForward {
+                               short_channel_id, ref payment_relay, ref payment_constraints, ref features, ..
+                       }, ..
                } => {
-                       todo!()
+                       let (amt_to_forward, outgoing_cltv_value) = match check_blinded_forward(
+                               msg.amount_msat, msg.cltv_expiry, &payment_relay, &payment_constraints, &features
+                       ) {
+                               Ok((amt, cltv)) => (amt, cltv),
+                               Err(()) => {
+                                       return_err!("Underflow calculating outbound amount or cltv value for blinded forward",
+                                               INVALID_ONION_BLINDING, &[0; 32]);
+                               }
+                       };
+                       let next_packet_pubkey = onion_utils::next_hop_pubkey(&secp_ctx,
+                               msg.onion_routing_packet.public_key.unwrap(), &shared_secret);
+                       NextPacketDetails {
+                               next_packet_pubkey, outgoing_scid: short_channel_id, outgoing_amt_msat: amt_to_forward,
+                               outgoing_cltv_value
+                       }
                },
                onion_utils::Hop::Receive { .. } => return Ok((next_hop, shared_secret, None)),
                onion_utils::Hop::Forward { next_hop_data: msgs::InboundOnionPayload::Receive { .. }, .. } |