Skip to content

Commit

Permalink
Add create_blinded_payment_paths to Router
Browse files Browse the repository at this point in the history
The Router trait is used to find a Route for paying a node. Expand the
interface with a create_blinded_payment paths method for creating such
paths to a recipient node.

Provide an implementation for DefaultRouter that creates two-hop
blinded paths where the recipient's peers serve as the introduction
nodes.
  • Loading branch information
jkczyz committed Dec 15, 2023
1 parent edb5892 commit 62f8669
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 6 deletions.
12 changes: 11 additions & 1 deletion fuzz/src/chanmon_consistency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use bitcoin::hashes::sha256d::Hash as Sha256dHash;
use bitcoin::hash_types::{BlockHash, WPubkeyHash};

use lightning::blinded_path::BlindedPath;
use lightning::blinded_path::payment::ReceiveTlvs;
use lightning::chain;
use lightning::chain::{BestBlock, ChannelMonitorUpdateStatus, chainmonitor, channelmonitor, Confirm, Watch};
use lightning::chain::channelmonitor::{ChannelMonitor, MonitorEvent};
Expand All @@ -45,7 +46,7 @@ use lightning::ln::channel::FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE;
use lightning::ln::msgs::{self, CommitmentUpdate, ChannelMessageHandler, DecodeError, UpdateAddHTLC, Init};
use lightning::ln::script::ShutdownScript;
use lightning::ln::functional_test_utils::*;
use lightning::offers::invoice::UnsignedBolt12Invoice;
use lightning::offers::invoice::{BlindedPayInfo, UnsignedBolt12Invoice};
use lightning::offers::invoice_request::UnsignedInvoiceRequest;
use lightning::onion_message::{Destination, MessageRouter, OnionMessagePath};
use lightning::util::test_channel_signer::{TestChannelSigner, EnforcementState};
Expand Down Expand Up @@ -101,6 +102,15 @@ impl Router for FuzzRouter {
action: msgs::ErrorAction::IgnoreError
})
}

fn create_blinded_payment_paths<
ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification
>(
&self, _recipient: PublicKey, _first_hops: Vec<ChannelDetails>, _tlvs: ReceiveTlvs,
_amount_msats: u64, _entropy_source: &ES, _secp_ctx: &Secp256k1<T>
) -> Result<Vec<(BlindedPayInfo, BlindedPath)>, ()> {
unreachable!()
}
}

impl MessageRouter for FuzzRouter {
Expand Down
12 changes: 11 additions & 1 deletion fuzz/src/full_stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ use bitcoin::hashes::sha256d::Hash as Sha256dHash;
use bitcoin::hash_types::{Txid, BlockHash, WPubkeyHash};

use lightning::blinded_path::BlindedPath;
use lightning::blinded_path::payment::ReceiveTlvs;
use lightning::chain;
use lightning::chain::{BestBlock, ChannelMonitorUpdateStatus, Confirm, Listen};
use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator};
Expand All @@ -42,7 +43,7 @@ use lightning::ln::peer_handler::{MessageHandler,PeerManager,SocketDescriptor,Ig
use lightning::ln::msgs::{self, DecodeError};
use lightning::ln::script::ShutdownScript;
use lightning::ln::functional_test_utils::*;
use lightning::offers::invoice::UnsignedBolt12Invoice;
use lightning::offers::invoice::{BlindedPayInfo, UnsignedBolt12Invoice};
use lightning::offers::invoice_request::UnsignedInvoiceRequest;
use lightning::onion_message::{Destination, MessageRouter, OnionMessagePath};
use lightning::routing::gossip::{P2PGossipSync, NetworkGraph};
Expand Down Expand Up @@ -144,6 +145,15 @@ impl Router for FuzzRouter {
action: msgs::ErrorAction::IgnoreError
})
}

fn create_blinded_payment_paths<
ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification
>(
&self, _recipient: PublicKey, _first_hops: Vec<ChannelDetails>, _tlvs: ReceiveTlvs,
_amount_msats: u64, _entropy_source: &ES, _secp_ctx: &Secp256k1<T>
) -> Result<Vec<(BlindedPayInfo, BlindedPath)>, ()> {
unreachable!()
}
}

