use bitcoin::secp256k1::{PublicKey, Secp256k1, self};
-use crate::blinded_path::{BlindedHop, BlindedPath};
+use crate::blinded_path::{BlindedHop, BlindedPath, Direction, IntroductionNode};
use crate::blinded_path::payment::{ForwardNode, ForwardTlvs, PaymentConstraints, PaymentRelay, ReceiveTlvs};
use crate::ln::PaymentHash;
use crate::ln::channelmanager::{ChannelDetails, PaymentId, MIN_FINAL_CLTV_EXPIRY_DELTA};
///
/// [`find_route`] validates this prior to constructing a [`CandidateRouteHop`].
///
- /// This is not exported to bindings users as lifetimes are not expressable in most languages.
+ /// This is not exported to bindings users as lifetimes are not expressible in most languages.
pub details: &'a ChannelDetails,
/// The node id of the payer, which is also the source side of this candidate route hop.
///
- /// This is not exported to bindings users as lifetimes are not expressable in most languages.
+ /// This is not exported to bindings users as lifetimes are not expressible in most languages.
pub payer_node_id: &'a NodeId,
}
/// Information about the channel, including potentially its capacity and
/// direction-specific information.
///
- /// This is not exported to bindings users as lifetimes are not expressable in most languages.
+ /// This is not exported to bindings users as lifetimes are not expressible in most languages.
pub info: DirectedChannelInfo<'a>,
/// The short channel ID of the channel, i.e. the identifier by which we refer to this
/// channel.
pub struct PrivateHopCandidate<'a> {
/// Information about the private hop communicated via BOLT 11.
///
- /// This is not exported to bindings users as lifetimes are not expressable in most languages.
+ /// This is not exported to bindings users as lifetimes are not expressible in most languages.
pub hint: &'a RouteHintHop,
/// Node id of the next hop in BOLT 11 route hint.
///
- /// This is not exported to bindings users as lifetimes are not expressable in most languages.
+ /// This is not exported to bindings users as lifetimes are not expressible in most languages.
pub target_node_id: &'a NodeId
}
/// Information about the blinded path including the fee, HTLC amount limits, and
/// cryptographic material required to build an HTLC through the given path.
///
- /// This is not exported to bindings users as lifetimes are not expressable in most languages.
+ /// This is not exported to bindings users as lifetimes are not expressible in most languages.
pub hint: &'a (BlindedPayInfo, BlindedPath),
/// Index of the hint in the original list of blinded hints.
///
///
/// Note that the [`BlindedPayInfo`] is ignored here.
///
- /// This is not exported to bindings users as lifetimes are not expressable in most languages.
+ /// This is not exported to bindings users as lifetimes are not expressible in most languages.
pub hint: &'a (BlindedPayInfo, BlindedPath),
/// Index of the hint in the original list of blinded hints.
///
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {
CandidateRouteHop::Blinded(BlindedPathCandidate { hint, .. }) | CandidateRouteHop::OneHopBlinded(OneHopBlindedPathCandidate { hint, .. }) => {
- "blinded route hint with introduction node id ".fmt(f)?;
- hint.1.introduction_node_id.fmt(f)?;
+ "blinded route hint with introduction node ".fmt(f)?;
+ match &hint.1.introduction_node {
+ IntroductionNode::NodeId(pubkey) => write!(f, "id {}", pubkey)?,
+ IntroductionNode::DirectedShortChannelId(direction, scid) => {
+ match direction {
+ Direction::NodeOne => {
+ write!(f, "one on channel with SCID {}", scid)?;
+ },
+ Direction::NodeTwo => {
+ write!(f, "two on channel with SCID {}", scid)?;
+ },
+ }
+ }
+ }
" and blinding point ".fmt(f)?;
hint.1.blinding_point.fmt(f)
},
return Err(LightningError{err: "Cannot send a payment of 0 msat".to_owned(), action: ErrorAction::IgnoreError});
}
+ let introduction_node_id_cache = payment_params.payee.blinded_route_hints().iter()
+ .map(|(_, path)| path.public_introduction_node_id(network_graph))
+ .collect::<Vec<_>>();
match &payment_params.payee {
Payee::Clear { route_hints, node_id, .. } => {
for route in route_hints.iter() {
}
},
Payee::Blinded { route_hints, .. } => {
- if route_hints.iter().all(|(_, path)| path.public_introduction_node_id(network_graph) == Some(&our_node_id)) {
+ if introduction_node_id_cache.iter().all(|introduction_node_id| *introduction_node_id == Some(&our_node_id)) {
return Err(LightningError{err: "Cannot generate a route to blinded paths if we are the introduction node to all of them".to_owned(), action: ErrorAction::IgnoreError});
}
- for (_, blinded_path) in route_hints.iter() {
+ for ((_, blinded_path), introduction_node_id) in route_hints.iter().zip(introduction_node_id_cache.iter()) {
if blinded_path.blinded_hops.len() == 0 {
return Err(LightningError{err: "0-hop blinded path provided".to_owned(), action: ErrorAction::IgnoreError});
- } else if blinded_path.public_introduction_node_id(network_graph) == Some(&our_node_id) {
+ } else if *introduction_node_id == Some(&our_node_id) {
log_info!(logger, "Got blinded path with ourselves as the introduction node, ignoring");
} else if blinded_path.blinded_hops.len() == 1 &&
- route_hints.iter().any( |(_, p)| p.blinded_hops.len() == 1
- && p.public_introduction_node_id(network_graph) != blinded_path.public_introduction_node_id(network_graph))
+ route_hints
+ .iter().zip(introduction_node_id_cache.iter())
+ .filter(|((_, p), _)| p.blinded_hops.len() == 1)
+ .any(|(_, p_introduction_node_id)| p_introduction_node_id != introduction_node_id)
{
return Err(LightningError{err: format!("1-hop blinded paths must all have matching introduction node ids"), action: ErrorAction::IgnoreError});
}
// Only add the hops in this route to our candidate set if either
// we have a direct channel to the first hop or the first hop is
// in the regular network graph.
- let source_node_id = match hint.1.public_introduction_node_id(network_graph) {
+ let source_node_id = match introduction_node_id_cache[hint_idx] {
Some(node_id) => node_id,
- None => {
- let node_id = NodeId::from_pubkey(&hint.1.introduction_node_id);
- match first_hop_targets.get_key_value(&node_id).map(|(key, _)| key) {
- Some(node_id) => node_id,
- None => continue,
- }
+ None => match &hint.1.introduction_node {
+ IntroductionNode::NodeId(pubkey) => {
+ let node_id = NodeId::from_pubkey(&pubkey);
+ match first_hop_targets.get_key_value(&node_id).map(|(key, _)| key) {
+ Some(node_id) => node_id,
+ None => continue,
+ }
+ },
+ IntroductionNode::DirectedShortChannelId(direction, scid) => {
+ let first_hop = first_hop_targets.iter().find(|(_, channels)|
+ channels
+ .iter()
+ .any(|details| Some(*scid) == details.get_outbound_payment_scid())
+ );
+ match first_hop {
+ Some((counterparty_node_id, _)) => {
+ direction.select_node_id(&our_node_id, counterparty_node_id)
+ },
+ None => continue,
+ }
+ },
},
};
if our_node_id == *source_node_id { continue }
#[cfg(test)]
mod tests {
- use crate::blinded_path::{BlindedHop, BlindedPath};
+ use crate::blinded_path::{BlindedHop, BlindedPath, IntroductionNode};
use crate::routing::gossip::{NetworkGraph, P2PGossipSync, NodeId, EffectiveCapacity};
use crate::routing::utxo::UtxoResult;
use crate::routing::router::{get_route, build_route_from_hops_internal, add_random_cltv_offset, default_node_features,
// MPP to a 1-hop blinded path for nodes[2]
let bolt12_features = channelmanager::provided_bolt12_invoice_features(&config);
let blinded_path = BlindedPath {
- introduction_node_id: nodes[2],
+ introduction_node: IntroductionNode::NodeId(nodes[2]),
blinding_point: ln_test_utils::pubkey(42),
blinded_hops: vec![BlindedHop { blinded_node_id: ln_test_utils::pubkey(42 as u8), encrypted_payload: Vec::new() }],
};
// MPP to 3 2-hop blinded paths
let mut blinded_path_node_0 = blinded_path.clone();
- blinded_path_node_0.introduction_node_id = nodes[0];
+ blinded_path_node_0.introduction_node = IntroductionNode::NodeId(nodes[0]);
blinded_path_node_0.blinded_hops.push(blinded_path.blinded_hops[0].clone());
let mut node_0_payinfo = blinded_payinfo.clone();
node_0_payinfo.htlc_maximum_msat = 50_000;
let mut blinded_path_node_7 = blinded_path_node_0.clone();
- blinded_path_node_7.introduction_node_id = nodes[7];
+ blinded_path_node_7.introduction_node = IntroductionNode::NodeId(nodes[7]);
let mut node_7_payinfo = blinded_payinfo.clone();
node_7_payinfo.htlc_maximum_msat = 60_000;
let mut blinded_path_node_1 = blinded_path_node_0.clone();
- blinded_path_node_1.introduction_node_id = nodes[1];
+ blinded_path_node_1.introduction_node = IntroductionNode::NodeId(nodes[1]);
let mut node_1_payinfo = blinded_payinfo.clone();
node_1_payinfo.htlc_maximum_msat = 180_000;
// Make sure this works for blinded route hints.
let blinded_path = BlindedPath {
- introduction_node_id: intermed_node_id,
+ introduction_node: IntroductionNode::NodeId(intermed_node_id),
blinding_point: ln_test_utils::pubkey(42),
blinded_hops: vec![
BlindedHop { blinded_node_id: ln_test_utils::pubkey(42), encrypted_payload: vec![] },
#[test]
fn blinded_route_ser() {
let blinded_path_1 = BlindedPath {
- introduction_node_id: ln_test_utils::pubkey(42),
+ introduction_node: IntroductionNode::NodeId(ln_test_utils::pubkey(42)),
blinding_point: ln_test_utils::pubkey(43),
blinded_hops: vec![
BlindedHop { blinded_node_id: ln_test_utils::pubkey(44), encrypted_payload: Vec::new() },
],
};
let blinded_path_2 = BlindedPath {
- introduction_node_id: ln_test_utils::pubkey(46),
+ introduction_node: IntroductionNode::NodeId(ln_test_utils::pubkey(46)),
blinding_point: ln_test_utils::pubkey(47),
blinded_hops: vec![
BlindedHop { blinded_node_id: ln_test_utils::pubkey(48), encrypted_payload: Vec::new() },
// account for the blinded tail's final amount_msat.
let mut inflight_htlcs = InFlightHtlcs::new();
let blinded_path = BlindedPath {
- introduction_node_id: ln_test_utils::pubkey(43),
+ introduction_node: IntroductionNode::NodeId(ln_test_utils::pubkey(43)),
blinding_point: ln_test_utils::pubkey(48),
blinded_hops: vec![BlindedHop { blinded_node_id: ln_test_utils::pubkey(49), encrypted_payload: Vec::new() }],
};
maybe_announced_channel: false,
},
RouteHop {
- pubkey: blinded_path.introduction_node_id,
+ pubkey: ln_test_utils::pubkey(43),
node_features: NodeFeatures::empty(),
short_channel_id: 43,
channel_features: ChannelFeatures::empty(),
fn blinded_path_cltv_shadow_offset() {
// Make sure we add a shadow offset when sending to blinded paths.
let blinded_path = BlindedPath {
- introduction_node_id: ln_test_utils::pubkey(43),
+ introduction_node: IntroductionNode::NodeId(ln_test_utils::pubkey(43)),
blinding_point: ln_test_utils::pubkey(44),
blinded_hops: vec![
BlindedHop { blinded_node_id: ln_test_utils::pubkey(45), encrypted_payload: Vec::new() },
maybe_announced_channel: false,
},
RouteHop {
- pubkey: blinded_path.introduction_node_id,
+ pubkey: ln_test_utils::pubkey(43),
node_features: NodeFeatures::empty(),
short_channel_id: 43,
channel_features: ChannelFeatures::empty(),
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let mut blinded_path = BlindedPath {
- introduction_node_id: nodes[2],
+ introduction_node: IntroductionNode::NodeId(nodes[2]),
blinding_point: ln_test_utils::pubkey(42),
blinded_hops: Vec::with_capacity(num_blinded_hops),
};
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let mut invalid_blinded_path = BlindedPath {
- introduction_node_id: nodes[2],
+ introduction_node: IntroductionNode::NodeId(nodes[2]),
blinding_point: ln_test_utils::pubkey(42),
blinded_hops: vec![
BlindedHop { blinded_node_id: ln_test_utils::pubkey(43), encrypted_payload: vec![0; 43] },
};
let mut invalid_blinded_path_2 = invalid_blinded_path.clone();
- invalid_blinded_path_2.introduction_node_id = ln_test_utils::pubkey(45);
+ invalid_blinded_path_2.introduction_node = IntroductionNode::NodeId(ln_test_utils::pubkey(45));
let payment_params = PaymentParameters::blinded(vec![
(blinded_payinfo.clone(), invalid_blinded_path.clone()),
(blinded_payinfo.clone(), invalid_blinded_path_2)]);
_ => panic!("Expected error")
}
- invalid_blinded_path.introduction_node_id = our_id;
+ invalid_blinded_path.introduction_node = IntroductionNode::NodeId(our_id);
let payment_params = PaymentParameters::blinded(vec![(blinded_payinfo.clone(), invalid_blinded_path.clone())]);
let route_params = RouteParameters::from_payment_params_and_value(payment_params, 1001);
match get_route(&our_id, &route_params, &network_graph, None, Arc::clone(&logger), &scorer,
_ => panic!("Expected error")
}
- invalid_blinded_path.introduction_node_id = ln_test_utils::pubkey(46);
+ invalid_blinded_path.introduction_node = IntroductionNode::NodeId(ln_test_utils::pubkey(46));
invalid_blinded_path.blinded_hops.clear();
let payment_params = PaymentParameters::blinded(vec![(blinded_payinfo, invalid_blinded_path)]);
let route_params = RouteParameters::from_payment_params_and_value(payment_params, 1001);
let bolt12_features = channelmanager::provided_bolt12_invoice_features(&config);
let blinded_path_1 = BlindedPath {
- introduction_node_id: nodes[2],
+ introduction_node: IntroductionNode::NodeId(nodes[2]),
blinding_point: ln_test_utils::pubkey(42),
blinded_hops: vec![
BlindedHop { blinded_node_id: ln_test_utils::pubkey(42 as u8), encrypted_payload: Vec::new() },
get_channel_details(Some(1), nodes[1], InitFeatures::from_le_bytes(vec![0b11]), 10_000_000)];
let blinded_path = BlindedPath {
- introduction_node_id: nodes[1],
+ introduction_node: IntroductionNode::NodeId(nodes[1]),
blinding_point: ln_test_utils::pubkey(42),
blinded_hops: vec![
BlindedHop { blinded_node_id: ln_test_utils::pubkey(42 as u8), encrypted_payload: Vec::new() },
18446744073709551615)];
let blinded_path = BlindedPath {
- introduction_node_id: nodes[1],
+ introduction_node: IntroductionNode::NodeId(nodes[1]),
blinding_point: ln_test_utils::pubkey(42),
blinded_hops: vec![
BlindedHop { blinded_node_id: ln_test_utils::pubkey(42 as u8), encrypted_payload: Vec::new() },
let amt_msat = 21_7020_5185_1423_0019;
let blinded_path = BlindedPath {
- introduction_node_id: our_id,
+ introduction_node: IntroductionNode::NodeId(our_id),
blinding_point: ln_test_utils::pubkey(42),
blinded_hops: vec![
BlindedHop { blinded_node_id: ln_test_utils::pubkey(42 as u8), encrypted_payload: Vec::new() },
(blinded_payinfo.clone(), blinded_path.clone()),
(blinded_payinfo.clone(), blinded_path.clone()),
];
- blinded_hints[1].1.introduction_node_id = nodes[6];
+ blinded_hints[1].1.introduction_node = IntroductionNode::NodeId(nodes[6]);
let bolt12_features = channelmanager::provided_bolt12_invoice_features(&config);
let payment_params = PaymentParameters::blinded(blinded_hints.clone())
let amt_msat = 21_7020_5185_1423_0019;
let blinded_path = BlindedPath {
- introduction_node_id: our_id,
+ introduction_node: IntroductionNode::NodeId(our_id),
blinding_point: ln_test_utils::pubkey(42),
blinded_hops: vec![
BlindedHop { blinded_node_id: ln_test_utils::pubkey(42 as u8), encrypted_payload: Vec::new() },
blinded_hints[1].0.htlc_minimum_msat = 21_7020_5185_1423_0019;
blinded_hints[1].0.htlc_maximum_msat = 1844_6744_0737_0955_1615;
- blinded_hints[2].1.introduction_node_id = nodes[6];
+ blinded_hints[2].1.introduction_node = IntroductionNode::NodeId(nodes[6]);
let bolt12_features = channelmanager::provided_bolt12_invoice_features(&config);
let payment_params = PaymentParameters::blinded(blinded_hints.clone())
let htlc_min = 2_5165_8240;
let payment_params = if blinded_payee {
let blinded_path = BlindedPath {
- introduction_node_id: nodes[0],
+ introduction_node: IntroductionNode::NodeId(nodes[0]),
blinding_point: ln_test_utils::pubkey(42),
blinded_hops: vec![
BlindedHop { blinded_node_id: ln_test_utils::pubkey(42 as u8), encrypted_payload: Vec::new() },
let htlc_mins = [1_4392, 19_7401, 1027, 6_5535];
let payment_params = if blinded_payee {
let blinded_path = BlindedPath {
- introduction_node_id: nodes[0],
+ introduction_node: IntroductionNode::NodeId(nodes[0]),
blinding_point: ln_test_utils::pubkey(42),
blinded_hops: vec![
BlindedHop { blinded_node_id: ln_test_utils::pubkey(42 as u8), encrypted_payload: Vec::new() },
cltv_expiry_delta: 10,
features: BlindedHopFeatures::empty(),
}, BlindedPath {
- introduction_node_id: nodes[0],
+ introduction_node: IntroductionNode::NodeId(nodes[0]),
blinding_point: ln_test_utils::pubkey(42),
blinded_hops: vec![
BlindedHop { blinded_node_id: ln_test_utils::pubkey(42 as u8), encrypted_payload: Vec::new() },
let htlc_mins = [49_0000, 1125_0000];
let payment_params = {
let blinded_path = BlindedPath {
- introduction_node_id: nodes[0],
+ introduction_node: IntroductionNode::NodeId(nodes[0]),
blinding_point: ln_test_utils::pubkey(42),
blinded_hops: vec![
BlindedHop { blinded_node_id: ln_test_utils::pubkey(42 as u8), encrypted_payload: Vec::new() },