Skip to content

Commit cdf574a

Browse files
committed
Filter route hints for phantom invoices
Filter the route hints in `create_phantom_invoice` based on the following criteria: * Only one channel per real participating node in the phantom invoice * Always select the channel with the highest inbound capacity * Filter out channels with a lower inbound capacity than the invoice amt
1 parent ef2a657 commit cdf574a

File tree

1 file changed

+74
-24
lines changed

1 file changed

+74
-24
lines changed

lightning-invoice/src/utils.rs

Lines changed: 74 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,17 @@ pub fn create_phantom_invoice<Signer: Sign, K: Deref>(
6262
if let Some(amt) = amt_msat {
6363
invoice = invoice.amount_milli_satoshis(amt);
6464
}
65+
let mut route_hints: HashMap<PublicKey, (u64, RouteHint)> = HashMap::new();
66+
let min_capacity_required = match amt_msat {
67+
Some(amt) => amt,
68+
None => 0,
69+
};
6570

6671
for hint in phantom_route_hints {
6772
for channel in &hint.channels {
73+
// Filter the channels to ensure that the `route_hints` only include the highest
74+
// inbound capacity channel per real node in the phantom invoice, and that channels
75+
// with a lower inbound capacity than the invoice amount aren't included in the hints.
6876
let short_channel_id = match channel.short_channel_id {
6977
Some(id) => id,
7078
None => continue,
@@ -73,33 +81,75 @@ pub fn create_phantom_invoice<Signer: Sign, K: Deref>(
7381
Some(info) => info.clone(),
7482
None => continue,
7583
};
76-
invoice = invoice.private_route(RouteHint(vec![
77-
RouteHintHop {
78-
src_node_id: channel.counterparty.node_id,
79-
short_channel_id,
80-
fees: RoutingFees {
81-
base_msat: forwarding_info.fee_base_msat,
82-
proportional_millionths: forwarding_info.fee_proportional_millionths,
83-
},
84-
cltv_expiry_delta: forwarding_info.cltv_expiry_delta,
85-
htlc_minimum_msat: None,
86-
htlc_maximum_msat: None,
87-
},
88-
RouteHintHop {
89-
src_node_id: hint.real_node_pubkey,
90-
short_channel_id: hint.phantom_scid,
91-
fees: RoutingFees {
92-
base_msat: 0,
93-
proportional_millionths: 0,
94-
},
95-
cltv_expiry_delta: MIN_CLTV_EXPIRY_DELTA,
96-
htlc_minimum_msat: None,
97-
htlc_maximum_msat: None,
98-
}])
99-
);
84+
if channel.inbound_capacity_msat < min_capacity_required {
85+
continue;
86+
};
87+
match route_hints.entry(hint.real_node_pubkey) {
88+
hash_map::Entry::Occupied(mut entry) => {
89+
let current_max_capacity = entry.get().0;
90+
if channel.inbound_capacity_msat < current_max_capacity {
91+
continue;
92+
}
93+
entry.insert((
94+
channel.inbound_capacity_msat,
95+
RouteHint(vec![
96+
RouteHintHop {
97+
src_node_id: channel.counterparty.node_id,
98+
short_channel_id,
99+
fees: RoutingFees {
100+
base_msat: forwarding_info.fee_base_msat,
101+
proportional_millionths: forwarding_info.fee_proportional_millionths,
102+
},
103+
cltv_expiry_delta: forwarding_info.cltv_expiry_delta,
104+
htlc_minimum_msat: None,
105+
htlc_maximum_msat: None,
106+
},
107+
RouteHintHop {
108+
src_node_id: hint.real_node_pubkey,
109+
short_channel_id: hint.phantom_scid,
110+
fees: RoutingFees {
111+
base_msat: 0,
112+
proportional_millionths: 0,
113+
},
114+
cltv_expiry_delta: MIN_CLTV_EXPIRY_DELTA,
115+
htlc_minimum_msat: None,
116+
htlc_maximum_msat: None,
117+
}])));
118+
}
119+
hash_map::Entry::Vacant(entry) => {
120+
entry.insert((
121+
channel.inbound_capacity_msat,
122+
RouteHint(vec![
123+
RouteHintHop {
124+
src_node_id: channel.counterparty.node_id,
125+
short_channel_id,
126+
fees: RoutingFees {
127+
base_msat: forwarding_info.fee_base_msat,
128+
proportional_millionths: forwarding_info.fee_proportional_millionths,
129+
},
130+
cltv_expiry_delta: forwarding_info.cltv_expiry_delta,
131+
htlc_minimum_msat: None,
132+
htlc_maximum_msat: None,
133+
},
134+
RouteHintHop {
135+
src_node_id: hint.real_node_pubkey,
136+
short_channel_id: hint.phantom_scid,
137+
fees: RoutingFees {
138+
base_msat: 0,
139+
proportional_millionths: 0,
140+
},
141+
cltv_expiry_delta: MIN_CLTV_EXPIRY_DELTA,
142+
htlc_minimum_msat: None,
143+
htlc_maximum_msat: None,
144+
}])));
145+
}
146+
}
100147
}
101148
}
102149

150+
for (_, (_, hint)) in &route_hints {
151+
invoice = invoice.private_route(hint.clone());
152+
}
103153
let raw_invoice = match invoice.build_raw() {
104154
Ok(inv) => inv,
105155
Err(e) => return Err(SignOrCreationError::CreationError(e))

0 commit comments

Comments
 (0)