impl MessageRouter for FuzzRouter {
Expand Down
88 changes: 87 additions & 1 deletion lightning/src/routing/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ use bitcoin::hashes::Hash;
use bitcoin::hashes::sha256::Hash as Sha256;

use crate::blinded_path::{BlindedHop, BlindedPath};
use crate::blinded_path::payment::{ForwardNode, ForwardTlvs, PaymentConstraints, PaymentRelay, ReceiveTlvs};
use crate::ln::PaymentHash;
use crate::ln::channelmanager::{ChannelDetails, PaymentId};
use crate::ln::features::{Bolt11InvoiceFeatures, Bolt12InvoiceFeatures, ChannelFeatures, NodeFeatures};
use crate::ln::features::{BlindedHopFeatures, Bolt11InvoiceFeatures, Bolt12InvoiceFeatures, ChannelFeatures, NodeFeatures};
use crate::ln::msgs::{DecodeError, ErrorAction, LightningError, MAX_VALUE_MSAT};
use crate::offers::invoice::{BlindedPayInfo, Bolt12Invoice};
use crate::onion_message::{DefaultMessageRouter, Destination, MessageRouter, OnionMessagePath};
Expand Down Expand Up @@ -82,6 +83,81 @@ impl<G: Deref<Target = NetworkGraph<L>> + Clone, L: Deref, S: Deref, SP: Sized,
&random_seed_bytes
)
}

fn create_blinded_payment_paths<
ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification
>(
&self, recipient: PublicKey, first_hops: Vec<ChannelDetails>, tlvs: ReceiveTlvs,
amount_msats: u64, entropy_source: &ES, secp_ctx: &Secp256k1<T>
) -> Result<Vec<(BlindedPayInfo, BlindedPath)>, ()> {
// Limit the number of blinded paths that are computed.
const MAX_PAYMENT_PATHS: usize = 3;

// Ensure peers have at least three channels so that it is more difficult to infer the
// recipient's node_id.
const MIN_PEER_CHANNELS: usize = 3;

let network_graph = self.network_graph.deref().read_only();
let paths = first_hops.into_iter()
.filter(|details| details.counterparty.features.supports_route_blinding())
.filter(|details| amount_msats <= details.inbound_capacity_msat)
.filter(|details| amount_msats >= details.inbound_htlc_minimum_msat.unwrap_or(0))
.filter(|details| amount_msats <= details.inbound_htlc_maximum_msat.unwrap_or(0))
.filter(|details| network_graph
.node(&NodeId::from_pubkey(&details.counterparty.node_id))
.map(|node_info| node_info.channels.len() >= MIN_PEER_CHANNELS)
.unwrap_or(false)
)
.filter_map(|details| {
let short_channel_id = match details.get_inbound_payment_scid() {
Some(short_channel_id) => short_channel_id,
None => return None,
};
let payment_relay: PaymentRelay = match details.counterparty.forwarding_info {
Some(forwarding_info) => forwarding_info.into(),
None => return None,
};

// Avoid exposing esoteric CLTV expiry deltas
let cltv_expiry_delta = match payment_relay.cltv_expiry_delta {
0..=40 => 40u32,
41..=80 => 80u32,
81..=144 => 144u32,
145..=216 => 216u32,
_ => return None,
};

let payment_constraints = PaymentConstraints {
max_cltv_expiry: tlvs.payment_constraints.max_cltv_expiry + cltv_expiry_delta,
htlc_minimum_msat: details.inbound_htlc_minimum_msat.unwrap_or(0),
};
Some(ForwardNode {
tlvs: ForwardTlvs {
short_channel_id,
payment_relay,
payment_constraints,
features: BlindedHopFeatures::empty(),
},
node_id: details.counterparty.node_id,
htlc_maximum_msat: details.inbound_htlc_maximum_msat.unwrap_or(0),
})
})
.map(|forward_node| {
BlindedPath::new_for_payment(
&[forward_node], recipient, tlvs.clone(), u64::MAX, entropy_source, secp_ctx
)
})
.take(MAX_PAYMENT_PATHS)
.collect::<Result<Vec<_>, _>>();

match paths {
Ok(paths) if !paths.is_empty() => Ok(paths),
_ => {
BlindedPath::one_hop_for_payment(recipient, tlvs, entropy_source, secp_ctx)
.map(|path| vec![path])
},
}
}
}

