Skip to content

Commit 0d07cce

Browse files
committed
Hit htlc_min in add entry + fix
1 parent aded129 commit 0d07cce

File tree

1 file changed

+13
-69
lines changed

1 file changed

+13
-69
lines changed

lightning/src/routing/router.rs

Lines changed: 13 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -509,21 +509,17 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
509509

510510
let value_contribution_msat = cmp::min(available_value_contribution_msat, $next_hops_value_contribution);
511511
// Includes paying fees for the use of the following channels.
512-
let amount_to_transfer_over_msat: u64 = match value_contribution_msat.checked_add($next_hops_fee_msat) {
512+
let mut amount_to_transfer_over_msat: u64 = match value_contribution_msat.checked_add($next_hops_fee_msat) {
513513
Some(result) => result,
514514
// Can't overflow due to how the values were computed right above.
515515
None => unreachable!(),
516516
};
517517

518-
// If HTLC minimum is larger than the amount we're going to transfer, we shouldn't
519-
// bother considering this channel.
520-
// Since we're choosing amount_to_transfer_over_msat as maximum possible, it can
521-
// be only reduced later (not increased), so this channel should just be skipped
522-
// as not sufficient.
523-
// TODO: Explore simply adding fee to hit htlc_minimum_msat
518+
// If HTLC minimum is larger than the amount we're going to transfer, we shouldn
519+
// transfer that value instead, thus overpaying fees.
524520
let effective_htlc_minimum_msat = cmp::max($directional_info.htlc_minimum_msat, $incl_fee_next_hops_htlc_minimum_msat);
525-
let over_path_minimum_msat = amount_to_transfer_over_msat >= effective_htlc_minimum_msat;
526-
if contributes_sufficient_value && over_path_minimum_msat {
521+
amount_to_transfer_over_msat = cmp::max(amount_to_transfer_over_msat, effective_htlc_minimum_msat);
522+
if contributes_sufficient_value {
527523
let hm_entry = dist.entry(&$src_node_id);
528524
let old_entry = hm_entry.or_insert_with(|| {
529525
// If there was previously no known way to access
@@ -602,34 +598,12 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
602598
},
603599
};
604600

605-
// Update the way of reaching $src_node_id with the given $chan_id (from $dest_node_id),
606-
// if this way is cheaper than the already known
601+
// Update the way of reaching $src_node_id with the given $chan_id (from
602+
// $dest_node_id), if this way is cheaper than the already known
607603
// (considering the cost to "reach" this channel from the route destination,
608-
// the cost of using this channel,
604+
// the cost of using this channel (with possible overpayment for htlc min)
609605
// and the cost of routing to the source node of this channel).
610-
// Also, consider that htlc_minimum_msat_difference, because we might end up
611-
// paying it. Consider the following exploit:
612-
// we use 2 paths to transfer 1.5 BTC. One of them is 0-fee normal 1 BTC path,
613-
// and for the other one we picked a 1sat-fee path with htlc_minimum_msat of
614-
// 1 BTC. Now, since the latter is more expensive, we gonna try to cut it
615-
// by 0.5 BTC, but then match htlc_minimum_msat by paying a fee of 0.5 BTC
616-
// to this channel.
617-
// TODO: this scoring could be smarter (e.g. 0.5*htlc_minimum_msat here).
618-
let mut old_cost = old_entry.total_fee_msat;
619-
if let Some(increased_old_cost) = old_cost.checked_add(old_entry.htlc_minimum_msat) {
620-
old_cost = increased_old_cost;
621-
} else {
622-
old_cost = u64::max_value();
623-
}
624-
625-
let mut new_cost = total_fee_msat;
626-
if let Some(increased_new_cost) = new_cost.checked_add($directional_info.htlc_minimum_msat) {
627-
new_cost = increased_new_cost;
628-
} else {
629-
new_cost = u64::max_value();
630-
}
631-
632-
if new_cost < old_cost {
606+
if total_fee_msat < old_entry.total_fee_msat {
633607
targets.push(new_graph_node);
634608
old_entry.next_hops_fee_msat = $next_hops_fee_msat;
635609
old_entry.hop_use_fee_msat = hop_use_fee_msat;
@@ -820,35 +794,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
820794
}
821795

822796
new_entry = match dist.remove(&ordered_hops.last().unwrap().route_hop.pubkey) {
823-
Some(payment_hop) => {
824-
if new_entry.hop_use_fee_msat + value_contribution_msat <
825-
ordered_hops.last_mut().unwrap().effective_htlc_minimum_msat {
826-
// This could happen if a mismatch happened in add_entry! when
827-
// there is a path fork: e.g. source-A-B-payee and source-A-C-payee.
828-
// The mismatch of htlc_min could happen if B is the cheapest path,
829-
// but while adding A-for-B, A can't hit htlc_minimum_msat.
830-
// Then, we add A-for-C because it does hit htlc_minimum msat.
831-
//
832-
// This block would trigger if after this we choose A-for-C
833-
// as a way to reach B, and we break from the loop because this
834-
// path would be invalid otherwise.
835-
//
836-
// The actual problem would happen when we propagate fees one hop
837-
// backward (see below), the A-for-C is not sufficient to hit
838-
// htlc_minimum_msat anymore.
839-
//
840-
// This check here attempts to check the compatibility:
841-
// (fine if A-for-C just matches following C or overpays for D).
842-
//
843-
// TODO. this is a safe approach. It indeed avoids invalid paths,
844-
// but it also could drop valid ones. Ideally, we should prevent
845-
// the latter too. Although dropping is rare, because in the example
846-
// above, C would be dropped from candidates, and then we just have
847-
// to implement hitting htlc_minimum_msat for B in add_entry.
848-
break 'paths_collection;
849-
}
850-
payment_hop
851-
}
797+
Some(payment_hop) => payment_hop,
852798
None => {
853799
// If we can't reach a given node, something went wrong
854800
// during path traverse.
@@ -1578,7 +1524,6 @@ mod tests {
15781524
});
15791525

15801526
// Check against amount_to_transfer_over_msat.
1581-
// We will be transferring 300_000_000 msat.
15821527
// Set minimal HTLC of 200_000_000 msat.
15831528
update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
15841529
chain_hash: genesis_block(Network::Testnet).header.block_hash(),
@@ -1608,10 +1553,9 @@ mod tests {
16081553
excess_data: Vec::new()
16091554
});
16101555

1611-
// Not possible to send 299_999_999: we first allocate 200_000_000 for the channel#12,
1612-
// and then the amount_to_transfer_over_msat over channel#2 is less than our limit.
1613-
if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2], None, &Vec::new(), 199_999_999, 42, Arc::clone(&logger)) {
1614-
assert_eq!(err, "Failed to find a path to the given destination");
1556+
// Not possible to send 299_999_999.
1557+
if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2], None, &Vec::new(), 299_999_999, 42, Arc::clone(&logger)) {
1558+
assert_eq!(err, "Failed to find a sufficient route to the given destination");
16151559
} else { panic!(); }
16161560

16171561
// Lift the restriction on the first hop.

0 commit comments

Comments
 (0)