Commit graph

1834 commits

Author SHA1 Message Date
Rusty Russell
d7adf06dfe xpay: created_at response from pay compatibility is a number, not integer.
Reported-by: @hMsats
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2024-12-18 14:04:14 +10:30
Rusty Russell
3e9a4fc750 xpay: simplify printing of destination.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2024-12-18 14:04:14 +10:30
Lagrang3
545220dfbc xpay: add destination to output
Changelog-Add: xpay: add destination to xpay success output when xpay-handle-pay is active.

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
2024-12-18 14:04:14 +10:30
Rusty Russell
d26ea6673d xpay: more accurately reflect pay when xpay-handle-pay is set.
Note that the slight code reorder changes the JSON order, which is generally
undefined, but our doc checker is very strict!

Changelog-Changed: `xpay` now gives the same JSON success return as documented by `pay` when `xpay-handle-pay` is set.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Fixes: https://github.com/ElementsProject/lightning/issues/7923
2024-12-17 15:49:03 +10:30
Rusty Russell
428c76068c xpay: emulate maxfeepercent and exemptfee when xpay-handle-pay used
maxfeepercent is use by Zeus, so let's make that work.

maxfee is more precise, so it's the only xpay option (maxfee was added
to pay later).

[ Fix to ppm logic by Lagrang3, thanks! --RR ]

Fixes: https://github.com/ElementsProject/lightning/issues/7926
Changelog-Changed: JSON-RPC: With `xpay-handle-pay` set, xpay will now be used even if `pay` uses maxfeeprecent or exemptfee parameters (e.g. Zeus)
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2024-12-17 10:54:31 +10:30
Rusty Russell
cf22762c8f xpay: tell injectpaymentonion what the amount being delivered to destination is.
This means that it gets shown in listsendpays: omitting this broke spark, apparently!

Changelog-Changed: `xpay` now populates more fields, so `listsendpays` and `listpays` show `destination` and `amount_msat` fields for xpay payments.
Fixes: https://github.com/ElementsProject/lightning/issues/7881
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2024-12-17 08:14:45 +10:30
Marcel Hernandez
d6bd61157e Changelog-Fixed: xpay no longer logs "Got command" at info level. 2024-12-15 21:53:43 +10:30
daywalker90
dcb6f2cb2e rust: bump versions for 24.11 release
Changelog-None
2024-12-10 09:37:30 +01:00
Rusty Russell
cb1bd82c86 Final changes for v24.11
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2024-12-09 14:54:50 +10:30
Rusty Russell
cc07f9afc6 v24.11rc4: hopefully the final Release Candidate
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2024-12-06 15:35:11 +10:30
Rusty Russell
57b199bb16 lightningd: simplify injectonionmessage API.
It's undocumented and only used in one place, so we can change it (it
was new in 24.08).

We really want to be able to just handle a raw onionmessage: this allows
oblivious sending of messages, but also, in combination with the coming
onionmessage_forward_fail notification, allows us to connect then
reinject.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2024-12-05 17:38:16 +10:30
Rusty Russell
113156858b xpay: don't excees maxfee *overall*.
We were handing "maxfee" to every getroutes call, even if we had already
used some of the fees.

Reported-by: @daywalker90
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-None: xpay is new this release.
2024-12-02 14:31:11 +10:30
Rusty Russell
b8e5b122d2 decode: don't fail to decode just because a bolt12 invoice has expired.
In fact, there are several places where we try to decode old invoices,
and they should all work.  The only place we should enforce expiration is
when we're going to pay.

This also revealed that xpay wasn't checking bolt11 expiries!

