The plugin start command only accepted plugin options flattened onto
the RPC call (e.g. via -k/--keyword), but the plugin RPC schema
documents an explicit 'options' array. This mismatch meant that callers
using named parameters against generated RPC bindings (cln-rpc, grpc,
protobuf), which cannot flatten arbitrary options, failed with
'unknown parameter options'.
Options without a value are treated as boolean flags, matching the
flattened form.
Changelog-Fixed: JSON-RPC: `plugin start` now accepts plugin options as an `options` array of `keyword=value` strings, as documented in the `plugin` schema.
CLN is too permissive for serde_json when validating json:
`lightning-cli -k myplugin-cmd channels='[123456x1x0]'`
is valid for lightning-cli but is actually invalid json (bare token in array).
The cln-plugin decoder would error and end the FramedRead stream, causing the
PluginDriver loop to exit, and therefore exiting the plugin itself.
We need to recover the id from the invalid json with a separate parser to return a
json rpc error to CLN with the correct id so the rpc command does not hang.
Changelog-None
```
def send_many_payments():
passes = 0
fails = 0
# Make sure we try many times, and get at least one pass and fail.
while passes == 0 or fails == 0 or passes + fails < 30:
inv = l3.rpc.invoice(100, "label-" + str(passes + fails), "desc")
l1.rpc.sendpay(route, inv['payment_hash'], payment_secret=inv['payment_secret'])
time.sleep(0.05)
try:
l1.rpc.waitsendpay(inv['payment_hash'])
passes += 1
except RpcError:
fails += 1
pass
# Send a heap of payments, while reconnecting...
fut = executor.submit(send_many_payments)
for _ in range(30):
l3.rpc.disconnect(l2.info['id'], force=True)
time.sleep(0.1)
l3.rpc.connect(l2.info['id'], 'localhost', l2.port)
> fut.result(TIMEOUT)
```
The while loop would somtimes go infinitely long if no payments fail.
Changelog-None
`check-source` -> `check-wire-format` unconditionally ran
`extract-bolt-csv`, which failed when `../bolts` was not checked out:
```
/bin/sh: 1: .tmp.lightningrfc/tools/extract-formats.py: not found
make: *** [wire/Makefile:54: wire/peer_wire.csv.raw] Error 127
```
Skip it via `bolt-precheck` when `.tmp.lightningrfc` is missing,
matching the `bolt-check` pattern.
Changelog-None
We need to wait for bwatch to see the deposit block or it won't see a
reorg.
```
assert l1.db_query('SELECT COUNT(*) AS c FROM outputs')[0]['c'] == 1
# Reorg the deposit block away. Deprioritize the returned mempool tx
# (same trick as simple_reorg) so the replacement blocks don't just
# re-confirm it.
bitcoind.rpc.invalidateblock(bitcoind.rpc.getblockhash(deposit_height))
memp = bitcoind.rpc.getrawmempool()
assert txid in memp
for t in memp:
bitcoind.rpc.prioritisetransaction(t, None, -1000000)
bitcoind.generate_block(2)
> l1.daemon.wait_for_log(r'Reorg detected', timeout=60)
```
Changelog-None
When an important plugin dies, lightningd shuts down while other plugins
may still be mid-sync-RPC during init. If rpc_open() fails because the
RPC socket is already gone (logging "Could not connect ... Connection
refused"), sync_req() proceeds with sync_fd == -1, and the subsequent
read(-1) fails with EBADF, producing the BROKEN message:
Reading sync lightningd: Bad file descriptor
This is the same intentional shutdown that already produces the
whitelisted "Reading sync lightningd: Connection reset by peer" (or a
clean EOF exit); the errno merely differs by the connection race. Add
"Bad file descriptor" to the broken_log whitelist so the test no longer
fails at teardown on this race.
Changelog-None
json_add_string copies its value, so the strings fmt_bitcoin_blkid
allocates in json_block_processed and json_getwatchmanheight are
referenced by nothing once the call returns. They are parented to
the response stream and freed with it, but the memleak scanner works
by searching memory for pointers to each allocation: when a dev
memleak check races an in-flight response, the unreferenced string is
reported as a leak and fails the test run (seen in a liquid CI run of
test_bwatch_add_watch_creates_datastore_entry). Parent them to
tmpctx, the idiom used elsewhere.
Fixes: #9362
Changelog-None
Shellcheck 0.11 (SC2268) rejects the old x"$1" comparison idiom.
Prevents: "tests/plugins/compacter-slow.sh:5:6: note: Avoid x-prefix in comparisons as it no longer serves a purpose. [SC2268]"
Shellcheck 0.11 (SC2268) rejects the old x"$1" comparison idiom.
Prevents: "tests/plugins/compacter-slow.sh:5:6: note: Avoid x-prefix in comparisons as it no longer serves a purpose. [SC2268]"
Fix test_explain_source_dest_failure by increasing the amounts by a
factor of ten, which reduces the relative significance of on-chain fees
on the commitment transaction.
This used to fail on liquid-regtest due to insufficient liquidity on
l2->l4 to prevent channel starvation.
At line
```
l1.rpc.xpay(l4.rpc.invoice('30000sat', 'test_explain_simple_failures2', 'test_explain_simple_failures2')['bolt11'])
```
we would get
```
lightningd-2 2026-07-06T11:29:09.973Z DEBUG 02287bfac8b99b35477ebe9334eede1e32b189e24644eb701c079614712331cec0-channeld-chan#3: Adding HTLC would leave us only 19454000msat: we need 25281sat for another HTLC if fees increase from 7500perkw to 13906perkw
lightningd-2 2026-07-06T11:29:09.973Z DEBUG 02287bfac8b99b35477ebe9334eede1e32b189e24644eb701c079614712331cec0-channeld-chan#3: Adding HTLC 0 amount=30000000msat cltv=130 gave CHANNEL_ERR_CHANNEL_CAPACITY_EXCEEDED
```
Changelog-None
Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
This is an undocumented interface, so we can just change it.
Rename "recurrence_label" to the more general "label", now we don't
require it to find previous payments.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
We don't actually need to enforce this check here: we can make that
the users' responsibility. This simplifies our work quite a lot,
since createinvoicerequest won't have to do a lookup any more.
This can be done by the repeatpay plugin itself.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This mirrors the previous commit, where we did it for recurring offers.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Fixed: Offers: we set a 10 minute expiry when we create invoices for offers in other currencies.
Consider the case in which payment fails due to not enough "known enabled"
liquidity. Notice that we cover the "known" and "enabled" cases already.
But:
known_enabled <= enabled
and
known_enabled <= known
Changelog-None
Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
Add PAY_INSUFFICIENT_FUNDS and PAY_ROUTE_NOT_FOUND, and give nice
detailed errors for those.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Changed: JSON-RPC: `getroutes` can now return PAY_INSUFFICIENT_FUNDS (215) and PAY_DESTINATION_INSUFFICIENT_CAPACITY (220) error codes.
It diagnoses if the *total capacity* of the source/dest are
insufficient, but not if the *known capacity* is.
So we get:
The shortest path is 103x1x0->105x1x0, but 103x1x0/1 layer auto.localchans says max is 77704899msat
Whereas it would be better to do:
We know from auto.localchans that source has maximum capacity xxx msat (in 1 channels)
Similarly for the destination, we get:
The shortest path is 103x1x0->105x1x0, but 103x1x0/1 layer auto.localchans says max is 77704899msat
on trim_constraints:
to be sure we don't miss elements we add to the hash table after the
loop and not during iteration.
Changelog-None
Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
On deletion of individual channel intel entries we need to free the
pointer inside the structure.
Changelog-None
Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
Pure "constraints" don't care about order (they simply clamp max and
min), but "impressions" are relative, so they do. Change the
hashtable to keep them timestamp sorted.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
channeld_fakenet sudbdaemon purposedly crafted for this test needs
updating to account for funds moving after every success payment.
I've tried to fix that but in doing so I also triggered an xpay bug not
related to this PR. Therefore, for the moment we skip this test.
Changelog-None
Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
Normal constraints are clamps on min/max caused by failed payments:
min for the channels that succeeded, max for the channel which failed.
Impressions are the results of successful payments, which alter both
min and max (negatively in the forward direction, positively in the
reverse).
impression: n
1. An effect, feeling, or image retained as a consequence of experience.
2. A vague notion, remembrance, or belief.
3. A mark produced on a surface by pressure.
Unlike constraints, this is the result of our own effect on the network: they're related
but different enough to get their own API and terminology.
The name conveys both we made an impression on the channel, and that
the results are a bit vague (due to other changes since then, which we
won't know about).
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Added: JSON-RPC: `askrene` layers now contain "impressions" representing the effects of successful payments we made through channels.
The prior implementation could read past the end of the buffer (we actually
pad our JSON so this isn't harmful, but still). Fix up json_to_s64 and
json_to_double too, but since they're not used as often, just copy the
string there.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
In particular, "struct jsonrpc_request"'s id is always a string.
cmd->id isn't, though.
We can also remove the now-unused json_get_id.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Following the recent enforcement of ids being printable strings
we put guards on "method" and "prefix" as well.
Changelog-None
Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
We used to handle it being a literal, but this was removed in
73fc9b0c2a (v25.05) so we don't need to handle that at all.
Not using the raw JSON means we handle weird methodnames by replacement: otherwise we would
not match the responses. Only an issue for commando, where the command would time out
rather than report "Unknown method".
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Shades of `efacada7dd` which did the same thing in multifundchannel:
(ab)used the id, which being a string, gave and id of 34 (").
Also clean up the leftover assert in multifundchannel.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
It's not okay to call free() on the pointer to a truncated log message
that was allocated by tal_fmt() in cap_header(). Let's call tal_free()
instead, and rather than calling vasprintf() to malloc the log message in
the first place, let's call tal_vfmt().
Also, since we're now always using a tallocated string for the log message,
let's have cap_header() take ownership of it and either free it if it
truncated the message (and is returning a different pointer to the truncated
message) or else return the taken original pointer without freeing it.
Also, log_io()'s str and data parameters are marked TAKES, but the function
was not actually taking them, so fix that up too.
Also, don't call strlen() on a string returned by tal_fmt(). The returned
pointer is guaranteed to have tal_count() == strlen() + 1, so there's no
sense in scanning through the string to find its length.
Suggested-by: Lagrang3 <lagrang3@protonmail.com>
See: https://github.com/ElementsProject/lightning/pull/9331#discussion_r3628600315
Changelog-Fixed: log: don't crash when truncating large log messages
We were using vasprintf to generate the log line and then using free to
deallocate the string. However, in the case of a very long log line
a new pointer was created with tal_fmt and then tried to use free on it.
This was introduced in commit: 4d8f923a9a
```
free(): invalid pointer
lightningd: FATAL SIGNAL 6 (version v26.06-21-gebc5dc2)
0x563dab20be43 send_backtrace
common/daemon.c:38
0x563dab20becd crashdump
common/daemon.c:83
0x7f0cf0c96def ???
./signal/../sysdeps/unix/sysv/linux/x86_64/libc_sigaction.c:0
0x7f0cf0ceb95c __pthread_kill_implementation
./nptl/pthread_kill.c:44
0x7f0cf0c96cc1 __GI_raise
../sysdeps/posix/raise.c:26
0x7f0cf0c7f4ab __GI_abort
./stdlib/abort.c:77
0x7f0cf0c80290 __libc_message_impl
../sysdeps/posix/libc_fatal.c:134
0x7f0cf0cf5464 malloc_printerr
./malloc/malloc.c:5832
0x7f0cf0cfa41b _int_free_check
./malloc/malloc.c:4560
0x7f0cf0cfa41b _int_free
./malloc/malloc.c:4692
0x7f0cf0cfa41b __GI___libc_free
./malloc/malloc.c:3476
0x563dab19be60 logv
lightningd/log.c:688
0x563dab19c0bc log_
lightningd/log.c:728
0x563dab1c1047 plugin_log_handle
lightningd/plugin.c:530
0x563dab1c5801 plugin_notification_handle
lightningd/plugin.c:609
0x563dab1c5a9a plugin_read_json
lightningd/plugin.c:753
0x563dab2388a5 next_plan
ccan/ccan/io/io.c:60
0x563dab238c83 do_plan
ccan/ccan/io/io.c:422
0x563dab238d3c io_ready
ccan/ccan/io/io.c:439
0x563dab239e5b io_loop
ccan/ccan/io/poll.c:470
0x563dab194399 io_loop_with_timers
lightningd/io_loop_with_timers.c:22
0x563dab199c29 main
lightningd/lightningd.c:1480
0x7f0cf0c80ca7 __libc_start_call_main
../sysdeps/nptl/libc_start_call_main.h:58
0x7f0cf0c80d64 __libc_start_main_impl
../csu/libc-start.c:360
0x563dab169020 ???
_start+0x20:0
0xffffffffffffffff ???
???:0
```
Changelog-None
Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
The graceful command notifies watchers about the closest-expiry HTLC,
including its state, whenever that message changes. But nothing
re-evaluated the message when an HTLC changed state: only HTLC
removal, peer disconnect and another graceful invocation re-ran the
check. If graceful was invoked while a commitment dance was in
flight, the initial notification named a transient state (e.g.
RCVD_ADD_REVOCATION) and no follow-up ever announced the settled
state.
test_graceful_htlc waits for exactly that follow-up (since 6994681ae
"flake: Fix test_graceful_htlc to be flexible for notifs"), so it
times out whenever graceful catches the dance mid-flight, which
valgrind CI runs make likely. In one CI failure graceful caught the
outgoing HTLC in RCVD_ADD_REVOCATION, the dance completed 600ms
later, and no notification followed for the remaining 180 seconds.
Re-check graceful progress in the handlers that advance HTLC states
(peer_sending_commitsig, peer_got_commitsig, peer_got_revoke). The
check is a no-op unless a graceful command is outstanding, and
identical messages are already deduplicated.
The dance now generates transient-state notifications, so rewrite the
test to match expected notifications as an ordered subsequence
instead of by exact index. That also removes two accidents the old
indexing depended on: the RCVD_ADD_REVOCATION special case (the
settled state now always notifies), and the wait for a notification
after l1's disconnect, which was really satisfied by the graceful(1)
call's own initial notification arriving on the shared rpc socket --
the disconnect itself never notifies, since the message text
describes the still-connected peer l3 and does not change.
Fixes: https://github.com/ElementsProject/lightning/issues/9219
Changelog-Fixed: JSON-RPC: `graceful` notifications now update when a pending HTLC changes state.