Correctly fail back on outbound channel check for blinded HTLC
authorValentine Wallace <vwallace@protonmail.com>
Fri, 15 Sep 2023 22:21:28 +0000 (18:21 -0400)
committerValentine Wallace <vwallace@protonmail.com>
Thu, 30 Nov 2023 03:46:49 +0000 (22:46 -0500)
Forwarding intro nodes should always fail with 0x8000|0x4000|24.

lightning/src/ln/blinded_payment_tests.rs
lightning/src/ln/channelmanager.rs

index e20464cb964a956a08dad8a8e07c246f76dfd5a5..e26da7922096f23c0c52a79c4a8a9739fdc484cd 100644 (file)
@@ -120,12 +120,15 @@ enum ForwardCheckFail {
        InboundOnionCheck,
        // The forwarding node's payload is encoded as a receive, i.e. the next hop HMAC is [0; 32].
        ForwardPayloadEncodedAsReceive,
+       // Fail a check on the outbound channel. In this case, our next-hop peer is offline.
+       OutboundChannelCheck,
 }
 
 #[test]
 fn forward_checks_failure() {
        do_forward_checks_failure(ForwardCheckFail::InboundOnionCheck);
        do_forward_checks_failure(ForwardCheckFail::ForwardPayloadEncodedAsReceive);
+       do_forward_checks_failure(ForwardCheckFail::OutboundChannelCheck);
 }
 
 fn do_forward_checks_failure(check: ForwardCheckFail) {
@@ -200,6 +203,10 @@ fn do_forward_checks_failure(check: ForwardCheckFail) {
                        onion_payloads.pop();
                        update_add.onion_routing_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &payment_hash).unwrap();
                },
+               ForwardCheckFail::OutboundChannelCheck => {
+                       // The intro node will see that the next-hop peer is disconnected and fail the HTLC backwards.
+                       nodes[1].node.peer_disconnected(&nodes[2].node.get_our_node_id());
+               },
        }
        nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &payment_event.msgs[0]);
        check_added_monitors!(nodes[1], 0);
index d56d1349231acffca4143ede3c00f16c458bb77e..5a6694fe5f4b5641cace4146c3c0191f9193e8a5 100644 (file)
@@ -53,7 +53,7 @@ use crate::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringFeeParame
 use crate::ln::onion_payment::{check_incoming_htlc_cltv, create_recv_pending_htlc_info, create_fwd_pending_htlc_info, decode_incoming_update_add_htlc_onion, InboundOnionErr, NextPacketDetails};
 use crate::ln::msgs;
 use crate::ln::onion_utils;
-use crate::ln::onion_utils::HTLCFailReason;
+use crate::ln::onion_utils::{HTLCFailReason, INVALID_ONION_BLINDING};
 use crate::ln::msgs::{ChannelMessageHandler, DecodeError, LightningError};
 #[cfg(test)]
 use crate::ln::outbound_payment;
@@ -2977,14 +2977,24 @@ where
                        msg, &self.node_signer, &self.logger, &self.secp_ctx
                )?;
 
+               let is_blinded = match next_hop {
+                       onion_utils::Hop::Forward {
+                               next_hop_data: msgs::InboundOnionPayload::BlindedForward { .. }, ..
+                       } => true,
+                       _ => false, // TODO: update this when we support receiving to multi-hop blinded paths
+               };
+
                macro_rules! return_err {
                        ($msg: expr, $err_code: expr, $data: expr) => {
                                {
                                        log_info!(self.logger, "Failed to accept/forward incoming HTLC: {}", $msg);
+                                       let (err_code, err_data) = if is_blinded {
+                                               (INVALID_ONION_BLINDING, &[0; 32][..])
+                                       } else { ($err_code, $data) };
                                        return Err(HTLCFailureMsg::Relay(msgs::UpdateFailHTLC {
                                                channel_id: msg.channel_id,
                                                htlc_id: msg.htlc_id,
-                                               reason: HTLCFailReason::reason($err_code, $data.to_vec())
+                                               reason: HTLCFailReason::reason(err_code, err_data.to_vec())
                                                        .get_encrypted_failure_packet(&shared_secret, &None),
                                        }));
                                }