]> git.bitcoin.ninja Git - rust-lightning/commitdiff
outbound_payment: set max path length in PaymentParameters.
authorValentine Wallace <vwallace@protonmail.com>
Mon, 15 Apr 2024 20:31:29 +0000 (16:31 -0400)
committerValentine Wallace <vwallace@protonmail.com>
Mon, 20 May 2024 22:57:13 +0000 (18:57 -0400)
So the router knows how long the maximum payment path can be.

lightning/src/ln/blinded_payment_tests.rs
lightning/src/ln/onion_utils.rs
lightning/src/ln/outbound_payment.rs

index 998c7a33d66b5eaf8cef4b0c33a6428c45a047fa..4c254628de6ec63525473aa927802ec6a8eb270c 100644 (file)
@@ -282,9 +282,10 @@ fn do_forward_checks_failure(check: ForwardCheckFail, intro_fails: bool) {
 
        let amt_msat = 5000;
        let (_, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[3], Some(amt_msat), None);
-       let route_params = get_blinded_route_parameters(amt_msat, payment_secret, 1, 1_0000_0000,
+       let mut route_params = get_blinded_route_parameters(amt_msat, payment_secret, 1, 1_0000_0000,
                nodes.iter().skip(1).map(|n| n.node.get_our_node_id()).collect(),
                &[&chan_upd_1_2, &chan_upd_2_3], &chanmon_cfgs[3].keys_manager);
+       route_params.payment_params.max_path_length = 18;
 
        let route = get_route(&nodes[0], &route_params).unwrap();
        node_cfgs[0].router.expect_find_route(route_params.clone(), Ok(route.clone()));
index 2aba2c75748ed4ad74813206b7e302964d594075..9a207c9e52d1dd1805592bf039dd9ff84387a6b4 100644 (file)
 use crate::blinded_path::BlindedHop;
 use crate::crypto::chacha20::ChaCha20;
 use crate::crypto::streams::ChaChaReader;
+use crate::ln::channel::TOTAL_BITCOIN_SUPPLY_SATOSHIS;
 use crate::ln::channelmanager::{HTLCSource, RecipientOnionFields};
+use crate::ln::features::{ChannelFeatures, NodeFeatures};
 use crate::ln::msgs;
 use crate::ln::types::{PaymentHash, PaymentPreimage};
 use crate::ln::wire::Encode;
 use crate::routing::gossip::NetworkUpdate;
-use crate::routing::router::{Path, RouteHop};
+use crate::routing::router::{Path, RouteHop, RouteParameters};
 use crate::sign::NodeSigner;
 use crate::util::errors::{self, APIError};
 use crate::util::logger::Logger;
@@ -310,6 +312,77 @@ where
        Ok((cur_value_msat, cur_cltv))
 }
 
+pub(crate) const MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY: u64 = 100_000_000;
+
+pub(crate) fn set_max_path_length(
+       route_params: &mut RouteParameters, recipient_onion: &RecipientOnionFields,
+       keysend_preimage: Option<PaymentPreimage>, best_block_height: u32,
+) -> Result<(), ()> {
+       const PAYLOAD_HMAC_LEN: usize = 32;
+       let unblinded_intermed_payload_len = msgs::OutboundOnionPayload::Forward {
+               short_channel_id: 42,
+               amt_to_forward: TOTAL_BITCOIN_SUPPLY_SATOSHIS,
+               outgoing_cltv_value: route_params.payment_params.max_total_cltv_expiry_delta,
+       }
+       .serialized_length()
+       .saturating_add(PAYLOAD_HMAC_LEN);
+
+       const OVERPAY_ESTIMATE_MULTIPLER: u64 = 3;
+       let final_value_msat_with_overpay_buffer = core::cmp::max(
+               route_params.final_value_msat.saturating_mul(OVERPAY_ESTIMATE_MULTIPLER),
+               MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY,
+       );
+
+       let blinded_tail_opt = route_params
+               .payment_params
+               .payee
+               .blinded_route_hints()
+               .iter()
+               .map(|(_, path)| path)
+               .max_by_key(|path| path.serialized_length())
+               .map(|largest_path| BlindedTailHopIter {
+                       hops: largest_path.blinded_hops.iter(),
+                       blinding_point: largest_path.blinding_point,
+                       final_value_msat: final_value_msat_with_overpay_buffer,
+                       excess_final_cltv_expiry_delta: 0,
+               });
+
+       let unblinded_route_hop = RouteHop {
+               pubkey: PublicKey::from_slice(&[2; 33]).unwrap(),
+               node_features: NodeFeatures::empty(),
+               short_channel_id: 42,
+               channel_features: ChannelFeatures::empty(),
+               fee_msat: final_value_msat_with_overpay_buffer,
+               cltv_expiry_delta: route_params.payment_params.max_total_cltv_expiry_delta,
+               maybe_announced_channel: false,
+       };
+       let mut num_reserved_bytes: usize = 0;
+       let build_payloads_res = build_onion_payloads_callback(
+               core::iter::once(&unblinded_route_hop),
+               blinded_tail_opt,
+               final_value_msat_with_overpay_buffer,
+               &recipient_onion,
+               best_block_height,
+               &keysend_preimage,
+               |_, payload| {
+                       num_reserved_bytes = num_reserved_bytes
+                               .saturating_add(payload.serialized_length())
+                               .saturating_add(PAYLOAD_HMAC_LEN);
+               },
+       );
+       debug_assert!(build_payloads_res.is_ok());
+
+       let max_path_length = 1300usize
+               .checked_sub(num_reserved_bytes)
+               .map(|p| p / unblinded_intermed_payload_len)
+               .and_then(|l| u8::try_from(l.saturating_add(1)).ok())
+               .ok_or(())?;
+
+       route_params.payment_params.max_path_length =
+               core::cmp::min(max_path_length, route_params.payment_params.max_path_length);
+       Ok(())
+}
+
 /// Length of the onion data packet. Before TLV-based onions this was 20 65-byte hops, though now
 /// the hops can be of variable length.
 pub(crate) const ONION_DATA_LEN: usize = 20 * 65;
index c452a660e4239dee0d96d42ea28552796b25ed35..b08a1e8a9bb1e3a565fcaf9c43492a9f22167c65 100644 (file)
@@ -17,6 +17,7 @@ use crate::sign::{EntropySource, NodeSigner, Recipient};
 use crate::events::{self, PaymentFailureReason};
 use crate::ln::types::{PaymentHash, PaymentPreimage, PaymentSecret};
 use crate::ln::channelmanager::{ChannelDetails, EventCompletionAction, HTLCSource, PaymentId};
+use crate::ln::onion_utils;
 use crate::ln::onion_utils::{DecodedOnionFailure, HTLCFailReason};
 use crate::offers::invoice::Bolt12Invoice;
 use crate::routing::router::{BlindedTail, InFlightHtlcs, Path, PaymentParameters, Route, RouteParameters, Router};
@@ -421,6 +422,12 @@ pub enum RetryableSendFailure {
        /// [`Event::PaymentSent`]: crate::events::Event::PaymentSent
        /// [`Event::PaymentFailed`]: crate::events::Event::PaymentFailed
        DuplicatePayment,
+       /// The [`RecipientOnionFields::payment_metadata`], [`RecipientOnionFields::custom_tlvs`], or
+       /// [`BlindedPath`]s provided are too large and caused us to exceed the maximum onion packet size
+       /// of 1300 bytes.
+       ///
+       /// [`BlindedPath`]: crate::blinded_path::BlindedPath
+       OnionPacketSizeExceeded,
 }
 
 /// If a payment fails to send with [`ChannelManager::send_payment_with_route`], it can be in one
@@ -886,7 +893,7 @@ impl OutboundPayments {
        /// [`Event::PaymentFailed`]: crate::events::Event::PaymentFailed
        fn send_payment_internal<R: Deref, NS: Deref, ES: Deref, IH, SP, L: Deref>(
                &self, payment_id: PaymentId, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields,
-               keysend_preimage: Option<PaymentPreimage>, retry_strategy: Retry, route_params: RouteParameters,
+               keysend_preimage: Option<PaymentPreimage>, retry_strategy: Retry, mut route_params: RouteParameters,
                router: &R, first_hops: Vec<ChannelDetails>, inflight_htlcs: IH, entropy_source: &ES,
                node_signer: &NS, best_block_height: u32, logger: &L,
                pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>, send_payment_along_path: SP,
@@ -907,6 +914,15 @@ impl OutboundPayments {
                        }
                }
 
+               onion_utils::set_max_path_length(
+                       &mut route_params, &recipient_onion, keysend_preimage, best_block_height
+               )
+                       .map_err(|()| {
+                               log_error!(logger, "Can't construct an onion packet without exceeding 1300-byte onion \
+                                       hop_data length for payment with id {} and hash {}", payment_id, payment_hash);
+                               RetryableSendFailure::OnionPacketSizeExceeded
+                       })?;
+
                let mut route = router.find_route_with_id(
                        &node_signer.get_node_id(Recipient::Node).unwrap(), &route_params,
                        Some(&first_hops.iter().collect::<Vec<_>>()), inflight_htlcs(),