Skip to content

Commit 95d7995

Browse files
committed
WIP: three-hop blinded payment paths
1 parent e793eb7 commit 95d7995

File tree

1 file changed

+62
-10
lines changed

1 file changed

+62
-10
lines changed

lightning/src/routing/router.rs

Lines changed: 62 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ impl<G: Deref<Target = NetworkGraph<L>> + Clone, L: Deref, S: Deref, SP: Sized,
9090
&self, recipient: PublicKey, first_hops: Vec<ChannelDetails>, tlvs: ReceiveTlvs,
9191
amount_msats: u64, entropy_source: &ES, secp_ctx: &Secp256k1<T>
9292
) -> Result<Vec<(BlindedPayInfo, BlindedPath)>, ()> {
93+
let recipient_node_id = NodeId::from_pubkey(&recipient);
94+
9395
// Limit the number of blinded paths that are computed.
9496
const MAX_PAYMENT_PATHS: usize = 3;
9597

@@ -103,12 +105,15 @@ impl<G: Deref<Target = NetworkGraph<L>> + Clone, L: Deref, S: Deref, SP: Sized,
103105
.filter(|details| amount_msats <= details.inbound_capacity_msat)
104106
.filter(|details| amount_msats >= details.inbound_htlc_minimum_msat.unwrap_or(0))
105107
.filter(|details| amount_msats <= details.inbound_htlc_maximum_msat.unwrap_or(u64::MAX))
106-
.filter(|details| network_graph
108+
// Limit to counterparties with announced channels
109+
.filter_map(|details|
110+
network_graph
107111
.node(&NodeId::from_pubkey(&details.counterparty.node_id))
108-
.map(|node_info| node_info.channels.len() >= MIN_PEER_CHANNELS)
109-
.unwrap_or(false)
112+
.map(|info| &info.channels[..])
113+
.and_then(|channels| (channels.len() >= MIN_PEER_CHANNELS).then(|| channels))
114+
.map(|channels| (details, channels))
110115
)
111-
.filter_map(|details| {
116+
.filter_map(|(details, counterparty_channels)| {
112117
let short_channel_id = match details.get_inbound_payment_scid() {
113118
Some(short_channel_id) => short_channel_id,
114119
None => return None,
@@ -126,7 +131,7 @@ impl<G: Deref<Target = NetworkGraph<L>> + Clone, L: Deref, S: Deref, SP: Sized,
126131
max_cltv_expiry: tlvs.payment_constraints.max_cltv_expiry + cltv_expiry_delta,
127132
htlc_minimum_msat: details.inbound_htlc_minimum_msat.unwrap_or(0),
128133
};
129-
Some(ForwardNode {
134+
let forward_node = ForwardNode {
130135
tlvs: ForwardTlvs {
131136
short_channel_id,
132137
payment_relay,
@@ -135,12 +140,59 @@ impl<G: Deref<Target = NetworkGraph<L>> + Clone, L: Deref, S: Deref, SP: Sized,
135140
},
136141
node_id: details.counterparty.node_id,
137142
htlc_maximum_msat: details.inbound_htlc_maximum_msat.unwrap_or(u64::MAX),
138-
})
143+
};
144+
Some((forward_node, counterparty_channels))
139145
})
140-
.map(|forward_node| {
141-
BlindedPath::new_for_payment(
142-
&[forward_node], recipient, tlvs.clone(), u64::MAX, entropy_source, secp_ctx
143-
)
146+
// Pair counterparties with their other channels
147+
.flat_map(|(forward_node, counterparty_channels)|
148+
counterparty_channels
149+
.iter()
150+
.filter_map(|scid| network_graph.channels().get_key_value(scid))
151+
.filter_map(move |(scid, info)| info
152+
.as_directed_to(&NodeId::from_pubkey(&forward_node.node_id))
153+
.map(|(info, source)| (source, *scid, info))
154+
)
155+
.filter(|(source, _, _)| **source != recipient_node_id)
156+
.filter(|(source, _, _)| network_graph
157+
.node(source)
158+
.and_then(|info| info.announcement_info.as_ref())
159+
.map(|info| info.features.supports_route_blinding())
160+
.unwrap_or(false)
161+
)
162+
.filter(|(_, _, info)| amount_msats >= info.direction().htlc_minimum_msat)
163+
.filter(|(_, _, info)| amount_msats <= info.direction().htlc_maximum_msat)
164+
.map(move |(source, scid, info)| (source, scid, info, forward_node.clone()))
165+
)
166+
// Construct blinded paths where the counterparty's counterparty is the introduction
167+
// node:
168+
//
169+
// source --- info ---> counterparty --- counterparty_forward_node ---> recipient
170+
.filter_map(|(introduction_node_id, scid, info, counterparty_forward_node)| {
171+
let htlc_minimum_msat = info.direction().htlc_minimum_msat;
172+
let htlc_maximum_msat = info.direction().htlc_maximum_msat;
173+
let payment_relay: PaymentRelay = match info.try_into() {
174+
Ok(payment_relay) => payment_relay,
175+
Err(()) => return None,
176+
};
177+
let payment_constraints = PaymentConstraints {
178+
max_cltv_expiry: payment_relay.cltv_expiry_delta as u32
179+
+ counterparty_forward_node.tlvs.payment_constraints.max_cltv_expiry,
180+
htlc_minimum_msat,
181+
};
182+
let introduction_forward_node = ForwardNode {
183+
tlvs: ForwardTlvs {
184+
short_channel_id: scid,
185+
payment_relay,
186+
payment_constraints,
187+
features: BlindedHopFeatures::empty(),
188+
},
189+
node_id: introduction_node_id.as_pubkey().unwrap(),
190+
htlc_maximum_msat,
191+
};
192+
Some(BlindedPath::new_for_payment(
193+
&[introduction_forward_node, counterparty_forward_node], recipient,
194+
tlvs.clone(), u64::MAX, entropy_source, secp_ctx
195+
))
144196
})
145197
.take(MAX_PAYMENT_PATHS)
146198
.collect::<Result<Vec<_>, _>>();

0 commit comments

Comments
 (0)