Skip to content

Commit 62f8669

Browse files
committed
Add create_blinded_payment_paths to Router
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.
1 parent edb5892 commit 62f8669

File tree

4 files changed

+122
-6
lines changed

4 files changed

+122
-6
lines changed

fuzz/src/chanmon_consistency.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ use bitcoin::hashes::sha256d::Hash as Sha256dHash;
3131
use bitcoin::hash_types::{BlockHash, WPubkeyHash};
3232

3333
use lightning::blinded_path::BlindedPath;
34+
use lightning::blinded_path::payment::ReceiveTlvs;
3435
use lightning::chain;
3536
use lightning::chain::{BestBlock, ChannelMonitorUpdateStatus, chainmonitor, channelmonitor, Confirm, Watch};
3637
use lightning::chain::channelmonitor::{ChannelMonitor, MonitorEvent};
@@ -45,7 +46,7 @@ use lightning::ln::channel::FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE;
4546
use lightning::ln::msgs::{self, CommitmentUpdate, ChannelMessageHandler, DecodeError, UpdateAddHTLC, Init};
4647
use lightning::ln::script::ShutdownScript;
4748
use lightning::ln::functional_test_utils::*;
48-
use lightning::offers::invoice::UnsignedBolt12Invoice;
49+
use lightning::offers::invoice::{BlindedPayInfo, UnsignedBolt12Invoice};
4950
use lightning::offers::invoice_request::UnsignedInvoiceRequest;
5051
use lightning::onion_message::{Destination, MessageRouter, OnionMessagePath};
5152
use lightning::util::test_channel_signer::{TestChannelSigner, EnforcementState};
@@ -101,6 +102,15 @@ impl Router for FuzzRouter {
101102
action: msgs::ErrorAction::IgnoreError
102103
})
103104
}
105+
106+
fn create_blinded_payment_paths<
107+
ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification
108+
>(
109+
&self, _recipient: PublicKey, _first_hops: Vec<ChannelDetails>, _tlvs: ReceiveTlvs,
110+
_amount_msats: u64, _entropy_source: &ES, _secp_ctx: &Secp256k1<T>
111+
) -> Result<Vec<(BlindedPayInfo, BlindedPath)>, ()> {
112+
unreachable!()
113+
}
104114
}
105115

106116
impl MessageRouter for FuzzRouter {

fuzz/src/full_stack.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use bitcoin::hashes::sha256d::Hash as Sha256dHash;
2929
use bitcoin::hash_types::{Txid, BlockHash, WPubkeyHash};
3030

3131
use lightning::blinded_path::BlindedPath;
32+
use lightning::blinded_path::payment::ReceiveTlvs;
3233
use lightning::chain;
3334
use lightning::chain::{BestBlock, ChannelMonitorUpdateStatus, Confirm, Listen};
3435
use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator};
@@ -42,7 +43,7 @@ use lightning::ln::peer_handler::{MessageHandler,PeerManager,SocketDescriptor,Ig
4243
use lightning::ln::msgs::{self, DecodeError};
4344
use lightning::ln::script::ShutdownScript;
4445
use lightning::ln::functional_test_utils::*;
45-
use lightning::offers::invoice::UnsignedBolt12Invoice;
46+
use lightning::offers::invoice::{BlindedPayInfo, UnsignedBolt12Invoice};
4647
use lightning::offers::invoice_request::UnsignedInvoiceRequest;
4748
use lightning::onion_message::{Destination, MessageRouter, OnionMessagePath};
4849
use lightning::routing::gossip::{P2PGossipSync, NetworkGraph};
@@ -144,6 +145,15 @@ impl Router for FuzzRouter {
144145
action: msgs::ErrorAction::IgnoreError
145146
})
146147
}
148+
149+
fn create_blinded_payment_paths<
150+
ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification
151+
>(
152+
&self, _recipient: PublicKey, _first_hops: Vec<ChannelDetails>, _tlvs: ReceiveTlvs,
153+
_amount_msats: u64, _entropy_source: &ES, _secp_ctx: &Secp256k1<T>
154+
) -> Result<Vec<(BlindedPayInfo, BlindedPath)>, ()> {
155+
unreachable!()
156+
}
147157
}
148158

