Commit Graph

96 Commits

Author SHA1 Message Date
Rusty Russell
0ede29b81f askrene: fork before calling the route solver.
This is fairly simple.  We do all the prep work, fire off the child,
and it continues all the way to producing JSON output (or an error).
The parent then forwards it.

Limitations (fixed in successive patches):

1. Child logging currently gets lost.
2. We wait for the child, so this code is not a speedup.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2026-02-19 17:04:35 +10:30
Lagrang3
3822a67030 askrene: add auto.include_fees layer
Changelog-Added: askrene: add a new layer auto.include_fees thhat makes fees be deducted from the payment amount making in effect the receiver pay for routing fees.

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
2026-01-29 15:20:25 +10:30
Rusty Russell
a3946663ec askrene: speed up when using large number of layers.
Simple bench.

Before:
   Creating 20,000 layers:  20 seconds
   Creating 50,000 layers:  107 seconds

After:
   Creating 20,000 layers:  7 seconds
   Creating 50,000 layers:  15 seconds
   Creating 100,000 layers: 29 seconds

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2026-01-27 13:59:36 +10:30
Rusty Russell
241324aa09 gossipd: don't shortcut dying phase for local channels.
This means that we won't complain to peers which gossip about our
channels, but it does mean that our channel graph (like other nodes on
the network) will show two channels, not one, for the duration.

For this reason, we need askrene to omit local dying channels.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2026-01-08 22:33:19 +10:30
Rusty Russell
1102d8063e askrene: add optional layers to reservations.
We have the issue of aliases: xpay uses scids like 0x0x0 for
routehints and blinded paths, and then can apply reservations to them.  But
generally, reservations are *global*, so we need to differentiate.

Changelog-Added: Plugins: `askrene-reserve` and `askrene-unreserve` can take an optional `layer` inside `path` elements.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2025-11-17 13:52:54 +10:30
Rusty Russell
050b149e53 askrene: handle maxparts parameter values 1 and 0.
For 1, we use single-path.  For 0, reject.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2025-11-17 10:56:18 +10:30
Rusty Russell
35f65c5d91 common: add amount_msat_deduct / amount_msat_deduct_sub.
I added amount_msat_accumulate for the "a+=b" case, but I was struggling
with a name for the subtractive equivalent.  After some prompting, ChatGPT
suggested deduct.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2025-11-17 10:56:18 +10:30
Rusty Russell
8b9020d7b9 global: use clock_time in place of time_now().
Except for tracing, that sticks with time_now().

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2025-11-13 21:21:29 +10:30
Lagrang3
4f1c8806d7 askrene: add askrene-bias-node rpc
Changelog-Added: askrene-bias-node: an RPC command to set a bias on node's outgoing or incoming channels.

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
2025-11-13 15:15:27 +10:30
Lagrang3
5769beb1db askrene: add timestamp to biases
We add one more field to biases: "timestamp".
With the timestamp variable old biases can be removed with the
askrene-age command.

Changelog-Added: Plugins: askrene channel biases now have an associated timestamp, and are timed out by askrene-age

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
2025-11-13 15:15:27 +10:30
Rusty Russell
07c57b6015 askrene: implement 10-second deadline.
We have another report of looping.  This maxparts code is being completely
rewritten, but it's good to have a catchall for any other cases which might
emerge.

I had to make it customizable since our tests under valgrind are SLOW!

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2025-11-03 12:54:37 +10:30
Rusty Russell
75616f6b77 common: add new_htable() macro to allocate, initialize and setup memleak coverage for any typed hash table.
You can now simply add per-tal-object helpers for memleak, but our older pattern required
calling memleak functions explicitly during memleak handling.  Hash tables in particular need
to be dynamically allocated (we override the allocators using htable_set_allocator and assume
this), so it makes sense to have a helper macro that does all three.

This eliminates a huge amount of code.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2025-10-24 11:30:17 +10:30
Rusty Russell
f6a4e79420 global: remove unnecessary includes from headers.
Each header should only include the other headers it needs to compile;
`devtools/reduce-includes.sh */*.h` does this.  The C files then need
additional includes if they don't compile.

