X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Frouting%2Frouter.rs;h=0910985418ed8b23a958efa5f9056bef8414c3e7;hb=b5827f77ad539ce047e513401f6cabb08576e204;hp=6f131fc56d6caa6aa8517cdb6cf19bad9b5dcf0a;hpb=369eea45a1efadda2dc1fe6496dfdcaad0c4d1e1;p=rust-lightning diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs index 6f131fc5..09109854 100644 --- a/lightning/src/routing/router.rs +++ b/lightning/src/routing/router.rs @@ -13,10 +13,12 @@ use bitcoin::secp256k1::PublicKey; use bitcoin::hashes::Hash; use bitcoin::hashes::sha256::Hash as Sha256; +use crate::blinded_path::BlindedPath; use crate::ln::PaymentHash; use crate::ln::channelmanager::{ChannelDetails, PaymentId}; use crate::ln::features::{ChannelFeatures, InvoiceFeatures, NodeFeatures}; use crate::ln::msgs::{DecodeError, ErrorAction, LightningError, MAX_VALUE_MSAT}; +use crate::offers::invoice::BlindedPayInfo; use crate::routing::gossip::{DirectedChannelInfo, EffectiveCapacity, ReadOnlyNetworkGraph, NetworkGraph, NodeId, RoutingFees}; use crate::routing::scoring::{ChannelUsage, LockableScore, Score}; use crate::util::ser::{Writeable, Readable, ReadableArgs, Writer}; @@ -210,7 +212,8 @@ impl Readable for InFlightHtlcs { } } -/// A hop in a route +/// A hop in a route, and additional metadata about it. "Hop" is defined as a node and the channel +/// that leads to it. #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct RouteHop { /// The node_id of the node at this hop. @@ -224,11 +227,10 @@ pub struct RouteHop { /// to reach this node. pub channel_features: ChannelFeatures, /// The fee taken on this hop (for paying for the use of the *next* channel in the path). - /// For the last hop, this should be the full value of the payment (might be more than - /// requested if we had to match htlc_minimum_msat). + /// For the last hop, this should be the full value of this path's part of the payment. pub fee_msat: u64, - /// The CLTV delta added for this hop. For the last hop, this should be the full CLTV value - /// expected at the destination, in excess of the current block height. + /// The CLTV delta added for this hop. For the last hop, this is the CLTV delta expected at the + /// destination. pub cltv_expiry_delta: u32, } @@ -245,11 +247,11 @@ impl_writeable_tlv_based!(RouteHop, { /// it can take multiple paths. Each path is composed of one or more hops through the network. #[derive(Clone, Hash, PartialEq, Eq)] pub struct Route { - /// The list of routes taken for a single (potentially-)multi-part payment. The pubkey of the - /// last RouteHop in each path must be the same. Each entry represents a list of hops, NOT - /// INCLUDING our own, where the last hop is the destination. Thus, this must always be at - /// least length one. While the maximum length of any given path is variable, keeping the length - /// of any path less or equal to 19 should currently ensure it is viable. + /// The list of paths taken for a single (potentially-)multi-part payment. The pubkey of the + /// last [`RouteHop`] in each path must be the same. Each entry represents a list of hops, where + /// the last hop is the destination. Thus, this must always be at least length one. While the + /// maximum length of any given path is variable, keeping the length of any path less or equal to + /// 19 should currently ensure it is viable. pub paths: Vec>, /// The `payment_params` parameter passed to [`find_route`]. /// This is used by `ChannelManager` to track information which may be required for retries, @@ -281,7 +283,8 @@ impl Route { self.paths.iter().map(|path| path.get_path_fees()).sum() } - /// Returns the total amount paid on this [`Route`], excluding the fees. + /// Returns the total amount paid on this [`Route`], excluding the fees. Might be more than + /// requested if we had to reach htlc_minimum_msat. pub fn get_total_amount(&self) -> u64 { return self.paths.iter() .map(|path| path.split_last().map(|(hop, _)| hop.fee_msat).unwrap_or(0)) @@ -420,7 +423,7 @@ pub struct PaymentParameters { pub features: Option, /// Hints for routing to the payee, containing channels connecting the payee to public nodes. - pub route_hints: Vec, + pub route_hints: Hints, /// Expiration of a payment to the payee, in seconds relative to the UNIX epoch. pub expiry_time: Option, @@ -459,15 +462,22 @@ pub struct PaymentParameters { impl Writeable for PaymentParameters { fn write(&self, writer: &mut W) -> Result<(), io::Error> { + let mut clear_hints = &vec![]; + let mut blinded_hints = &vec![]; + match &self.route_hints { + Hints::Clear(hints) => clear_hints = hints, + Hints::Blinded(hints) => blinded_hints = hints, + } write_tlv_fields!(writer, { (0, self.payee_pubkey, required), (1, self.max_total_cltv_expiry_delta, required), (2, self.features, option), (3, self.max_path_count, required), - (4, self.route_hints, vec_type), + (4, *clear_hints, vec_type), (5, self.max_channel_saturation_power_of_half, required), (6, self.expiry_time, option), (7, self.previously_failed_channels, vec_type), + (8, *blinded_hints, optional_vec), (9, self.final_cltv_expiry_delta, required), }); Ok(()) @@ -485,14 +495,23 @@ impl ReadableArgs for PaymentParameters { (5, max_channel_saturation_power_of_half, (default_value, 2)), (6, expiry_time, option), (7, previously_failed_channels, vec_type), + (8, blinded_route_hints, optional_vec), (9, final_cltv_expiry_delta, (default_value, default_final_cltv_expiry_delta)), }); + let clear_route_hints = route_hints.unwrap_or(vec![]); + let blinded_route_hints = blinded_route_hints.unwrap_or(vec![]); + let route_hints = if blinded_route_hints.len() != 0 { + if clear_route_hints.len() != 0 { return Err(DecodeError::InvalidValue) } + Hints::Blinded(blinded_route_hints) + } else { + Hints::Clear(clear_route_hints) + }; Ok(Self { payee_pubkey: _init_tlv_based_struct_field!(payee_pubkey, required), max_total_cltv_expiry_delta: _init_tlv_based_struct_field!(max_total_cltv_expiry_delta, (default_value, unused)), features, max_path_count: _init_tlv_based_struct_field!(max_path_count, (default_value, unused)), - route_hints: route_hints.unwrap_or(Vec::new()), + route_hints, max_channel_saturation_power_of_half: _init_tlv_based_struct_field!(max_channel_saturation_power_of_half, (default_value, unused)), expiry_time, previously_failed_channels: previously_failed_channels.unwrap_or(Vec::new()), @@ -511,7 +530,7 @@ impl PaymentParameters { Self { payee_pubkey, features: None, - route_hints: vec![], + route_hints: Hints::Clear(vec![]), expiry_time: None, max_total_cltv_expiry_delta: DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, max_path_count: DEFAULT_MAX_PATH_COUNT, @@ -540,7 +559,7 @@ impl PaymentParameters { /// /// This is not exported to bindings users since bindings don't support move semantics pub fn with_route_hints(self, route_hints: Vec) -> Self { - Self { route_hints, ..self } + Self { route_hints: Hints::Clear(route_hints), ..self } } /// Includes a payment expiration in seconds relative to the UNIX epoch. @@ -572,6 +591,16 @@ impl PaymentParameters { } } +/// Routing hints for the tail of the route. +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub enum Hints { + /// The recipient provided blinded paths and payinfo to reach them. The blinded paths themselves + /// will be included in the final [`Route`]. + Blinded(Vec<(BlindedPayInfo, BlindedPath)>), + /// The recipient included these route hints in their BOLT11 invoice. + Clear(Vec), +} + /// A list of hops along a payment path terminating with a channel to the recipient. #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct RouteHint(pub Vec); @@ -1021,12 +1050,18 @@ where L::Target: Logger { return Err(LightningError{err: "Cannot send a payment of 0 msat".to_owned(), action: ErrorAction::IgnoreError}); } - for route in payment_params.route_hints.iter() { - for hop in &route.0 { - if hop.src_node_id == payment_params.payee_pubkey { - return Err(LightningError{err: "Route hint cannot have the payee as the source.".to_owned(), action: ErrorAction::IgnoreError}); + match &payment_params.route_hints { + Hints::Clear(hints) => { + for route in hints.iter() { + for hop in &route.0 { + if hop.src_node_id == payment_params.payee_pubkey { + return Err(LightningError{err: "Route hint cannot have the payee as the source.".to_owned(), action: ErrorAction::IgnoreError}); + } + } } - } + }, + _ => return Err(LightningError{err: "Routing to blinded paths isn't supported yet".to_owned(), action: ErrorAction::IgnoreError}), + } if payment_params.max_total_cltv_expiry_delta <= final_cltv_expiry_delta { return Err(LightningError{err: "Can't find a route where the maximum total CLTV expiry delta is below the final CLTV expiry.".to_owned(), action: ErrorAction::IgnoreError}); @@ -1551,7 +1586,11 @@ where L::Target: Logger { // If a caller provided us with last hops, add them to routing targets. Since this happens // earlier than general path finding, they will be somewhat prioritized, although currently // it matters only if the fees are exactly the same. - for route in payment_params.route_hints.iter().filter(|route| !route.0.is_empty()) { + let route_hints = match &payment_params.route_hints { + Hints::Clear(hints) => hints, + _ => return Err(LightningError{err: "Routing to blinded paths isn't supported yet".to_owned(), action: ErrorAction::IgnoreError}), + }; + for route in route_hints.iter().filter(|route| !route.0.is_empty()) { let first_hop_in_route = &(route.0)[0]; let have_hop_src_in_graph = // Only add the hops in this route to our candidate set if either