Skip to content

Commit b379500

Browse files
Lagrang3rustyrussell
authored andcommitted
askrene: fixed routing in high capacity channels
We may introduce high capacity channels in askrene to represent problems with multiple destinations (eg. multiple blinded paths) or to solve self-payments. The integer computation of the deliverable amount through these channels (I tested this with a channel with 21M bitcoin) would fail due to an integer overflow in the function `amount_msat_sub_fee`, 21M BTC = 21 x 10^17 msat, which overflows u64 integers when multiplied by 10^6. We have fixed `amount_msat_sub_fee` operations, so that it doesn't overflow. Changelog-Fixed: askrene: fixed routing in high capacity channels. Signed-off-by: Lagrang3 <[email protected]>
1 parent 8141795 commit b379500

File tree

2 files changed

+52
-2
lines changed

2 files changed

+52
-2
lines changed

common/amount.c

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,20 @@ bool amount_msat_mul(struct amount_msat *res, struct amount_msat msat, u64 mul)
554554
return true;
555555
}
556556

557+
static bool amount_msat_mul_div(struct amount_msat *res,
558+
struct amount_msat msat, u64 mul, u64 div)
559+
{
560+
/* We avoid overflow in the operation (a/b)*x noticing that we can
561+
* decompose x = q_x * b + r_x, where q_x = floor(x/b) and
562+
* r_x = x mod b, then: x*(a/b) = a*q_x + a*r_x/b, which does not
563+
* overflow if a*b does not. */
564+
if (mul_overflows_u64(mul, div))
565+
return false;
566+
u64 x = msat.millisatoshis;
567+
res->millisatoshis = mul * (x / div) + (mul * (x % div)) / div;
568+
return true;
569+
}
570+
557571
bool amount_msat_fee(struct amount_msat *fee,
558572
struct amount_msat amt,
559573
u32 fee_base_msat,
@@ -606,9 +620,9 @@ struct amount_msat amount_msat_sub_fee(struct amount_msat in,
606620
*/
607621
if (!amount_msat_sub(&out, in, amount_msat(fee_base_msat)))
608622
return AMOUNT_MSAT(0);
609-
if (!amount_msat_mul(&out, out, 1000000))
623+
if (!amount_msat_mul_div(&out, out, 1000000,
624+
1000000 + fee_proportional_millionths))
610625
return AMOUNT_MSAT(0);
611-
out = amount_msat_div(out, 1000000ULL + fee_proportional_millionths);
612626

613627
/* If we calc reverse, it must work! */
614628
assert(within_fee(in, out, fee_base_msat, fee_proportional_millionths));

tests/test_askrene.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1437,3 +1437,39 @@ def test_askrene_fake_channeld(node_factory, bitcoind):
14371437
'constrained')
14381438
else:
14391439
raise err
1440+
1441+
1442+
def test_simple_dummy_channel(node_factory):
1443+
"""Test if askrene can resolve a route with dummy channels, ie. channels
1444+
that we might set artificially to resolve blinded paths and self payments,
1445+
they have unlimited capacities and possibly zero fees."""
1446+
ALOT = "2100000000000000sat"
1447+
node1 = "020000000000000000000000000000000000000000000000000000000000000001"
1448+
node2 = "020000000000000000000000000000000000000000000000000000000000000002"
1449+
l1 = node_factory.get_node()
1450+
l1.rpc.askrene_create_layer("mylayer")
1451+
l1.rpc.askrene_create_channel(
1452+
layer="mylayer",
1453+
source=node1,
1454+
destination=node2,
1455+
short_channel_id="0x0x0",
1456+
capacity_msat=ALOT,
1457+
)
1458+
l1.rpc.askrene_update_channel(
1459+
layer="mylayer",
1460+
short_channel_id_dir="0x0x0/0",
1461+
enabled=True,
1462+
htlc_minimum_msat=0,
1463+
htlc_maximum_msat=ALOT,
1464+
fee_base_msat=0,
1465+
fee_proportional_millionths=0,
1466+
cltv_expiry_delta=5,
1467+
)
1468+
l1.rpc.getroutes(
1469+
source=node1,
1470+
destination=node2,
1471+
amount_msat=100,
1472+
maxfee_msat=5000,
1473+
final_cltv=5,
1474+
layers=["mylayer"],
1475+
)

0 commit comments

Comments
 (0)