Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
e330d3d
pytest: test for blockheight entries in bookkeeper being saved across…
rustyrussell Oct 31, 2025
50c9d04
bookkeeper: fix restoration of derived wallet blockheights on restart.
rustyrussell Oct 31, 2025
12ac901
pytest: test for askrene infinite loop with maxparts set.
rustyrussell Oct 30, 2025
409471b
askrene: fix infinite loop if refine_flows() cuts down our last flow …
rustyrussell Oct 30, 2025
316cf0a
askrene: implement 10-second deadline.
rustyrussell Oct 31, 2025
855fe63
libplugin: remove redundant destructor which causes exponential slowd…
rustyrussell Oct 17, 2025
3546b7e
plugins/sql: add payment_hash index to channelmoves table.
rustyrussell Oct 17, 2025
83f2497
bookkeeper: only read listchannelmoves 1000 entries at a time.
rustyrussell Oct 17, 2025
4489f64
tools: Read the correct default-key from gpgconf
ShahanaFarooqui Oct 15, 2025
fb68a2e
contrib: Added fixed SOURCE_DATE_EPOCH flag for reproducible ubuntu b…
ShahanaFarooqui Sep 12, 2025
0abac58
ci: Add a new step to update pyln versions out of WORKDIR
ShahanaFarooqui Sep 2, 2025
9a6c53c
pytest: test for parallel bookkeeper queries.
rustyrussell Nov 3, 2025
37dcaf5
bookkeeper: fix assert() which happens with parallel queries.
rustyrussell Nov 3, 2025
a3b5571
common: remove tal_check() call on libwally allocations.
rustyrussell Oct 28, 2025
e1e729a
pytest: test for bcli crash with huge PSBTs.
rustyrussell Nov 3, 2025
b8297ba
plugins/bcli: use -stdin to feed arguments, in case we have a giant tx.
rustyrussell Nov 3, 2025
108e2fa
pytest: test that we correctly mark a payment part failed if we canno…
rustyrussell Oct 26, 2025
d67092b
lightningd: fix case where injectpaymentonion failure results in list…
rustyrussell Nov 3, 2025
d996522
bookkeeper: don't flood logs if we have many channelmoves all at once.
rustyrussell Nov 4, 2025
aed69e6
sql: only create sql indices after initial load of data.
rustyrussell Nov 4, 2025
ababb41
included a changelog
madelinevibes Nov 4, 2025
8ab968e
updated the versions
madelinevibes Nov 4, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions .github/workflows/pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,13 @@ jobs:
- name: Install uv
uses: astral-sh/setup-uv@v5

- name: Update pyln versions
id: update-versions
run: |
export VERSION=$(git describe --tags --abbrev=0)
echo "Pyln VERSION: $VERSION"
make update-pyln-versions NEW_VERSION=$VERSION

- name: Publish distribution 📦 to Test PyPI
if: github.repository == 'ElementsProject/lightning' && steps.set-values.outputs.DISTLOCATION == 'test'
env:
Expand All @@ -84,10 +91,6 @@ jobs:
WORKDIR: ${{ matrix.WORKDIR }}
run: |
echo "UV VERSION PUBLISH: $(uv --version)"
cd ${{ env.WORKDIR }}
export VERSION=$(git describe --tags --abbrev=0)
echo "Pyln VERSION: $VERSION"
make update-pyln-versions NEW_VERSION=$VERSION
cd /github/workspace
uv build --package ${{ matrix.PACKAGE }}
uv publish
2 changes: 1 addition & 1 deletion .version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
25.09.1
25.09.2
37 changes: 37 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,43 @@
All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## [25.09.2] - 2025-11-04: "Hot Wallet Guardian III"

`Bookkeeper` and `xpay` users: please upgrade!
This point release includes fixes for `xpay`, `bookkeeper` and optimizations for large nodes using `bookkeeper`.

### Changed