And remove the entirely useless wire/onion_wire.h, which only serves to include wire/onion_wiregen.h.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2025-10-23 06:44:04 +10:30
Lagrang3
0c5c7a5334 askrene: add maxparts
Changelog-Added: askrene: add a new parameter maxparts to getroutes that limits the number of routes in the solution.

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
2025-08-15 16:13:19 +09:30
Lagrang3
b479963c09 askrene: refine: disable HTLC min violations
Disable channels with HTLC min violations so that we don't hit them
twice when computing routes.

Changelog-None

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
2025-08-08 14:07:51 +09:30
Lagrang3
25c88447fa askrene: add algorithm for single path routing
Changelog-Added: askrene: an optimal single-path solver has been added, it can be called using the developer option --dev_algorithm=single-path or by adding the layer "auto.no_mpp_support"

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
2025-07-18 15:13:41 +09:30
Lagrang3
34cf3334f8 askrene: add a dev parameter to switch algorithm
Changelog-None

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
2025-07-11 12:14:45 +09:30
Rusty Russell
4500818312 askrene: fix up getroutes_info struct.
It was originally a wrapper for JSON param() results, but now it's a more
generic struct.  So make it clear that the fields are not optional.  This
means a manual assignment in the initial population of this struct, but
all the users are now far clearer.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2025-07-11 12:14:45 +09:30
Rusty Russell
2a5239589e askrene: move get_routes into do_getroutes.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2025-07-11 12:14:45 +09:30
Rusty Russell
71525173a0 askrene: extract getroutes json formatting function.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2025-07-11 12:14:45 +09:30
Rusty Russell
98e30f2c42 askrene: extracet convert_flows_to_routes into its own function.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2025-07-11 12:14:45 +09:30
Rusty Russell
2a8334b06e plugins/askrene: extract apply_layers into its own function.
Simple refactoring.

Stolen entirely from Eduardo.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2025-07-11 12:14:45 +09:30
Rusty Russell
f1fa498663 askrene: houst struct getroutes_info higher to clarify following patches.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2025-07-11 12:14:45 +09:30
Lagrang3
88bb1636bc askrene: refactor get_routes ...
Move the feature tuning algorithm to mcf.c, ie. the loops for searching
a good mu and delay_feefactor to satisfy the problem constraints.

We are looking to set the stage for an execution logic that allows for
multiple choices of routing algorithms, mainly for experimenting without
breaking the default working code.

Changelog-None

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
2025-07-11 12:14:45 +09:30
Lagrang3
266a5f9e85 askrene: add runtime of getroutes to the logs
Add log information about the runtime of getroutes.

Changelog-None: askrene: add runtime of getroutes to the logs

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
2025-07-10 23:37:06 +09:30
Lagrang3
81417958d4 askrene-inform-channel: change order of operations
Change order of operations to ensure that askrene-inform-channel with
inform=constrained computes the correct upper bound on the liquidity:

    upperBound = reserves + amount - 1

With the previous order of operations in the case amount=0 we would get:

    upperBound = reserves

instead of
    upperBound = reserves - 1

