Skip to content

Commit 6353f21

Browse files
committed
WIP: three-hop blinded payment paths
1 parent 585a36c commit 6353f21

File tree

1 file changed

+89
-24
lines changed

1 file changed

+89
-24
lines changed

lightning/src/routing/router.rs

Lines changed: 89 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ impl<G: Deref<Target = NetworkGraph<L>> + Clone, L: Deref, ES: Deref, S: Deref,
9292
&self, recipient: PublicKey, first_hops: Vec<ChannelDetails>, tlvs: ReceiveTlvs,
9393
amount_msats: u64, secp_ctx: &Secp256k1<T>
9494
) -> Result<Vec<(BlindedPayInfo, BlindedPath)>, ()> {
95+
let entropy_source = &*self.entropy_source;
96+
let recipient_node_id = NodeId::from_pubkey(&recipient);
97+
9598
// Limit the number of blinded paths that are computed.
9699
const MAX_PAYMENT_PATHS: usize = 3;
97100

@@ -100,17 +103,20 @@ impl<G: Deref<Target = NetworkGraph<L>> + Clone, L: Deref, ES: Deref, S: Deref,
100103
const MIN_PEER_CHANNELS: usize = 3;
101104

102105
let network_graph = self.network_graph.deref().read_only();
103-
let paths = first_hops.into_iter()
106+
let counterparty_channels = first_hops.into_iter()
104107
.filter(|details| details.counterparty.features.supports_route_blinding())
105108
.filter(|details| amount_msats <= details.inbound_capacity_msat)
106109
.filter(|details| amount_msats >= details.inbound_htlc_minimum_msat.unwrap_or(0))
107110
.filter(|details| amount_msats <= details.inbound_htlc_maximum_msat.unwrap_or(u64::MAX))
108-
.filter(|details| network_graph
111+
// Limit to counterparties with announced channels
112+
.filter_map(|details|
113+
network_graph
109114
.node(&NodeId::from_pubkey(&details.counterparty.node_id))
110-
.map(|node_info| node_info.channels.len() >= MIN_PEER_CHANNELS)
111-
.unwrap_or(false)
115+
.map(|info| &info.channels[..])
116+
.and_then(|channels| (channels.len() >= MIN_PEER_CHANNELS).then(|| channels))
117+
.map(|channels| (details, channels))
112118
)
113-
.filter_map(|details| {
119+
.filter_map(|(details, counterparty_channels)| {
114120
let short_channel_id = match details.get_inbound_payment_scid() {
115121
Some(short_channel_id) => short_channel_id,
116122
None => return None,
@@ -128,7 +134,7 @@ impl<G: Deref<Target = NetworkGraph<L>> + Clone, L: Deref, ES: Deref, S: Deref,
128134
max_cltv_expiry: tlvs.payment_constraints.max_cltv_expiry + cltv_expiry_delta,
129135
htlc_minimum_msat: details.inbound_htlc_minimum_msat.unwrap_or(0),
130136
};
131-
Some(payment::ForwardNode {
137+
let forward_node = payment::ForwardNode {
132138
tlvs: ForwardTlvs {
133139
short_channel_id,
134140
payment_relay,
@@ -137,29 +143,88 @@ impl<G: Deref<Target = NetworkGraph<L>> + Clone, L: Deref, ES: Deref, S: Deref,
137143
},
138144
node_id: details.counterparty.node_id,
139145
htlc_maximum_msat: details.inbound_htlc_maximum_msat.unwrap_or(u64::MAX),
140-
})
146+
};
147+
Some((forward_node, counterparty_channels))
148+
});
149+
150+
let three_hop_paths = counterparty_channels.clone()
151+
// Pair counterparties with their other channels
152+
.flat_map(|(forward_node, counterparty_channels)|
153+
counterparty_channels
154+
.iter()
155+
.filter_map(|scid| network_graph.channels().get_key_value(scid))
156+
.filter_map(move |(scid, info)| info
157+
.as_directed_to(&NodeId::from_pubkey(&forward_node.node_id))
158+
.map(|(info, source)| (source, *scid, info))
159+
)
160+
.filter(|(source, _, _)| **source != recipient_node_id)
161+
.filter(|(source, _, _)| network_graph
162+
.node(source)
163+
.and_then(|info| info.announcement_info.as_ref())
164+
.map(|info| info.features.supports_route_blinding())
165+
.unwrap_or(false)
166+
)
167+
.filter(|(_, _, info)| amount_msats >= info.direction().htlc_minimum_msat)
168+
.filter(|(_, _, info)| amount_msats <= info.direction().htlc_maximum_msat)
169+
.map(move |(source, scid, info)| (source, scid, info, forward_node.clone()))
170+
)
171+
// Construct blinded paths where the counterparty's counterparty is the introduction
172+
// node:
173+
//
174+
// source --- info ---> counterparty --- counterparty_forward_node ---> recipient
175+
.filter_map(|(introduction_node_id, scid, info, counterparty_forward_node)| {
176+
let htlc_minimum_msat = info.direction().htlc_minimum_msat;
177+
let htlc_maximum_msat = info.direction().htlc_maximum_msat;
178+
let payment_relay: PaymentRelay = match info.try_into() {
179+
Ok(payment_relay) => payment_relay,
180+
Err(()) => return None,
181+
};
182+
let payment_constraints = PaymentConstraints {
183+
max_cltv_expiry: payment_relay.cltv_expiry_delta as u32
184+
+ counterparty_forward_node.tlvs.payment_constraints.max_cltv_expiry,
185+
htlc_minimum_msat,
186+
};
187+
let introduction_forward_node = payment::ForwardNode {
188+
tlvs: ForwardTlvs {
189+
short_channel_id: scid,
190+
payment_relay,
191+
payment_constraints,
192+
features: BlindedHopFeatures::empty(),
193+
},
194+
node_id: introduction_node_id.as_pubkey().unwrap(),
195+
htlc_maximum_msat,
196+
};
197+
Some(BlindedPath::new_for_payment(
198+
&[introduction_forward_node, counterparty_forward_node], recipient,
199+
tlvs.clone(), u64::MAX, MIN_FINAL_CLTV_EXPIRY_DELTA, entropy_source, secp_ctx
200+
))
141201
})
142-
.map(|forward_node| {
202+
.take(MAX_PAYMENT_PATHS);
203+
204+
let two_hop_paths = counterparty_channels
205+
.map(|(forward_node, _)| {
143206
BlindedPath::new_for_payment(
144207
&[forward_node], recipient, tlvs.clone(), u64::MAX, MIN_FINAL_CLTV_EXPIRY_DELTA,
145-
&*self.entropy_source, secp_ctx
208+
entropy_source, secp_ctx
146209
)
147210
})
148-
.take(MAX_PAYMENT_PATHS)
149-
.collect::<Result<Vec<_>, _>>();
150-
151-
match paths {
152-
Ok(paths) if !paths.is_empty() => Ok(paths),
153-
_ => {
154-
if network_graph.nodes().contains_key(&NodeId::from_pubkey(&recipient)) {
155-
BlindedPath::one_hop_for_payment(
156-
recipient, tlvs, MIN_FINAL_CLTV_EXPIRY_DELTA, &*self.entropy_source, secp_ctx
157-
).map(|path| vec![path])
158-
} else {
159-
Err(())
160-
}
161-
},
162-
}
211+
.take(MAX_PAYMENT_PATHS);
212+
213+
three_hop_paths
214+
.collect::<Result<Vec<_>, _>>().ok()
215+
.and_then(|paths| (!paths.is_empty()).then(|| paths))
216+
.or_else(|| two_hop_paths.collect::<Result<Vec<_>, _>>().ok())
217+
.and_then(|paths| (!paths.is_empty()).then(|| paths))
218+
.or_else(|| network_graph
219+
.node(&NodeId::from_pubkey(&recipient)).ok_or(())
220+
.and_then(|_| BlindedPath::one_hop_for_payment(
221+
recipient, tlvs, MIN_FINAL_CLTV_EXPIRY_DELTA, entropy_source, secp_ctx
222+
)
223+
)
224+
.map(|path| vec![path])
225+
.ok()
226+
)
227+
.ok_or(())
163228
}
164229
}
165230

0 commit comments

Comments
 (0)