marginal_feerate() computed current_feerate * 1.1 as a double and
converted the result back to u32. Since current_feerate is chosen by
the peer in open_channel or update_fee, they could choose an absurdly
high value that overflows u32 after the computation. UBSan reports:
common/fee_states.c:179:10: runtime error: 4.72446e+09 is outside the range of representable values of type 'unsigned int'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior common/fee_states.c:179:10
Do the arithmetic with u64 and saturate at UINT32_MAX to avoid the
undefined behavior.
Found by fuzzing with smite.
Changelog-Fixed: JSON-RPC: `listpeerchannels` no longer derives `receivable_msat` from an overflowed fee estimate when the peer sets an absurd `feerate_per_kw`.
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.
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>
On macOS under load, socketpair fds sent via SCM_RIGHTS can have
O_NONBLOCK set (from the sender's io_new_conn call in hsmd's
pass_client_hsmfd). This causes wire_sync_read to return NULL
with EAGAIN, killing connectd or channeld with "No hsmd ECDH response".
The fix mirrors the "Don't trust subd to set it blocking" pattern
already used in lightningd/subd.c:read_fds(): explicitly call
io_fd_block(hsm_fd, true) in ecdh_hsmd_setup.
Changelog-Fixed: connectd: fix intermittent "No hsmd ECDH response" crash on macOS under load.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previous commit stopped trace ids consuming the deterministic
randbytes stream, but tracing still touched it once: span_key_hash()
calls siphash_seed(), whose lazy init draws 16 bytes from the stream.
Since randbytes call sites are assigned their offset blocks in
first-use order, that single draw shifts every later draw in the
process by 1000 when tracing is active (HAVE_USDT builds, as in CI):
keysend preimages and invoicerequest payer keys then differ from a
build with tracing disabled, showing up as an 11-file doc-examples
diff.
Use a cheap multiplicative hash for span keys when the deterministic
override is active, so tracing touches the RNG stream zero times.
Trace/span ids now come from a plain counter when CLN_DEV_ENTROPY_SEED is active, so tracing (which CI's HAVE_USDT=1 build enables by default) no longer shifts the deterministic RNG stream that PSBT serial ids and locktime fuzz are drawn from.
dev_override_randbytes() hashes argv0 into the CLN_DEV_ENTROPY_SEED
stream, so every plugin and subdaemon gets a distinct seed. But
lightningd execs them with absolute paths, so the "deterministic"
stream silently depended on where the source tree was checked out:
canned blocks generated in one directory would not replay in another
(funding tx output order and nlocktime fuzz diverge, so txids change).
This is why check-doc-examples passed locally but failed in CI.
Hash only the basename: binaries still get distinct seeds, but the
stream no longer depends on the checkout path.
Only in developer mode, ofc.
Notes:
1. We have to move the initialization before the lightningd main trace_start,
since that uses pseudorand().
2. To make the results stable, we need to use per-caller values to randbytes().
Otherwise external timing changes the call order.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
New subdaemon implementing the BOLT2 simple close protocol, replacing `lightning_closingd` when `option_simple_close` is negotiated:
- Each peer independently sends `closing_complete` with their fee proposal;
- The other side signs it and sends `closing_sig`;
- Both sides broadcast two conflicting closing transactions and whichever confirms first wins.
Key protocol details:
- Closer pays the fee and closee receives their exact channel balance;
- TLV variants selected per BOLT2: `closer_output_only`, `closer_and_closee_outputs`, `closee_output_only`;
- Sequence 0xFFFFFFFD enables RBF via re-sending `closing_complete`;
- Script mismatch on `closee_scriptpubkey` warns and fails to reconnect;
common/shutdown_scriptpubkey.h/c: removed `static` from `is_valid_op_return` so it can be used in `simpleclosed.c`
Adds the BOLT3 simple closing transaction builder:
- sequence 0xFFFFFFFD (RBF-signalling)
- locktime from closing_complete
- closer pays fee (their output is reduced)
- dust outputs are omitted and a zero-value OP_RETURN is used when both outputs would be dust.
features.h: Reserves bits 60/61 for `option_simple_close` per BOLT2.
features.c: Add `OPT_SIMPLE_CLOSE` to `feature_styles[]` and declare the correct `feature_name`.
tests/test_closing.py: Update options on `test_simple_close_...` to include `{experimental-simple-close: None}`.
lightningd/options.c: Register noarg option for simple close.
doc/schemas/listconfigs.json: add `experimental-simple-close` to config targeting v26.08.
doc/lightningd-config.5.md: Specify `experimental-simple-close` option.
contrib/pyln-testing/pyln/testing/utils.py: Allow setting `EXPERIMENTAL_SIMPLE_CLOSE` on tests.
Generated files after modifying sources.
Rename splice→splice_init, relative_satoshis→funding_contribution_satoshis,
txsigs_tlvs→tx_signatures_tlvs, and splice_info/batch_info→funding_txid/message_type.
Consolidate three CLN-specific shim patches into extracted_peer_cln_batch_element.patch.
Changelog-None
Reverts commits b0e728572b through aa6ecad90e (11 commits).
This PR was accidentally merged without proper review. The `channeld.c` splice-detection logic (`is_splice_active` + txid comparison) was NACKed by @ddustin as deviating from the spec, the correct approach requires a `funding_tx_index` on the inflight rather than a `txid` comparison.
A clean replacement PR will be opened for discussion once the approach is agreed upon.
Changelog-None
new cases added by autogenerated mocks after updating the default BOLT spec version.
bolt12.c: add new rules to satisfy case `offer_chains with zero entries`.
bech32_util.c: fix padding on `from_bech32_charset` to satisfy case `Bech32 padding exceeds 4-bit limit`
bolt12.c:
- quote update on `bolt12_chains_match`;
- add a new condition to `offer_decode` to check if the amount is greater than 0;
run-bolt-12-encode-test.c: add check for `offer_amount > 0`
run-bolt12-format-string-test.c: update BOLT quote
bolt12-cli.c: update BOLT quote
common/bolt11.c: Some BOLT quotes had changed slightly.
run-bolt11.c: A test was updated to reflect nomenclature changes (from `signature recovery` to `public-key recovery`).
Calculate proof_merkle_root properly: we accidentally printed the
`invoice_merkle_root` again here.
Reported-by: @t-bast.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
These helper functions decode hex strings from JSON into big-endian 32-bit and 64-bit values, useful for parsing datastore entries exposing these into a more common space so they can be used by bwatch in the future.
We had an assert(!(ot->type & OPT_MULTI)) which crashed when using
setconfig on a plugin option marked as both dynamic and multi.
The fix changes plugin_set_dynamic_opt to accept an array of values
(scalar options pass a 1-element array, multi options pass the complete
set). For multi options, setconfig replaces ALL values atomically - an
empty array clears them.
Fixes: #8295
Changelog-Fixed: setconfig no longer crashes on dynamic multi-value plugin options
If you don't use complex pay options, you can use `invstring` now and when xpay
becomes pay, there's no transition.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Changed: JSON-RPC: `pay` now accepts `invstring` as a parameter name for `bolt11`, to ease transition when xpay takes over in v27.03.
These are useful for the common pattern of "append these bytes to this tal array".
As a bonus, we do memcheck() on all these callers, for extra checking
under valgrind.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-EXPERIMENTAL: JSON-RPC: `createproof` to create a payment proof for a (successful) BOLT12 payment.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Create canonical JSON test vectors, using SUPERVERBOSE. We remove the
ones from bolt12_merkle.c.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Claude helped debug, using the LDK implementation at first, then
modified heavily.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
If we have 5 fields, ilog64(5) is 3, and (1 << 3) is 8. Adding one
made it 16.
In fact, since ilog64(4) is also 3, we should *subtract one*, but
this handles 0 more neatly (ilog64(0) is defined to return 0, as
a special case).
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
And enhance some of our quotes to use `...` at the start to link them.
As they were, we didn't notice when a new requirement appeared in the
middle.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
There's a new test for bolt12 in commit 7153bed9705d7493 ("BOLT 12:
add test vector for invalid bech32 padding (#1312)") which requires us
to b stricter in decoding.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
It's a bit moot, since we can't pay them anyway, but this brings us into line
with the test vectors. It *did* catch that we treated empty as missing, though.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This means we can assume support, but we *can't* assume it's present, because
of keysend, which doesn't use it.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>