149159
impl MessageRouter for FuzzRouter {

lightning/src/routing/router.rs

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@ use bitcoin::hashes::Hash;
1414
use bitcoin::hashes::sha256::Hash as Sha256;
1515

1616
use crate::blinded_path::{BlindedHop, BlindedPath};
17+
use crate::blinded_path::payment::{ForwardNode, ForwardTlvs, PaymentConstraints, PaymentRelay, ReceiveTlvs};
1718
use crate::ln::PaymentHash;
1819
use crate::ln::channelmanager::{ChannelDetails, PaymentId};
19-
use crate::ln::features::{Bolt11InvoiceFeatures, Bolt12InvoiceFeatures, ChannelFeatures, NodeFeatures};
20+
use crate::ln::features::{BlindedHopFeatures, Bolt11InvoiceFeatures, Bolt12InvoiceFeatures, ChannelFeatures, NodeFeatures};
2021
use crate::ln::msgs::{DecodeError, ErrorAction, LightningError, MAX_VALUE_MSAT};
2122
use crate::offers::invoice::{BlindedPayInfo, Bolt12Invoice};
2223
use crate::onion_message::{DefaultMessageRouter, Destination, MessageRouter, OnionMessagePath};
@@ -82,6 +83,81 @@ impl<G: Deref<Target = NetworkGraph<L>> + Clone, L: Deref, S: Deref, SP: Sized,
8283
&random_seed_bytes
8384
)
8485
}
86+
87+
fn create_blinded_payment_paths<
88+
ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification
89+
>(
90+
&self, recipient: PublicKey, first_hops: Vec<ChannelDetails>, tlvs: ReceiveTlvs,
91+
amount_msats: u64, entropy_source: &ES, secp_ctx: &Secp256k1<T>
92+
) -> Result<Vec<(BlindedPayInfo, BlindedPath)>, ()> {
93+
// Limit the number of blinded paths that are computed.
94+
const MAX_PAYMENT_PATHS: usize = 3;
95+
96+
// Ensure peers have at least three channels so that it is more difficult to infer the
97+
// recipient's node_id.
98+
const MIN_PEER_CHANNELS: usize = 3;
99+
100+
let network_graph = self.network_graph.deref().read_only();
101+
let paths = first_hops.into_iter()
102+
.filter(|details| details.counterparty.features.supports_route_blinding())
103+
.filter(|details| amount_msats <= details.inbound_capacity_msat)
104+
.filter(|details| amount_msats >= details.inbound_htlc_minimum_msat.unwrap_or(0))
105+
.filter(|details| amount_msats <= details.inbound_htlc_maximum_msat.unwrap_or(0))
106+
.filter(|details| network_graph
107+
.node(&NodeId::from_pubkey(&details.counterparty.node_id))
108+
.map(|node_info| node_info.channels.len() >= MIN_PEER_CHANNELS)
109+
.unwrap_or(false)
110+
)
111+
.filter_map(|details| {
112+
let short_channel_id = match details.get_inbound_payment_scid() {
113+
Some(short_channel_id) => short_channel_id,
114+
None => return None,
115+
};
116+
let payment_relay: PaymentRelay = match details.counterparty.forwarding_info {
117+
Some(forwarding_info) => forwarding_info.into(),
118+
None => return None,
119+
};
120+
121+
// Avoid exposing esoteric CLTV expiry deltas
122+
let cltv_expiry_delta = match payment_relay.cltv_expiry_delta {
123+
0..=40 => 40u32,
124+
41..=80 => 80u32,
125+
81..=144 => 144u32,
126+
145..=216 => 216u32,
127+
_ => return None,
128+
};
129+
130+
let payment_constraints = PaymentConstraints {
131+
max_cltv_expiry: tlvs.payment_constraints.max_cltv_expiry + cltv_expiry_delta,
132+
htlc_minimum_msat: details.inbound_htlc_minimum_msat.unwrap_or(0),
133+
};
134+
Some(ForwardNode {
135+
tlvs: ForwardTlvs {
136+
short_channel_id,
137+
payment_relay,
138+
payment_constraints,
139+
features: BlindedHopFeatures::empty(),
140+
},
141+
node_id: details.counterparty.node_id,
142+
htlc_maximum_msat: details.inbound_htlc_maximum_msat.unwrap_or(0),
143+
})
144+
})
145+
.map(|forward_node| {
146+
BlindedPath::new_for_payment(
147+
&[forward_node], recipient, tlvs.clone(), u64::MAX, entropy_source, secp_ctx
148+
)
149+
})
150+
.take(MAX_PAYMENT_PATHS)
151+
.collect::<Result<Vec<_>, _>>();
152+
153+
match paths {
154+
Ok(paths) if !paths.is_empty() => Ok(paths),
155+
_ => {
156+
BlindedPath::one_hop_for_payment(recipient, tlvs, entropy_source, secp_ctx)
157+
.map(|path| vec![path])
158+
},
159+
}
160+
}
85161
}
86162