Changelog-None

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
2025-05-13 19:14:57 +09:30
Rusty Russell
9ce3f5dde4 askrene: fix API breakage, add tests.
We cannot add new parameters in the middle, since we accept parameters by JSON
array as well as by dicts.  In fact, this broke tests, but due to unrelated
breakage in the GitHub "Automerge" functionality, it got applied as
556e38c838 ("askrene-bias-channel: bias call add
up.").

Also add tests, and a better Changelog line.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Added: JSON-RPC: `askrene-bias-channel` now has a `relative` option to add, rather than replace, a channel bias.
2025-05-11 11:25:40 +09:30
Lagrang3
556e38c838 askrene-bias-channel: bias call add up.
The channel bias feature is not being used yet by any plugin, so this
hopefully doesn't break any working code.
When askrene-bias-channel is called the bias quantity is added on top of
any previous biased already present on that channel instead of
overwriting it.

Changelog-Changed: askrene-bias-channel: bias call add up.

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
2025-05-08 12:28:57 +09:30
Lagrang3
47babbf661 askrene: add LOG_TRACE to every rpc entry point
This could be helpful for debugging the interaction of renepay/xpay with
askrene.

Changelog-None

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
2025-05-02 13:59:13 -07:00
Lagrang3
35f899a588 fetch max total htlc value from listpeerchannel
The parameter max_htlc_value_in_flight_msat stablished by peers on
channel opening (BOLT02) can now be retrived from the
gossmods_from_listpeerchannels API.

Adapted the corresponding callback functions in renepay and askrene to
take into account that value as a constraint to the value we can send
through a channel.

Changelog-Add: fetch max_htlc_value_in_flight_msat from gossmods_listpeerchannels API

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
2025-02-21 17:03:36 -06:00
Rusty Russell
461eb2eddb askrene: remove all small channels if there's no MPP support.
This is an inefficient hack.  Can you tell I really didn't want to
implement this?  MPP was finalized in 2018 FFS.

We do this by adding another "auto" layer, which removes all too-small
channels, and then makes our MPP node pile all the funds into the largest
channel it chooses.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2025-02-14 22:17:21 +10:30
Rusty Russell
fdfc7ce62f gossmap: add (and use) logging hook.
Default goes to stderr for LOG_UNUSUAL and higher.

We have to whitelist more cases in map_catchup so we don't spam the logs
with perfectly-expected (but ignored) messages though.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2025-02-11 15:11:47 -06:00
Rusty Russell
c31eed433c askrene: expose fmt_flow_full and make more generic.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2025-01-28 10:53:22 +10:30
Rusty Russell
01bce4e7b7 askrene: don't crash if asked to route 0 msat.
But don't allow it either.

```
Jan 19 13:18:52 boltz lightningd[2259911]: cln-askrene: plugins/askrene/algorithm.c:274: simple_feasibleflow: Assertion `amount > 0' failed.
Jan 19 13:18:52 boltz lightningd[2259911]: cln-askrene: FATAL SIGNAL 6 (version v24.11.1-1-ge9dbdeb)
Jan 19 13:18:52 boltz lightningd[2259911]: 0x5576e212b407 send_backtrace
Jan 19 13:18:52 boltz lightningd[2259911]:         common/daemon.c:33
Jan 19 13:18:52 boltz lightningd[2259911]: 0x5576e212b49e crashdump
Jan 19 13:18:52 boltz lightningd[2259911]:         common/daemon.c:75
Jan 19 13:18:52 boltz lightningd[2259911]: 0x7f964ba9251f ???
Jan 19 13:18:52 boltz lightningd[2259911]:         ???:0
Jan 19 13:18:52 boltz lightningd[2259911]: 0x7f964bae69fc ???
Jan 19 13:18:52 boltz lightningd[2259911]:         ???:0
Jan 19 13:18:52 boltz lightningd[2259911]: 0x7f964ba92475 ???
Jan 19 13:18:52 boltz lightningd[2259911]:         ???:0
Jan 19 13:18:52 boltz lightningd[2259911]: 0x7f964ba787f2 ???
Jan 19 13:18:52 boltz lightningd[2259911]:         ???:0
Jan 19 13:18:52 boltz lightningd[2259911]: 0x7f964ba7871a ???
Jan 19 13:18:52 boltz lightningd[2259911]:         ???:0
Jan 19 13:18:52 boltz lightningd[2259911]: 0x7f964ba89e95 ???
Jan 19 13:18:52 boltz lightningd[2259911]:         ???:0
Jan 19 13:18:52 boltz lightningd[2259911]: 0x5576e211695e simple_feasibleflow
Jan 19 13:18:52 boltz lightningd[2259911]:         plugins/askrene/algorithm.c:274
Jan 19 13:18:52 boltz lightningd[2259911]: 0x5576e2111495 minflow
Jan 19 13:18:52 boltz lightningd[2259911]:         plugins/askrene/mcf.c:1014
Jan 19 13:18:52 boltz lightningd[2259911]: 0x5576e210bc74 get_routes
Jan 19 13:18:52 boltz lightningd[2259911]:         plugins/askrene/askrene.c:414
Jan 19 13:18:52 boltz lightningd[2259911]: 0x5576e210c610 do_getroutes
Jan 19 13:18:52 boltz lightningd[2259911]:         plugins/askrene/askrene.c:615
Jan 19 13:18:52 boltz lightningd[2259911]: 0x5576e210cad8 listpeerchannels_done
Jan 19 13:18:52 boltz lightningd[2259911]:         plugins/askrene/askrene.c:741
Jan 19 13:18:52 boltz lightningd[2259911]: 0x5576e211b35a handle_rpc_reply
Jan 19 13:18:52 boltz lightningd[2259911]:         plugins/libplugin.c:1084
Jan 19 13:18:52 boltz lightningd[2259911]: 0x5576e211b54c rpc_read_response_one
Jan 19 13:18:52 boltz lightningd[2259911]:         plugins/libplugin.c:1388
Jan 19 13:18:52 boltz lightningd[2259911]: 0x5576e211b5fd rpc_conn_read_response
Jan 19 13:18:52 boltz lightningd[2259911]:         plugins/libplugin.c:1412
Jan 19 13:18:52 boltz lightningd[2259911]: 0x5576e214fe8f next_plan
Jan 19 13:18:52 boltz lightningd[2259911]:         ccan/ccan/io/io.c:60
Jan 19 13:18:52 boltz lightningd[2259911]: 0x5576e215036e do_plan
Jan 19 13:18:52 boltz lightningd[2259911]:         ccan/ccan/io/io.c:422
Jan 19 13:18:52 boltz lightningd[2259911]: 0x5576e215042b io_ready
Jan 19 13:18:52 boltz lightningd[2259911]:         ccan/ccan/io/io.c:439
Jan 19 13:18:52 boltz lightningd[2259911]: 0x5576e2151e2e io_loop
Jan 19 13:18:52 boltz lightningd[2259911]:         ccan/ccan/io/poll.c:455
Jan 19 13:18:52 boltz lightningd[2259911]: 0x5576e211cc29 plugin_main
Jan 19 13:18:52 boltz lightningd[2259911]:         plugins/libplugin.c:2488
Jan 19 13:18:52 boltz lightningd[2259911]: 0x5576e210cb38 main
Jan 19 13:18:52 boltz lightningd[2259911]:         plugins/askrene/askrene.c:1262
Jan 19 13:18:52 boltz lightningd[2259911]: 0x7f964ba79d8f ???
```

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Fixed: JSON-RPC: `getroutes` will refuse, not crash, if asked to find a route fr 0msat.
2025-01-28 10:53:22 +10:30
michael1011
060368bb0a xpay: add maxdelay parameter
Changelog-Added: Plugins: `xpay` now supports a `maxdelay` parameter for better `xpay-handle-pay` compatibility.
2025-01-22 12:19:47 -08:00
Rusty Russell
b6c1ffa359 ccan/htable: update to explicit DUPS/NODUPS types.
The updated API requires typed htables to explicitly state whether they
allow duplicates: for most cases we don't, but we've had issues in the
past.

This is a big patch, but mainly mechanical.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2025-01-21 09:18:25 +10:30
Rusty Russell
fba738b65e askrene: really fix race between layer creation and persistent layer loading.
Using jsonrpc_request_sync, layers are loaded before we finish init,
so we never can be asked to create a layer before we've loaded it
(xpay creates a layer immediately on startup).

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-None: persistent layers new this release.
2024-11-26 16:04:13 +10:30
Rusty Russell
af629e600e askrene: don't re-save layers as we restore them!
Create lower-level versions of routines to create biases, layers,
constraints, etc and only save the ones called from the public APIs.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-None: persistent layers were only added in this release
2024-11-26 16:04:13 +10:30
Rusty Russell
d85dcc0ce4 askrene: persistent layer support.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2024-11-08 21:48:55 +10:30
Rusty Russell
b2dcf7248d askrene: add askrene-bias-channel.
This lets you place annotated biases on channels, to influence routing.

Uses include avoiding TOR nodes, slow channels or other local preferences.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-None: askrene is new anyway.
2024-11-08 21:48:55 +10:30
Rusty Russell
c797b6fb20 libplugin: add method string to jsonrpc callbacks, implement generic helpers.
Without knowing what method was called, we can't have useful general logging
methods, so go through the pain of adding "const char *method" everywhere,
and add:

1. ignore_and_complete - we're done when jsonrpc returned
2. log_broken_and_complete - we're done, but emit BROKEN log.
3. plugin_broken_cb - if this happens, fail the plugin.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2024-11-07 17:04:35 +10:30
Rusty Russell
c5099b1647 libplugin: clean up API.
When we used to allow cmd to be NULL, we had to hand the plugin
everywhere.  We no longer do.

1. Various jsonrpc_ functions no longer need the plugin arg.
2. send_outreq no longer needs a plugin arg.
3. The init function takes a command, not a plugin.
4. Remove command_deprecated_in_nocmd_ok.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2024-11-07 17:04:35 +10:30
Rusty Russell
95c5fda79f askrene: remove flowset_probability() now refine step calculates it.
Now we've checked it gives the same answers, we can remove a lot of
work in flow.c.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2024-10-15 09:58:04 +10:30
Rusty Russell
5501e4b13d askrene: use refine step to calculate flowset probability.
Since we know the total reservations on each hop, we can more easily
determine probabilities than using flowset_probability() which has to
replicate this collision detection.

We leave both in place for now, to check.  The results are not
identical, due to slightly different calculation methods.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2024-10-15 09:58:04 +10:30
Rusty Russell
bcc8bd59c8 askrene: don't *completely* ignore fees to start.
I noticed that increasing mu a little bit sometimes made a big difference,
because by completely ignoring fees we were choosing the worst of two channels
in some cases.

Start at 1% fees; this saves a lot on initial fees in this test!

Here's the new stats on mu levels:

     96  mu=1
     90  mu=10
     41  mu=20
     30  mu=30
     24  mu=40
     19  mu=50
     22  mu=60
      8  mu=70
     95  mu=80
     19  mu=90

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-EXPERIMENTAL: `askrene` is now better at finding low-fee paths.
2024-10-15 09:58:04 +10:30
Rusty Russell
32aa79a1e2 askrene: debug and check we actually reduce fees when mu increase.
Even after the previous fix, we still occasionally increase fees when my increases.

This is due to the difference between MCF's linear fees, and actual fees, and
is unavoidable, but add a check if it somehow happens.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2024-10-15 09:58:04 +10:30
Rusty Russell
08df93cb25 askrene: fix base fee.
I noticed this in the logs:

	plugin-cln-askrene: notify msg unusual: The flows had a fee of 151950msat, greater than max of 53697msat, retrying with mu of 10%...
	plugin-cln-askrene: notify msg unusual: The flows had a fee of 220126msat, greater than max of 53697msat, retrying with mu of 20%...

We would expect increasing mu to *reduce* the fee!

Turns out that our linear fee is a bad terrible approximation, because I
was using base_fee_penalty of 10.0.

 |
 |          /   __ <- real fee, with base: fee = base + propfee * amount.
 |         / __/
 |       _//
 |    __/
 | __/_/
 |/  _/
 | _/ <- linearized fee: fee = linear * amount
 |/
 +-----------------------------------

These cross over where linear = propfee + base / amount.  Assume we split the
payment into 10 parts, this implies that the base_fee_penalty should be 10 / amount
(this gives a slight penalty to the normal case, but that's ok).

This gives better results, too: we get down to 650099 sats in fees, vs 801613
before.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2024-10-15 09:58:04 +10:30
Rusty Russell
6273adbe47 askrene: calculate prob_cost_factor using ratio of typical mainnet channel.
During "test_real_data", then only successes with reduced fees were 92 on "mu=10", and only
1 on "mu=30": the rest went to mu=100 and failed.

I tried numerous approaches, and in the end, opted for the simplest:

The typical range of probability costs looks likes:
	min = 0, max = 924196240, mean = 10509.4, stddev = 1.9e+06

The typical range of linear fee costs looks like:
	min = 0, max = 101000000, mean = 81894.6, stddev = 2.6e+06

This implies a k factor of 8 makes the two comparable.

This makes the two numbers comparable, and thus makes "mu" much more
effective.  Here are the number of different mu values we succeeded at:

     87  mu=0
     90  mu=10
     42  mu=20
     24  mu=30
     17  mu=40
     19  mu=50
     19  mu=60
     11  mu=70
     95  mu=80
     19  mu=90

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2024-10-15 09:58:04 +10:30
Rusty Russell
83eee64fda pytest: test askrene with worse maxfee argument.
We ask it again, but reduce fees by 1msat from the previous answer.
This is really nasty, as it frequently exercises the case where we
only go over fee when we do the refinement step.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2024-10-15 09:58:04 +10:30
Rusty Russell
1b82a3ad5b askrene: constrain to exact htlc_min/htlc_max values.
The fp16_t values are approximations (overestimate for htlc_max,
underestimate for htlc_min), so in the refinement step we should use
the exact values.

This also fixes a logic bug: flow_remaining_capacity returned the
total capacity, not the additional capacity!

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-EXPERIMENTAL: `askrene` now honors exact htlc_maximum_msat limits.
2024-10-15 09:58:04 +10:30