Reported-by: hMsats
Fixes: https://github.com/ElementsProject/lightning/issues/7869
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Fixed: JSON-RPC: `decode` refused to decode expired bolt12 invoices.
2024-11-30 13:17:55 +01:00
Rusty Russell
996fff94fd plugins/Makefile: make cln-grpc depend on ALL THE THINGS.
```
error[E0277]: the trait bound `cln_rpc::model::requests::AskrenereserveRequest: From<pb::AskrenereserveRequest>` is not satisfied
    --> cln-grpc/src/server.rs:3994:56
     |
3994 |         let req: requests::AskrenereserveRequest = req.into();
     |                                                        ^^^^ the trait `From<pb::AskrenereserveRequest>` is not implemented for `cln_rpc::model::requests::AskrenereserveRequest`, which is required by `pb::AskrenereserveRequest: Into<_>`
     |
     = note: required for `pb::AskrenereserveRequest` to implement `Into<cln_rpc::model::requests::AskrenereserveRequest>`

error[E0277]: the trait bound `cln_rpc::model::requests::AskreneageRequest: From<pb::AskreneageRequest>` is not satisfied
    --> cln-grpc/src/server.rs:4026:52
     |
4026 |         let req: requests::AskreneageRequest = req.into();
     |                                                    ^^^^ the trait `From<pb::AskreneageRequest>` is not implemented for `cln_rpc::model::requests::AskreneageRequest`, which is required by `pb::AskreneageRequest: Into<_>`
     |
     = note: required for `pb::AskreneageRequest` to implement `Into<cln_rpc::model::requests::AskreneageRequest>`

error[E0277]: the trait bound `cln_rpc::model::requests::GetroutesRequest: From<pb::GetroutesRequest>` is not satisfied
    --> cln-grpc/src/server.rs:4058:51
     |
4058 |         let req: requests::GetroutesRequest = req.into();
     |                                                   ^^^^ the trait `From<pb::GetroutesRequest>` is not implemented for `cln_rpc::model::requests::GetroutesRequest`, which is required by `pb::GetroutesRequest: Into<_>`
     |
     = note: required for `pb::GetroutesRequest` to implement `Into<cln_rpc::model::requests::GetroutesRequest>`

error[E0277]: the trait bound `cln_rpc::model::requests::AskrenedisablenodeRequest: From<pb::AskrenedisablenodeRequest>` is not satisfied
    --> cln-grpc/src/server.rs:4090:60
     |
4090 |         let req: requests::AskrenedisablenodeRequest = req.into();
     |                                                            ^^^^ the trait `From<pb::AskrenedisablenodeRequest>` is not implemented for `cln_rpc::model::requests::AskrenedisablenodeRequest`, which is required by `pb::AskrenedisablenodeRequest: Into<_>`
     |
     = note: required for `pb::AskrenedisablenodeRequest` to implement `Into<cln_rpc::model::requests::AskrenedisablenodeRequest>`

error[E0277]: the trait bound `cln_rpc::model::requests::AskreneinformchannelRequest: From<pb::AskreneinformchannelRequest>` is not satisfied
    --> cln-grpc/src/server.rs:4122:62
     |
4122 |         let req: requests::AskreneinformchannelRequest = req.into();
     |                                                              ^^^^ the trait `From<pb::AskreneinformchannelRequest>` is not implemented for `cln_rpc::model::requests::AskreneinformchannelRequest`, which is required by `pb::AskreneinformchannelRequest: Into<_>`
     |
     = note: required for `pb::AskreneinformchannelRequest` to implement `Into<cln_rpc::model::requests::AskreneinformchannelRequest>`

error[E0277]: the trait bound `cln_rpc::model::requests::AskrenecreatechannelRequest: From<pb::AskrenecreatechannelRequest>` is not satisfied
    --> cln-grpc/src/server.rs:4154:62
     |
4154 |         let req: requests::AskrenecreatechannelRequest = req.into();
     |                                                              ^^^^ the trait `From<pb::AskrenecreatechannelRequest>` is not implemented for `cln_rpc::model::requests::AskrenecreatechannelRequest`, which is required by `pb::AskrenecreatechannelRequest: Into<_>`
     |
     = note: required for `pb::AskrenecreatechannelRequest` to implement `Into<cln_rpc::model::requests::AskrenecreatechannelRequest>`

error[E0277]: the trait bound `cln_rpc::model::requests::AskreneupdatechannelRequest: From<pb::AskreneupdatechannelRequest>` is not satisfied
    --> cln-grpc/src/server.rs:4186:62
     |
4186 |         let req: requests::AskreneupdatechannelRequest = req.into();
     |                                                              ^^^^ the trait `From<pb::AskreneupdatechannelRequest>` is not implemented for `cln_rpc::model::requests::AskreneupdatechannelRequest`, which is required by `pb::AskreneupdatechannelRequest: Into<_>`
     |
     = note: required for `pb::AskreneupdatechannelRequest` to implement `Into<cln_rpc::model::requests::AskreneupdatechannelRequest>`

error[E0277]: the trait bound `cln_rpc::model::requests::AskrenebiaschannelRequest: From<pb::AskrenebiaschannelRequest>` is not satisfied
    --> cln-grpc/src/server.rs:4218:60
     |
4218 |         let req: requests::AskrenebiaschannelRequest = req.into();
     |                                                            ^^^^ the trait `From<pb::AskrenebiaschannelRequest>` is not implemented for `cln_rpc::model::requests::AskrenebiaschannelRequest`, which is required by `pb::AskrenebiaschannelRequest: Into<_>`
     |
     = note: required for `pb::AskrenebiaschannelRequest` to implement `Into<cln_rpc::model::requests::AskrenebiaschannelRequest>`

error[E0277]: the trait bound `cln_rpc::model::requests::AskrenelistreservationsRequest: From<pb::AskrenelistreservationsRequest>` is not satisfied
    --> cln-grpc/src/server.rs:4250:65
     |
4250 |         let req: requests::AskrenelistreservationsRequest = req.into();
     |                                                                 ^^^^ the trait `From<pb::AskrenelistreservationsRequest>` is not implemented for `cln_rpc::model::requests::AskrenelistreservationsRequest`, which is required by `pb::AskrenelistreservationsRequest: Into<_>`
     |
     = note: required for `pb::AskrenelistreservationsRequest` to implement `Into<cln_rpc::model::requests::AskrenelistreservationsRequest>`

error[E0277]: the trait bound `cln_rpc::model::requests::InjectpaymentonionRequest: From<pb::InjectpaymentonionRequest>` is not satisfied
    --> cln-grpc/src/server.rs:4282:60
     |
4282 |         let req: requests::InjectpaymentonionRequest = req.into();
     |                                                            ^^^^ the trait `From<pb::InjectpaymentonionRequest>` is not implemented for `cln_rpc::model::requests::InjectpaymentonionRequest`, which is required by `pb::InjectpaymentonionRequest: Into<_>`
     |
     = note: required for `pb::InjectpaymentonionRequest` to implement `Into<cln_rpc::model::requests::InjectpaymentonionRequest>`

error[E0277]: the trait bound `cln_rpc::model::requests::XpayRequest: From<pb::XpayRequest>` is not satisfied
    --> cln-grpc/src/server.rs:4314:46
     |
4314 |         let req: requests::XpayRequest = req.into();
     |                                              ^^^^ the trait `From<pb::XpayRequest>` is not implemented for `cln_rpc::model::requests::XpayRequest`, which is required by `pb::XpayRequest: Into<_>`
     |
     = note: required for `pb::XpayRequest` to implement `Into<cln_rpc::model::requests::XpayRequest>`
```

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2024-11-29 09:46:17 +10:30
daywalker90
36ad654fc1 Makefile: ensure that cln-grpc depends on msggen generated rust files.
Changelog-None
2024-11-28 16:40:02 +10:30
Rusty Russell
034d3c9628 Build: update to version 24.11rc1
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2024-11-27 06:27:50 +10:30
ShahanaFarooqui
01ede70e80 gitignore: cln-xpay plugin 2024-11-26 21:45:19 +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
d256e11108 askrene: reorder functions.
No code changes.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
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
efacada7dd spender: fix multifundchannel ids.
They're not always 34 (aka '"').  This is a side-effect of ids
changing from u64 to strings quite a while ago.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2024-11-26 16:04:13 +10:30
Christian Decker
7b18030ee2 grpc: Do not print wildcard notifications that don't have a handler
Changelog-Fixed: grpc: We no longer log a warning if a notification does not have a handler
2024-11-25 15:41:43 +01:00
Rusty Russell
68feb55dbf wallet: save last known address.
If we connected out, remember that address.  We always remember the last
address, but that may be an incoming address.  This is explicitly the last
outgoing address which worked.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2024-11-25 15:39:13 +10:30
Rusty Russell
90ab9325a1 xpay: give an additional block "slack" for CLTV values.
pay does this, xpay does not.  Which means if a block comes in (or you're behind),
you get gratuitous failures:

```
    def test_xpay_simple(node_factory):
        l1, l2, l3, l4 = node_factory.get_nodes(4, opts={'may_reconnect': True})
        node_factory.join_nodes([l1, l2, l3], wait_for_announce=True)
        node_factory.join_nodes([l3, l4], announce_channels=False)

        # BOLT 11, direct peer
        b11 = l2.rpc.invoice('10000msat', 'test_xpay_simple', 'test_xpay_simple bolt11')['bolt11']
>       ret = l1.rpc.xpay(b11)

tests/test_xpay.py:148:
...
        if not isinstance(resp, dict):
            raise TypeError("Malformed response, response is not a dictionary %s." % resp)
        elif "error" in resp:
>           raise RpcError(method, payload, resp['error'])
E           pyln.client.lightning.RpcError: RPC call failed: method: xpay, payload: ('lnbcrt100n1pn5qu7csp53rp0mfwtfsyyy8gzsggepnxgslyalwvz3jkg9ptmqq452ln2nmgqpp58ak9nmfz9l93r0fpm266ewyjrhurhatrs05nda0r03p82cykp0vsdp9w3jhxazl0pcxz72lwd5k6urvv5sxymmvwscnzxqyjw5qcqp99qxpqysgqa798258yppu2tlfj8herr3zuz0zgux79zvtx6z57cmfzs2wdesmr4nvnkcmyssyu6k64ud54eg0v45c3mcw342jj6uy7tu202p6klrcp6ljc9w',), error: {'code': 203, 'message': "Destination said it doesn't know invoice: incorrect_or_unknown_payment_details"}
```

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-None: xpay is new this release.
2024-11-23 10:20:30 +10:30
Lagrang3
05514b46e3 Askrene: change median factor to 1.
The ratio of the median of the fees and probability cost is overall not
a bad factor to combine these two features. This is what the
test_real_data shows.

Changelog-None

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
2024-11-21 16:17:52 +10:30
Lagrang3
2b3fd67dfb askrene: don't skip fee_fallback test
The fee_fallback test would fail after fixing the computation of the
median. Now by we can restore it by making the probability cost factor
1000x higher than the ratio of the median. This shows how hard it is to
combine fee and probability costs and why is the current approach so
fragile.

Changelog-None

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
2024-11-21 16:17:52 +10:30
Lagrang3
9fdcc26d1d askrene: bugfix queue overflow
Changelog-none

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
2024-11-21 16:17:52 +10:30
Lagrang3
9966969d4c askrene: remove allocation checks
Rusty: "allocations don't fail"

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
2024-11-21 16:17:52 +10:30
Lagrang3
460a28bb32 askrene: add compiler flag ASKRENE_UNITTEST
Rusty: "We don't generally use NDEBUG in our code"

Instead use a compile time flag ASKRENE_UNITTEST to make checks on unit
tests that we don't normally need on release code.

Changelog-none

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
2024-11-21 16:17:52 +10:30
Lagrang3
b1cd26373b askrene: small fixes suggested by Rusty Russell
- use graph_max_num_arcs/nodes instead of tal_count in bound checks,
- don't use ccan/lqueue, use instead a minimalistic queue
  implementation with an array,
- add missing const qualifiers to temporary tal allocators,
- check preconditions with assert,
- remove inline specifier for static functions,

Changelog-None

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
2024-11-21 16:17:52 +10:30
Lagrang3
44c9609f3a askrene: add arbitrary precision flow unit
Changelog-none: askrene: add arbitrary precision flow unit

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
2024-11-21 16:17:52 +10:30
Lagrang3
4dc1a44cd9 askrene: fix the median
The calculation of the median values of probability and fee cost in the
linear approximation had a bug by counting on non-existing arcs.

Changelog-none: askrene: fix the median

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
2024-11-21 16:17:52 +10:30
Lagrang3
ee623616d2 askrene: fix CI
check the return value of scanf in askrene unit tests,

Changelog-none: askrene: fix CI

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
2024-11-21 16:17:52 +10:30
Lagrang3
937cf7a554 askrene: use the new MCF solver
Changelog-none: askrene: use the new MCF solver

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
2024-11-21 16:17:52 +10:30
Lagrang3
84a9476311 askrene: add mcf_refinement to the public API
Changelog-none: askrene: add mcf_refinement to the public API

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
2024-11-21 16:17:52 +10:30
Lagrang3
2142094e43 askrene: fix bug, not all arcs exists
We use an arc "array" in the graph structure, but not all arc indexes
correspond to real topological arcs. We must be careful when iterating
through all arcs, and check if they are enabled before making operations
on them.

Changelog-None: askrene: fix bug, not all arcs exists

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
2024-11-21 16:17:52 +10:30
Lagrang3
e655fe7bbd askrene: add bigger test for MCF
Using zlib to read big test case file.

Changelog-None: askrene: add bigger test for MCF

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
2024-11-21 16:17:52 +10:30
Lagrang3
2ea8e49683 askrene: add a MCF refinement
Add a new function to compute a MCF using a more general description of
the problem. I call it mcf_refinement because it can start with a
feasible flow (though this is not necessary) and adapt it to achieve
optimality.

Changelog-None: askrene: add a MCF refinement

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
2024-11-21 16:17:52 +10:30
Lagrang3
1dfa562cd9 askrene algorithm add helper for flow conservation
Changelog-None: askrene algorithm add helper for flow conservation

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
2024-11-21 16:17:52 +10:30
Lagrang3
42d075cc97 askrene: add a simple MCF solver
Changelog-EXPERIMENTAL: askrene: add a simple MCF solver

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
2024-11-21 16:17:52 +10:30
Lagrang3
8558299dec askrene: add algorithm to compute feasible flow
Changelog-EXPERIMENTAL: askrene: add algorithm to compute feasible flow

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
2024-11-21 16:17:52 +10:30
Lagrang3
f4f2985bdf askrene: add dijkstra algorithm
Changelog-EXPERIMENTAL: askrene: add dijkstra algorithm

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
2024-11-21 16:17:52 +10:30
Lagrang3
507153a1cd askrene: add graph algorithms module
Changelog-EXPERIMENTAL: askrene: add graph algorithms module

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
2024-11-21 16:17:52 +10:30
Lagrang3
59ce410699 askrene: add priorityqueue
It is just a copy-paste of "dijkstra" but the name
implies what it actually is. Not an implementation of minimum cost path
Dijkstra algorithm, but a helper data structure.
I keep the old "dijkstra.h/c" files for the moment to avoid breaking the
current code.

Changelog-EXPERIMENTAL: askrene: add priorityqueue

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
2024-11-21 16:17:52 +10:30
Lagrang3
32548cf02b askrene: add a new graph abstraction
Changelog-EXPERIMENTAL: askrene new graph abstraction

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
2024-11-21 16:17:52 +10:30
Rusty Russell
2bf1053cdb offers: update block height correctly.
As we can see from the previous test, l3 tells us why it rejected the payment:

```
lightningd-3 2024-11-19T03:56:27.151Z DEBUG   022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-chan#1: Failing HTLC because of an invalid payload (TLV 10 pos 104): cltv_expiry 136 > payment_constraint 130
```

It turns out, we were not updating the block height in the plugin!

Without this, when we create a (non-dummy) blinded path we set a
too-low CLTV restriction, and it doesn't work after a few blocks!

Note we were actually triggering this error in the xpay tests!

Reported-by: Vincenzo Palazzo
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Fixed: Offers: Receiving bolt12 payments where we have no public channels would fail a few blocks after startup.
2024-11-20 12:29:27 +01:00
Rusty Russell
ba7bf334b1 fetchinvoice: check better.
We do a lot more parameter checking than simply parsing, so use
param_check().

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2024-11-19 22:54:22 +01:00
Vincenzo Palazzo
6f0dbbb20f bolt12: allow to inject payer_metadata
Payer metadata is a field that controls the payer ID
provided during the fetchinvoice process.

There are use cases where this is highly useful, such as
proving that the payer has paid for the correct item.

Imagine visiting a merchant's website to pay for multiple offers, where
one of these offers is a default offer (with no description and no set amount).

In this scenario, the merchant could claim not to have received
payment for a specific item. Since the same offer may be used to
fetch invoices for different products, there needs to be a way to
identify which product the invoice corresponds to.

With this commit, it will be possible to inject payer metadata,
which helps solve the issue described above.

For example, possible payer metadata could be `to_hex(b"{payer_node_id}.{product_id}.{created_at}")`.

Changelog-Added: JSON-RPC: `fetchinvoice` allows setting invreq_metadata via `payer_metadata` parameter.
Signed-off-by: Vincenzo Palazzo <vincenzopalazzodev@gmail.com>
2024-11-19 22:54:22 +01:00
Rusty Russell
1b2d5acf16 askrene: don't create duplicate layers if xpay creates layer before we load them.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2024-11-19 17:51:18 +10:30
Rusty Russell
1380d36898 xpay: don't try to timeout things until after we have created xpay layer.
```
lightningd-1 2024-11-19T05:21:16.313Z DEBUG   lightningd: Looking for [askrene,layers]
lightningd-1 2024-11-19T05:21:16.314Z DEBUG   lightningd: Got [askrene,layers,xpay]
lightningd-1 2024-11-19T05:21:16.314Z DEBUG   lightningd: Printing
lightningd-1 2024-11-19T05:21:16.315Z **BROKEN** plugin-cln-xpay: askrene-age failed with {\"code\":-32602,\"message\":\"layer: Unknown layer: invalid token '\\\"xpay\\\"'\"}
lightningd-1 2024-11-19T05:21:16.318Z DEBUG   plugin-cln-askrene: datastore = {\"datastore\":[{\"key\":[\"askrene\",\"layers\",\"xpay\"],\"generation\":13,\"hex\":\"000300001000003f47af0100000000673c1fea010000000000d1b0d4000003000000000ce5066e0000000000673c1fea010000000000d1b0d400000300001000001a47050000000000673c1fea010000000000d1b0d400000300003f00005a72b40100000000673c1fea010000000000d1b0d400000300000000005a07e80100000000673c1fea010000000000d1b0d400000300001a0000e3564c0100000000673c1fea010000000000d1b0d40000030000e3000db69cf50000000000673c1fea0001000000000100637e\"}]}
lightningd-1 2024-11-19T05:21:16.318Z DEBUG   plugin-cln-askrene: Loaded level xpay (203 bytes)
lightningd-1 2024-11-19T05:21:16.391Z INFO    plugin-cln-xpay: Killing plugin: exited during normal operation
```

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2024-11-19 17:51:18 +10:30