87163
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
@@ -129,6 +205,16 @@ pub trait Router: MessageRouter {
129205
) -> Result<Route, LightningError> {
130206
self.find_route(payer, route_params, first_hops, inflight_htlcs)
131207
}
208+
209+
/// Creates [`BlindedPath`]s for payment to the `recipient` node. The channels in `first_hops`
210+
/// are assumed to be with the `recipient`'s peers. The payment secret and any constraints are
211+
/// given in `tlvs`.
212+
fn create_blinded_payment_paths<
213+
ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification
214+
>(
215+
&self, recipient: PublicKey, first_hops: Vec<ChannelDetails>, tlvs: ReceiveTlvs,
216+
amount_msats: u64, entropy_source: &ES, secp_ctx: &Secp256k1<T>
217+
) -> Result<Vec<(BlindedPayInfo, BlindedPath)>, ()>;
132218
}
133219

134220
/// [`ScoreLookUp`] implementation that factors in in-flight HTLC liquidity.

lightning/src/util/test_utils.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
// licenses.
99

1010
use crate::blinded_path::BlindedPath;
11+
use crate::blinded_path::payment::ReceiveTlvs;
1112
use crate::chain;
1213
use crate::chain::WatchedOutput;
1314
use crate::chain::chaininterface;
@@ -23,13 +24,13 @@ use crate::sign;
2324
use crate::events;
2425
use crate::events::bump_transaction::{WalletSource, Utxo};
2526
use crate::ln::ChannelId;
26-
use crate::ln::channelmanager;
27+
use crate::ln::channelmanager::{ChannelDetails, self};
2728
use crate::ln::chan_utils::CommitmentTransaction;
2829
use crate::ln::features::{ChannelFeatures, InitFeatures, NodeFeatures};
2930
use crate::ln::{msgs, wire};
3031
use crate::ln::msgs::LightningError;
3132
use crate::ln::script::ShutdownScript;
32-
use crate::offers::invoice::UnsignedBolt12Invoice;
33+
use crate::offers::invoice::{BlindedPayInfo, UnsignedBolt12Invoice};
3334
use crate::offers::invoice_request::UnsignedInvoiceRequest;
3435
use crate::onion_message::{Destination, MessageRouter, OnionMessagePath};
3536
use crate::routing::gossip::{EffectiveCapacity, NetworkGraph, NodeId, RoutingFees};
@@ -121,7 +122,7 @@ impl<'a> TestRouter<'a> {
121122

122123
impl<'a> Router for TestRouter<'a> {
123124
fn find_route(
124-
&self, payer: &PublicKey, params: &RouteParameters, first_hops: Option<&[&channelmanager::ChannelDetails]>,
125+
&self, payer: &PublicKey, params: &RouteParameters, first_hops: Option<&[&ChannelDetails]>,
125126
inflight_htlcs: InFlightHtlcs
126127
) -> Result<Route, msgs::LightningError> {
127128
if let Some((find_route_query, find_route_res)) = self.next_routes.lock().unwrap().pop_front() {
@@ -191,6 +192,15 @@ impl<'a> Router for TestRouter<'a> {
191192
&[42; 32]
192193
)
193194
}
195+
196+
fn create_blinded_payment_paths<
197+
ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification
198+
>(
199+
&self, _recipient: PublicKey, _first_hops: Vec<ChannelDetails>, _tlvs: ReceiveTlvs,
200+
_amount_msats: u64, _entropy_source: &ES, _secp_ctx: &Secp256k1<T>
201+
) -> Result<Vec<(BlindedPayInfo, BlindedPath)>, ()> {
202+
unreachable!()
203+
}
194204
}
195205

196206
impl<'a> MessageRouter for TestRouter<'a> {

0 commit comments

Comments
 (0)