Skip to content

Commit 1b5e87a

Browse files
committed
askrene: prune some arcs for the MCF computation
Constraint the number of flow units to 1000 and prune arcs that are provably not used in the MCF computation. Changelog-None. Signed-off-by: Lagrang3 <[email protected]>
1 parent 5778e06 commit 1b5e87a

File tree

3 files changed

+31
-21
lines changed

3 files changed

+31
-21
lines changed

plugins/askrene/mcf.c

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -360,9 +360,13 @@ static void linearize_channel(const struct pay_parameters *params,
360360
b = 1 + amount_msat_ratio_floor(maxcap, params->accuracy);
361361

362362
/* An extra bound on capacity, here we use it to reduce the flow such
363-
* that it does not exceed htlcmax. */
363+
* that it does not exceed htlcmax.
364+
* Also there is no need to keep track of more capacity than the payment
365+
* amount, this can help us prune some arcs. */
364366
u64 cap_on_capacity =
365-
amount_msat_ratio_floor(gossmap_chan_htlc_max(c, dir), params->accuracy);
367+
MIN(amount_msat_ratio_floor(gossmap_chan_htlc_max(c, dir),
368+
params->accuracy),
369+
amount_msat_ratio_ceil(params->amount, params->accuracy));
366370

367371
set_capacity(&capacity[0], a, &cap_on_capacity);
368372
cost[0]=0;
@@ -598,8 +602,9 @@ static void init_linear_network(const tal_t *ctx,
598602
// when the `i` hits the `next` node.
599603
for(size_t k=0;k<CHANNEL_PARTS;++k)
600604
{
601-
/* FIXME: Can we prune arcs with 0 capacity?
602-
* if(capacity[k]==0)continue; */
605+
/* prune arcs with 0 capacity */
606+
if (capacity[k] == 0)
607+
continue;
603608

604609
struct arc arc = arc_from_parts(chan_id, half, k, false);
605610

@@ -959,10 +964,14 @@ struct flow **minflow(const tal_t *ctx,
959964
params->source = source;
960965
params->target = target;
961966
params->amount = amount;
962-
params->accuracy = AMOUNT_MSAT(1000);
963-
/* FIXME: params->accuracy = amount_msat_max(amount_msat_div(amount,
964-
* 1000), AMOUNT_MSAT(1));
967+
/* -> At most 1000 units of flow are allowed, that reduces the
968+
* computational burden for algorithms that depend on it, eg. "capacity
969+
* scaling" and "successive shortest path".
970+
* -> Using Ceil operation instead of Floor so that
971+
* accuracy x 1000 >= amount
965972
* */
973+
params->accuracy = amount_msat_max(
974+
AMOUNT_MSAT(1), amount_msat_div_ceil(amount, 1000));
966975

967976
// template the channel partition into linear arcs
968977
params->cap_fraction[0]=0;

tests/test_askrene.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1204,7 +1204,7 @@ def test_real_data(node_factory, bitcoind):
12041204
# CI, it's slow.
12051205
if SLOW_MACHINE:
12061206
limit = 25
1207-
expected = (6, 25, 1544756, 142986, 91)
1207+
expected = (6, 25, 1568821, 143649, 91)
12081208
else:
12091209
limit = 100
12101210
expected = (9, 95, 6347877, 566288, 92)
@@ -1321,7 +1321,7 @@ def test_real_biases(node_factory, bitcoind):
13211321
# CI, it's slow.
13221322
if SLOW_MACHINE:
13231323
limit = 25
1324-
expected = ({1: 5, 2: 7, 4: 7, 8: 11, 16: 14, 32: 19, 64: 25, 100: 25}, 0)
1324+
expected = ({1: 6, 2: 6, 4: 7, 8: 12, 16: 14, 32: 19, 64: 25, 100: 25}, 0)
13251325
else:
13261326
limit = 100
13271327
expected = ({1: 23, 2: 31, 4: 40, 8: 53, 16: 70, 32: 82, 64: 96, 100: 96}, 0)

tests/test_xpay.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -671,7 +671,7 @@ def test_xpay_bolt12_no_mpp(node_factory, chainparams, deprecations):
671671

672672
# Amount needs to be enought that it bothers splitting, but not
673673
# so much that it can't pay without mpp.
674-
AMOUNT = 500000000
674+
AMOUNT = 800000000
675675

676676
# l2 will advertize mpp, l3 won't.
677677
l2offer = l2.rpc.offer(AMOUNT, 'test_xpay_bolt12_no_mpp')
@@ -686,10 +686,11 @@ def test_xpay_bolt12_no_mpp(node_factory, chainparams, deprecations):
686686
assert ret['failed_parts'] == 0
687687
if deprecations:
688688
assert ret['successful_parts'] == 2
689+
assert ret['amount_sent_msat'] == AMOUNT + AMOUNT // 100000 + 2
689690
else:
690691
assert ret['successful_parts'] == 1
692+
assert ret['amount_sent_msat'] == AMOUNT + AMOUNT // 100000 + 1
691693
assert ret['amount_msat'] == AMOUNT
692-
assert ret['amount_sent_msat'] == AMOUNT + AMOUNT // 100000 + 1
693694

694695

695696
def test_xpay_slow_mode(node_factory, bitcoind):
@@ -706,18 +707,18 @@ def test_xpay_slow_mode(node_factory, bitcoind):
706707
wait_for(lambda: len(l1.rpc.listchannels()['channels']) == 10)
707708

708709
# First try an MPP which fails
709-
inv = l5.rpc.invoice(500000000, 'test_xpay_slow_mode_fail', 'test_xpay_slow_mode_fail', preimage='01' * 32)['bolt11']
710+
inv = l5.rpc.invoice(800000000, 'test_xpay_slow_mode_fail', 'test_xpay_slow_mode_fail', preimage='01' * 32)['bolt11']
710711
l5.rpc.delinvoice('test_xpay_slow_mode_fail', status='unpaid')
711712

712713
with pytest.raises(RpcError, match=r"Destination said it doesn't know invoice: incorrect_or_unknown_payment_details"):
713714
l1.rpc.xpay(inv)
714715

715716
# Now a successful one
716-
inv = l5.rpc.invoice(500000000, 'test_xpay_slow_mode', 'test_xpay_slow_mode', preimage='00' * 32)['bolt11']
717+
inv = l5.rpc.invoice(800000000, 'test_xpay_slow_mode', 'test_xpay_slow_mode', preimage='00' * 32)['bolt11']
717718

718719
assert l1.rpc.xpay(inv) == {'payment_preimage': '00' * 32,
719-
'amount_msat': 500000000,
720-
'amount_sent_msat': 500010002,
720+
'amount_msat': 800000000,
721+
'amount_sent_msat': 800016004,
721722
'failed_parts': 0,
722723
'successful_parts': 2}
723724

@@ -739,7 +740,7 @@ def test_fail_after_success(node_factory, bitcoind, executor, slow_mode):
739740
bitcoind.generate_block(5)
740741
wait_for(lambda: len(l1.rpc.listchannels()['channels']) == 10)
741742

742-
inv = l5.rpc.invoice(500000000, 'test_xpay_slow_mode', 'test_xpay_slow_mode', preimage='00' * 32)['bolt11']
743+
inv = l5.rpc.invoice(800000000, 'test_xpay_slow_mode', 'test_xpay_slow_mode', preimage='00' * 32)['bolt11']
743744
fut = executor.submit(l1.rpc.xpay, invstring=inv, retry_for=0)
744745

745746
# Part via l3 is fine. Part via l4 is stuck, so we kill l4 and mine
@@ -750,8 +751,8 @@ def test_fail_after_success(node_factory, bitcoind, executor, slow_mode):
750751
# Normally, we return as soon as first part succeeds.
751752
if slow_mode is False:
752753
assert fut.result(TIMEOUT) == {'payment_preimage': '00' * 32,
753-
'amount_msat': 500000000,
754-
'amount_sent_msat': 500010002,
754+
'amount_msat': 800000000,
755+
'amount_sent_msat': 800016004,
755756
'failed_parts': 0,
756757
'successful_parts': 2}
757758

@@ -763,15 +764,15 @@ def test_fail_after_success(node_factory, bitcoind, executor, slow_mode):
763764
l1.daemon.wait_for_log(r"UNUSUAL.*Destination accepted partial payment, failed a part \(Error permanent_channel_failure for path ->022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59->0382ce59ebf18be7d84677c2e35f23294b9992ceca95491fcf8a56c6cb2d9de199->032cf15d1ad9c4a08d26eab1918f732d8ef8fdc6abb9640bf3db174372c491304e, from 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59\)")
764765
# Could be either way around, check both
765766
line = l1.daemon.is_in_log(r"UNUSUAL.*Destination accepted partial payment, failed a part")
766-
assert re.search(r'but accepted only 32000msat of 500000000msat\. Winning\?!', line) or re.search(r'but accepted only 499968000msat of 500000000msat\. Winning\?!', line)
767+
assert re.search(r'but accepted only .* of 800000000msat\. Winning\?!', line)
767768

768769
if slow_mode is True:
769770
# Now it succeeds, but notes that it only sent one part!
770771
res = fut.result(TIMEOUT)
771772
# Some variation due to floating point.
772-
assert res['amount_sent_msat'] < 500000000
773+
assert res['amount_sent_msat'] < 800000000
773774
assert res == {'payment_preimage': '00' * 32,
774-
'amount_msat': 500000000,
775+
'amount_msat': 800000000,
775776
'amount_sent_msat': res['amount_sent_msat'],
776777
'failed_parts': 1,
777778
'successful_parts': 1}

0 commit comments

Comments
 (0)