- plugins: the sql plugin now keeps an index on `channelmoves` by `payment_hash`. ([#8618])
 - plugins: `bookkeeper` reduced logging for large imports to increase speed. ([#8657])
 - plugins: `sql` initial load for tables is much faster (e.g 82 to 17 seconds for very large channelmoves table). ([#8657])

### Fixed

- Core lightning builds for Ubuntu Focal, Jammy and Noble are deterministic again. ([#8547])
 - Reproducible build for Ubuntu noble by updating sqlite3 version and shasums. ([#8551])
- plugins: bookkeeper first invocation after migration from prior to 25.09 with very large databases will not crash. ([#8618])
- `xpay` would sometimes leave payment parts status `pending` in failure cases (as seen in listpays or listsendpays). ([#8635])
- Plugins: `askrene` could enter an infinite loop when maxparts is restricted. ([#8636])
- plugins: `bcli` would fail with "Argument list too long" when sending a giant tx. ([#8639])
- JSON-RPC: Dealing with giant PSBTs (700 inputs!) is now much faster. ([#8639])
- plugins: assertion crash in bookkeeper when fresh records arrive while multiple queries in progress. ([#8642])
- Plugins: `bookkeeper` now correctly restores chain event blockheights it has derived. ([#8649])

[#8529]: https://github.com/ElementsProject/lightning/pull/8529
[#8547]: https://github.com/ElementsProject/lightning/pull/8547
[#8551]: https://github.com/ElementsProject/lightning/pull/8551
[#8607]: https://github.com/ElementsProject/lightning/pull/8607
[#8618]: https://github.com/ElementsProject/lightning/pull/8618
[#8635]: https://github.com/ElementsProject/lightning/pull/8635
[#8636]: https://github.com/ElementsProject/lightning/pull/8636
[#8639]: https://github.com/ElementsProject/lightning/pull/8639
[#8642]: https://github.com/ElementsProject/lightning/pull/8642
[#8649]: https://github.com/ElementsProject/lightning/pull/8649
[#8657]: https://github.com/ElementsProject/lightning/pull/8657
[25.09.2]: https://github.com/ElementsProject/lightning/releases/tag/v25.09.2

## [25.09.1] - 2025-10-15: "Hot Wallet Guardian II"

Several important fixes, please upgrade!
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ RUST_TARGET_DIR = target/$(TARGET)/$(RUST_PROFILE)
endif

ifneq ($(RUST_PROFILE),debug)
CARGO_OPTS := --profile=$(RUST_PROFILE) --quiet
CARGO_OPTS := --profile=$(RUST_PROFILE) --locked --quiet
else
CARGO_OPTS := --quiet
endif
Expand Down
1 change: 0 additions & 1 deletion common/setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
static void *cln_wally_tal(size_t size)
{
assert(wally_tal_ctx);
assert(tal_check(wally_tal_ctx, "cln_wally_tal ctx check"));
return tal_arr_label(wally_tal_ctx, u8, size, "cln_wally_tal");
}

Expand Down
2 changes: 1 addition & 1 deletion contrib/pyln-client/pyln/client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from .gossmapstats import GossmapStats
from .version import NodeVersion

__version__ = "25.09.1"
__version__ = "25.09.2"

__all__ = [
"LightningRpc",
Expand Down
2 changes: 1 addition & 1 deletion contrib/pyln-client/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "pyln-client"
version = "25.09.1"
version = "25.09.2"
description = "Client library and plugin library for Core Lightning"
authors = [{ name = "Christian Decker", email = "decker.christian@gmail.com" }]
license = { text = "BSD-MIT" }
Expand Down
2 changes: 1 addition & 1 deletion contrib/pyln-proto/pyln/proto/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from .onion import OnionPayload, TlvPayload, LegacyOnionPayload
from .wire import LightningConnection, LightningServerSocket

__version__ = "25.09.1"
__version__ = "25.09.2"

__all__ = [
"Invoice",
Expand Down
2 changes: 1 addition & 1 deletion contrib/pyln-proto/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "pyln-proto"
version = "25.09.1"
version = "25.09.2"
description = "This package implements some of the Lightning Network protocol in pure python. It is intended for protocol testing and some minor tooling only. It is not deemed secure enough to handle any amount of real funds (you have been warned!)."
authors = [
{name = "Christian Decker", email = "decker.christian@gmail.com"}
Expand Down
2 changes: 1 addition & 1 deletion contrib/pyln-testing/pyln/testing/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = "25.09.1"
__version__ = "25.09.2"

__all__ = [
"__version__",
Expand Down
2 changes: 1 addition & 1 deletion contrib/pyln-testing/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "pyln-testing"
version = "25.09.1"
version = "25.09.2"
description = "Test your Core Lightning integration, plugins or whatever you want"
authors = [{ name = "Christian Decker", email = "decker.christian@gmail.com" }]
license = { text = "BSD-MIT" }
Expand Down
1 change: 1 addition & 0 deletions contrib/reprobuild/Dockerfile.focal
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ FROM focal

ENV TZ=UTC
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
ENV SOURCE_DATE_EPOCH=1672531200
ENV RUST_PROFILE=release
ENV PATH=/root/.pyenv/shims:/root/.pyenv/bin:/root/.cargo/bin:/root/.local/bin:$PATH
ENV PROTOC_VERSION=29.4
Expand Down
1 change: 1 addition & 0 deletions contrib/reprobuild/Dockerfile.jammy
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ FROM jammy

ENV TZ=UTC
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
ENV SOURCE_DATE_EPOCH=1672531200
ENV RUST_PROFILE=release
ENV PATH=/root/.pyenv/shims:/root/.pyenv/bin:/root/.cargo/bin:/root/.local/bin:$PATH
ENV PROTOC_VERSION=29.4
Expand Down
1 change: 1 addition & 0 deletions contrib/reprobuild/Dockerfile.noble
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ FROM ubuntu:noble

ENV TZ=UTC
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
ENV SOURCE_DATE_EPOCH=1672531200
ENV RUST_PROFILE=release
ENV PATH=/root/.pyenv/shims:/root/.pyenv/bin:/root/.cargo/bin:/root/.local/bin:$PATH
ENV PROTOC_VERSION=29.4
Expand Down
4 changes: 4 additions & 0 deletions doc/lightningd-config.5.md
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,10 @@ command, so they invoices can also be paid onchain.

Setting this makes `xpay` wait until all parts have failed/succeeded before returning. Usually this is unnecessary, as xpay will return on the first success (we have the preimage, if they don't take all the parts that's their problem) or failure (the destination could succeed another part, but it would mean it was only partially paid). The default is `false`.

* **askrene-timeout**=*SECONDS* [plugin `askrene`, *dynamic*]

This option makes the `getroutes` call fail if it takes more than this many seconds. Setting it to zero is a fun way to ensure your node never makes payments.

### Networking options

Note that for simple setups, the implicit *autolisten* option does the
Expand Down
26 changes: 14 additions & 12 deletions lightningd/pay.c
Original file line number Diff line number Diff line change
Expand Up @@ -2080,19 +2080,11 @@ static struct command_result *json_injectpaymentonion(struct command *cmd,
if (command_check_only(cmd))
return command_check_done(cmd);

register_payment_and_waiter(cmd,
payment_hash,
*partid, *groupid,
*destination_msat, *msat, AMOUNT_MSAT(0),
label, invstring, local_invreq_id,
&shared_secret,
destination);

/* If unknown, we set this equal (so accounting logs 0 fees) */
if (amount_msat_eq(*destination_msat, AMOUNT_MSAT(0)))
*destination_msat = *msat;
failmsg = send_htlc_out(tmpctx, next, *msat,
*cltv, *destination_msat,
*cltv,
/* If unknown, we set this equal (so accounting logs 0 fees) */
amount_msat_eq(*destination_msat, AMOUNT_MSAT(0))
? *msat : *destination_msat,
payment_hash,
next_path_key, NULL, *partid, *groupid,
serialize_onionpacket(tmpctx, rs->next),
Expand All @@ -2102,6 +2094,16 @@ static struct command_result *json_injectpaymentonion(struct command *cmd,
"Could not send to first peer: %s",
onion_wire_name(fromwire_peektype(failmsg)));
}

/* Now HTLC is created, we can add the payment as pending */
register_payment_and_waiter(cmd,
payment_hash,
*partid, *groupid,
*destination_msat, *msat, AMOUNT_MSAT(0),
label, invstring, local_invreq_id,
&shared_secret,
destination);

return command_still_pending(cmd);
}

Expand Down
24 changes: 19 additions & 5 deletions plugins/askrene/askrene.c
Original file line number Diff line number Diff line change
Expand Up @@ -613,13 +613,15 @@ static struct command_result *do_getroutes(struct command *cmd,
/* Compute the routes. At this point we might select between multiple
* algorithms. Right now there is only one algorithm available. */
struct timemono time_start = time_mono();
struct timemono deadline = timemono_add(time_start,
time_from_sec(askrene->route_seconds));
if (info->dev_algo == ALGO_SINGLE_PATH) {
err = single_path_routes(rq, rq, srcnode, dstnode, info->amount,
err = single_path_routes(rq, rq, deadline, srcnode, dstnode, info->amount,
info->maxfee, info->finalcltv,
info->maxdelay, &flows, &probability);
} else {
assert(info->dev_algo == ALGO_DEFAULT);
err = default_routes(rq, rq, srcnode, dstnode, info->amount,
err = default_routes(rq, rq, deadline, srcnode, dstnode, info->amount,
info->maxfee, info->finalcltv,
info->maxdelay, &flows, &probability);
}
Expand Down Expand Up @@ -1301,7 +1303,8 @@ static const char *init(struct command *init_cmd,
const char *buf UNUSED, const jsmntok_t *config UNUSED)
{
struct plugin *plugin = init_cmd->plugin;
struct askrene *askrene = tal(plugin, struct askrene);
struct askrene *askrene = get_askrene(plugin);

askrene->plugin = plugin;
list_head_init(&askrene->layers);
askrene->reserved = new_reserve_htable(askrene);
Expand All @@ -1327,7 +1330,18 @@ static const char *init(struct command *init_cmd,

int main(int argc, char *argv[])
{
struct askrene *askrene;
setup_locale();
plugin_main(argv, init, NULL, PLUGIN_RESTARTABLE, true, NULL, commands, ARRAY_SIZE(commands),
NULL, 0, NULL, 0, NULL, 0, NULL);

askrene = tal(NULL, struct askrene);
askrene->route_seconds = 10;
plugin_main(argv, init, take(askrene), PLUGIN_RESTARTABLE, true, NULL, commands, ARRAY_SIZE(commands),
NULL, 0, NULL, 0, NULL, 0,
plugin_option_dynamic("askrene-timeout",
"int",
"How many seconds to try before giving up on calculating a route."
" Defaults to 10 seconds",
u32_option, u32_jsonfmt,
&askrene->route_seconds),
NULL);
}
2 changes: 2 additions & 0 deletions plugins/askrene/askrene.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ struct askrene {
struct node_id my_id;
/* Aux command for layer */
struct command *layer_cmd;
/* How long before we abort trying to find a route? */
u32 route_seconds;
};

/* Information for a single route query. */
Expand Down
38 changes: 27 additions & 11 deletions plugins/askrene/mcf.c
Original file line number Diff line number Diff line change
Expand Up @@ -1345,6 +1345,7 @@ static bool check_htlc_max_limits(struct route_query *rq, struct flow **flows)
*/
static const char *
linear_routes(const tal_t *ctx, struct route_query *rq,
struct timemono deadline,
const struct gossmap_node *srcnode,
const struct gossmap_node *dstnode, struct amount_msat amount,
struct amount_msat maxfee, u32 finalcltv, u32 maxdelay,
Expand Down Expand Up @@ -1376,6 +1377,14 @@ linear_routes(const tal_t *ctx, struct route_query *rq,

while (!amount_msat_is_zero(amount_to_deliver)) {
size_t num_parts, parts_slots, excess_parts;
u32 bottleneck_idx;

if (timemono_after(time_mono(), deadline)) {
error_message = rq_log(ctx, rq, LOG_BROKEN,
"%s: timed out after deadline",
__func__);
goto fail;
}

/* FIXME: This algorithm to limit the number of parts is dumb
* for two reasons:
Expand Down Expand Up @@ -1423,7 +1432,7 @@ linear_routes(const tal_t *ctx, struct route_query *rq,
}

error_message =
refine_flows(ctx, rq, amount_to_deliver, &new_flows);
refine_flows(ctx, rq, amount_to_deliver, &new_flows, &bottleneck_idx);
if (error_message)
goto fail;

Expand Down Expand Up @@ -1458,14 +1467,19 @@ linear_routes(const tal_t *ctx, struct route_query *rq,
excess_parts = 1;
} else
excess_parts = 0;
if (excess_parts > 0 &&
!remove_flows(&new_flows, excess_parts)) {
error_message = rq_log(ctx, rq, LOG_BROKEN,
"%s: failed to remove %zu"
" flows from a set of %zu",
__func__, excess_parts,
tal_count(new_flows));
goto fail;
if (excess_parts > 0) {
/* If we removed all the flows we found, avoid selecting them again,
* by disabling one. */
if (excess_parts == tal_count(new_flows))
bitmap_set_bit(rq->disabled_chans, bottleneck_idx);
if (!remove_flows(&new_flows, excess_parts)) {
error_message = rq_log(ctx, rq, LOG_BROKEN,
"%s: failed to remove %zu"
" flows from a set of %zu",
__func__, excess_parts,
tal_count(new_flows));
goto fail;
}
}

/* Is this set of flows too expensive?
Expand Down Expand Up @@ -1634,25 +1648,27 @@ linear_routes(const tal_t *ctx, struct route_query *rq,
}

const char *default_routes(const tal_t *ctx, struct route_query *rq,
struct timemono deadline,
const struct gossmap_node *srcnode,
const struct gossmap_node *dstnode,
struct amount_msat amount, struct amount_msat maxfee,
u32 finalcltv, u32 maxdelay, struct flow ***flows,
double *probability)
{
return linear_routes(ctx, rq, srcnode, dstnode, amount, maxfee,
return linear_routes(ctx, rq, deadline, srcnode, dstnode, amount, maxfee,
finalcltv, maxdelay, flows, probability, minflow);
}

const char *single_path_routes(const tal_t *ctx, struct route_query *rq,
struct timemono deadline,
const struct gossmap_node *srcnode,
const struct gossmap_node *dstnode,
struct amount_msat amount,
struct amount_msat maxfee, u32 finalcltv,
u32 maxdelay, struct flow ***flows,
double *probability)
{
return linear_routes(ctx, rq, srcnode, dstnode, amount, maxfee,
return linear_routes(ctx, rq, deadline, srcnode, dstnode, amount, maxfee,
finalcltv, maxdelay, flows, probability,
single_path_flow);
}
2 changes: 2 additions & 0 deletions plugins/askrene/mcf.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ struct amount_msat linear_flow_cost(const struct flow *flow,
/* A wrapper to the min. cost flow solver that actually takes into consideration
* the extra msats per channel needed to pay for fees. */
const char *default_routes(const tal_t *ctx, struct route_query *rq,
struct timemono deadline,
const struct gossmap_node *srcnode,
const struct gossmap_node *dstnode,
struct amount_msat amount,
Expand All @@ -73,6 +74,7 @@ const char *default_routes(const tal_t *ctx, struct route_query *rq,

/* A wrapper to the single-path constrained solver. */
const char *single_path_routes(const tal_t *ctx, struct route_query *rq,
struct timemono deadline,
const struct gossmap_node *srcnode,
const struct gossmap_node *dstnode,
struct amount_msat amount,
Expand Down
Loading
Loading