impl< G: Deref<Target = NetworkGraph<L>> + Clone, L: Deref, S: Deref, SP: Sized, Sc: ScoreLookUp<ScoreParams = SP>> MessageRouter for DefaultRouter<G, L, S, SP, Sc> where
Expand Down Expand Up @@ -129,6 +205,16 @@ pub trait Router: MessageRouter {
) -> Result<Route, LightningError> {
self.find_route(payer, route_params, first_hops, inflight_htlcs)
}

/// Creates [`BlindedPath`]s for payment to the `recipient` node. The channels in `first_hops`
/// are assumed to be with the `recipient`'s peers. The payment secret and any constraints are
/// given in `tlvs`.
fn create_blinded_payment_paths<
ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification
>(
&self, recipient: PublicKey, first_hops: Vec<ChannelDetails>, tlvs: ReceiveTlvs,
amount_msats: u64, entropy_source: &ES, secp_ctx: &Secp256k1<T>
) -> Result<Vec<(BlindedPayInfo, BlindedPath)>, ()>;
}

/// [`ScoreLookUp`] implementation that factors in in-flight HTLC liquidity.
Expand Down
16 changes: 13 additions & 3 deletions lightning/src/util/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
// licenses.

use crate::blinded_path::BlindedPath;
use crate::blinded_path::payment::ReceiveTlvs;
use crate::chain;
use crate::chain::WatchedOutput;
use crate::chain::chaininterface;
Expand All @@ -23,13 +24,13 @@ use crate::sign;
use crate::events;
use crate::events::bump_transaction::{WalletSource, Utxo};
use crate::ln::ChannelId;
use crate::ln::channelmanager;
use crate::ln::channelmanager::{ChannelDetails, self};
use crate::ln::chan_utils::CommitmentTransaction;
use crate::ln::features::{ChannelFeatures, InitFeatures, NodeFeatures};
use crate::ln::{msgs, wire};
use crate::ln::msgs::LightningError;
use crate::ln::script::ShutdownScript;
use crate::offers::invoice::UnsignedBolt12Invoice;
use crate::offers::invoice::{BlindedPayInfo, UnsignedBolt12Invoice};
use crate::offers::invoice_request::UnsignedInvoiceRequest;
use crate::onion_message::{Destination, MessageRouter, OnionMessagePath};
use crate::routing::gossip::{EffectiveCapacity, NetworkGraph, NodeId, RoutingFees};
Expand Down Expand Up @@ -121,7 +122,7 @@ impl<'a> TestRouter<'a> {

impl<'a> Router for TestRouter<'a> {
fn find_route(
&self, payer: &PublicKey, params: &RouteParameters, first_hops: Option<&[&channelmanager::ChannelDetails]>,
&self, payer: &PublicKey, params: &RouteParameters, first_hops: Option<&[&ChannelDetails]>,
inflight_htlcs: InFlightHtlcs
) -> Result<Route, msgs::LightningError> {
if let Some((find_route_query, find_route_res)) = self.next_routes.lock().unwrap().pop_front() {
Expand Down Expand Up @@ -191,6 +192,15 @@ impl<'a> Router for TestRouter<'a> {
&[42; 32]
)
}

fn create_blinded_payment_paths<
ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification
>(
&self, _recipient: PublicKey, _first_hops: Vec<ChannelDetails>, _tlvs: ReceiveTlvs,
_amount_msats: u64, _entropy_source: &ES, _secp_ctx: &Secp256k1<T>
) -> Result<Vec<(BlindedPayInfo, BlindedPath)>, ()> {
unreachable!()
}
}

impl<'a> MessageRouter for TestRouter<'a> {
Expand Down

0 comments on commit 62f8669

Please sign in to comment.