Skip to content

Commit a45c907

Browse files
committed
lightningd-downgrade: downgrade askrene's layer info too.
If they had a channel bias, and ran xpay, it will update the bias to a v2 bias (with a timestamp). We must downgrade that, or the older version won't load! Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
1 parent eb83a20 commit a45c907

File tree

3 files changed

+181
-9
lines changed

3 files changed

+181
-9
lines changed

tests/test_downgrade.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,26 @@
11
from fixtures import * # noqa: F401,F403
22
from utils import (
3-
TIMEOUT # noqa: F401
3+
TIMEOUT, # noqa: F401
4+
first_scid, only_one,
45
)
56

67
import os
78
import subprocess
89

910

1011
def test_downgrade(node_factory, executor):
11-
l1, l2 = node_factory.line_graph(2, opts={'may_reconnect': True})
12+
l1, l2 = node_factory.line_graph(2, opts={'may_reconnect': True}, wait_for_announce=True)
13+
14+
bias_scidd = f"{first_scid(l1, l2)}/0"
15+
# Create a bias for this channel.
16+
l1.rpc.askrene_bias_channel('xpay', bias_scidd, 1)
17+
bias = only_one(only_one(l1.rpc.askrene_listlayers('xpay')['layers'])['biases'])
18+
assert bias['short_channel_id_dir'] == bias_scidd
19+
assert bias['bias'] == 1
20+
21+
# Make a payment, which means we update layer information.
22+
old_inv = l2.rpc.invoice(1000, 'test_downgrade1', 'test_downgrade')
23+
l1.rpc.xpay(old_inv['bolt11'])
1224

1325
# From the binary:
1426
# ERROR_DBVERSION = 1
@@ -45,10 +57,19 @@ def test_downgrade(node_factory, executor):
4557

4658
l1.start()
4759

60+
# Disable schema checking here: the node is OLD!
61+
l1.rpc.jsonschemas = {}
62+
4863
# It should connect to l2 no problems, make payment.
4964
l1.connect(l2)
5065
inv = l2.rpc.invoice(1000, 'test_downgrade', 'test_downgrade')
5166
l1.rpc.xpay(inv['bolt11'])
67+
68+
# It should see the bias!
69+
bias = only_one(only_one(l1.rpc.askrene_listlayers('xpay')['layers'])['biases'])
70+
assert bias['short_channel_id_dir'] == bias_scidd
71+
assert bias['bias'] == 1
72+
5273
l1.stop()
5374
l1.daemon.executable = current_executable
5475

@@ -63,3 +84,8 @@ def test_downgrade(node_factory, executor):
6384
l1.connect(l2)
6485
inv2 = l2.rpc.invoice(1000, 'test_downgrade2', 'test_downgrade2')
6586
l1.rpc.xpay(inv2['bolt11'])
87+
88+
# bias still present
89+
bias = only_one(only_one(l1.rpc.askrene_listlayers('xpay')['layers'])['biases'])
90+
assert bias['short_channel_id_dir'] == bias_scidd
91+
assert bias['bias'] == 1

tools/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ tools/lightning-downgrade: \
2323
db/exec.o \
2424
db/bindings.o \
2525
db/utils.o \
26+
wallet/datastore.o \
2627
wallet/migrations.o \
28+
plugins/askrene/datastore_wire.o \
2729
$(DB_OBJS) \
2830
$(WALLET_DB_QUERIES:.c=.o) \
2931
tools/lightning-downgrade.o

tools/lightning-downgrade.c

Lines changed: 151 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,18 @@
77
#include <ccan/tal/path/path.h>
88
#include <ccan/tal/str/str.h>
99
#include <common/configdir.h>
10+
#include <common/node_id.h>
1011
#include <common/utils.h>
1112
#include <db/bindings.h>
1213
#include <db/common.h>
1314
#include <db/exec.h>
1415
#include <db/utils.h>
16+
#include <plugins/askrene/datastore_wire.h>
1517
#include <stdio.h>
1618
#include <unistd.h>
19+
#include <wallet/datastore.h>
1720
#include <wallet/migrations.h>
21+
#include <wire/wire.h>
1822

1923
#define ERROR_DBVERSION 1
2024
#define ERROR_DBFAIL 2
@@ -26,11 +30,144 @@
2630
struct db_version {
2731
const char *name;
2832
size_t db_height;
33+
const char *(*downgrade_datastore)(const tal_t *ctx, struct db *db);
2934
bool gossip_store_compatible;
3035
};
3136

37+
struct layer {
38+
const char **key;
39+
const u8 *data;
40+
};
41+
42+
static void copy_data(u8 **out, const u8 *in, size_t len)
43+
{
44+
size_t oldlen = tal_bytelen(*out);
45+
46+
tal_resize(out, oldlen + len);
47+
memcpy(*out + oldlen, in, len);
48+
}
49+
50+
/* askrene added DSTORE_CHANNEL_BIAS_V2 (convertable) and
51+
* DSTORE_NODE_BIAS (not convertable) */
52+
static const char *convert_layer_data(const tal_t *ctx,
53+
const char *layername,
54+
const u8 *data_in,
55+
const u8 **data_out)
56+
{
57+
size_t len = tal_bytelen(data_in);
58+
struct node_id n;
59+
struct short_channel_id scid;
60+
struct amount_msat msat, *msat_ptr;
61+
struct short_channel_id_dir scidd;
62+
bool *bool_ptr;
63+
u64 timestamp;
64+
u32 *u32_ptr;
65+
u16 *u16_ptr;
66+
s8 bias;
67+
const char *string;
68+
u8 *out = tal_arr(ctx, u8, 0);
69+
70+
/* Unfortunately, there are no explicit lengths, so we have
71+
* to read all records even if we don't care about them. */
72+
while (len != 0) {
73+
enum dstore_layer_type type;
74+
const u8 *olddata = data_in;
75+
type = fromwire_peektypen(data_in, len);
76+
77+
switch (type) {
78+
/* These are all simply digested and copied */
79+
case DSTORE_CHANNEL:
80+
if (fromwire_dstore_channel(&data_in, &len,
81+
&n, &n, &scid, &msat))
82+
copy_data(&out, data_in, olddata - data_in);
83+
continue;
84+
case DSTORE_CHANNEL_UPDATE:
85+
if (fromwire_dstore_channel_update(tmpctx, &data_in, &len,
86+
&scidd, &bool_ptr,
87+
&msat_ptr, &msat_ptr, &msat_ptr,
88+
&u32_ptr, &u16_ptr))
89+
copy_data(&out, data_in, olddata - data_in);
90+
continue;
91+
case DSTORE_CHANNEL_CONSTRAINT:
92+
if (fromwire_dstore_channel_constraint(tmpctx, &data_in, &len,
93+
&scidd, &timestamp,
94+
&msat_ptr, &msat_ptr))
95+
copy_data(&out, data_in, olddata - data_in);
96+
continue;
97+
case DSTORE_CHANNEL_BIAS:
98+
if (fromwire_dstore_channel_bias(tmpctx, &data_in, &len,
99+
&scidd, &bias,
100+
&string))
101+
copy_data(&out, data_in, olddata - data_in);
102+
continue;
103+
case DSTORE_DISABLED_NODE:
104+
if (fromwire_dstore_disabled_node(&data_in, &len, &n))
105+
copy_data(&out, data_in, olddata - data_in);
106+
continue;
107+
108+
/* Convert back, lose timestamp */
109+
case DSTORE_CHANNEL_BIAS_V2:
110+
if (fromwire_dstore_channel_bias_v2(tmpctx, &data_in, &len,
111+
&scidd, &bias,
112+
&string, &timestamp)) {
113+
towire_dstore_channel_bias(&out, &scidd, bias, string);
114+
}
115+
continue;
116+
117+
case DSTORE_NODE_BIAS:
118+
return "Askrene has a node bias, which is not supported in v25.09";
119+
}
120+
121+
return tal_fmt(ctx, "Unknown askrene layer record %u in %s", type, layername);
122+
}
123+
124+
if (!data_in)
125+
return tal_fmt(ctx, "Corrupt askrene layer record for %s", layername);
126+
127+
*data_out = out;
128+
return NULL;
129+
}
130+
131+
static const char *downgrade_askrene_layers(const tal_t *ctx, struct db *db)
132+
{
133+
const char **base, **k;
134+
const u8 *data;
135+
struct db_stmt *stmt;
136+
struct layer **layers = tal_arr(tmpctx, struct layer *, 0);
137+
138+
base = tal_arr(tmpctx, const char *, 2);
139+
base[0] = "askrene";
140+
base[1] = "layers";
141+
142+
/* Gather and convert */
143+
for (stmt = db_datastore_first(tmpctx, db, base,
144+
&k, &data, NULL);
145+
stmt;
146+
stmt = db_datastore_next(tmpctx, stmt, base,
147+
&k, &data, NULL)) {
148+
struct layer *layer;
149+
const char *err;
150+
151+
if (!data)
152+
continue;
153+
layer = tal(layers, struct layer);
154+
layer->key = tal_steal(layer, k);
155+
err = convert_layer_data(layer, k[2], data, &layer->data);
156+
if (err) {
157+
tal_free(stmt);
158+
return err;
159+
}
160+
tal_arr_expand(&layers, layer);
161+
}
162+
163+
/* Write back */
164+
for (size_t i = 0; i < tal_count(layers); i++)
165+
db_datastore_update(db, layers[i]->key, layers[i]->data);
166+
return NULL;
167+
}
168+
32169
static const struct db_version db_versions[] = {
33-
{ "v25.09", 276, false },
170+
{ "v25.09", 276, downgrade_askrene_layers, false },
34171
/* When we implement v25.12 downgrade: { "v25.12", 280, ???}, */
35172
};
36173

@@ -66,7 +203,8 @@ static void opt_log_stderr_exit_usage(const char *fmt, ...)
66203
int main(int argc, char *argv[])
67204
{
68205
char *config_filename, *base_dir, *net_dir, *rpc_filename, *wallet_dsn = NULL;
69-
size_t current, prev_version_height, num_migrations;
206+
const struct db_version *prev_version;
207+
size_t current, num_migrations;
70208
struct db *db;
71209
const struct db_migration *migrations;
72210
struct db_stmt *stmt;
@@ -99,7 +237,7 @@ int main(int argc, char *argv[])
99237
}
100238

101239
migrations = get_db_migrations(&num_migrations);
102-
prev_version_height = version_db(PREV_VERSION)->db_height;
240+
prev_version = version_db(PREV_VERSION);
103241

104242
/* Open db, check it's the expected version */
105243
db = db_open(tmpctx, wallet_dsn, false, false, db_error, NULL);
@@ -111,10 +249,10 @@ int main(int argc, char *argv[])
111249
db->data_version = db_data_version_get(db);
112250
current = db_get_version(db);
113251

114-
if (current < prev_version_height)
252+
if (current < prev_version->db_height)
115253
errx(ERROR_DBVERSION, "Database version %zu already less than %zu expected for %s",
116-
current, prev_version_height, PREV_VERSION);
117-
if (current == prev_version_height) {
254+
current, prev_version->db_height, PREV_VERSION);
255+
if (current == prev_version->db_height) {
118256
printf("Already compatible with %s\n", PREV_VERSION);
119257
exit(0);
120258
}
@@ -123,7 +261,7 @@ int main(int argc, char *argv[])
123261
current, num_migrations, stringify(CLN_NEXT_VERSION));
124262

125263
/* current version is the last migration we did. */
126-
while (current > prev_version_height) {
264+
while (current > prev_version->db_height) {
127265
if (migrations[current].revertsql) {
128266
stmt = db_prepare_v2(db, migrations[current].revertsql);
129267
db_exec_prepared_v2(stmt);
@@ -137,6 +275,12 @@ int main(int argc, char *argv[])
137275
current--;
138276
}
139277

278+
if (prev_version->downgrade_datastore) {
279+
const char *error = prev_version->downgrade_datastore(tmpctx, db);
280+
if (error)
281+
errx(ERROR_DBFAIL, "Downgrade failed: %s", error);
282+
}
283+
140284
/* Finally update the version number in the version table */
141285
stmt = db_prepare_v2(db, SQL("UPDATE version SET version=?;"));
142286
db_bind_int(stmt, current);

0 commit comments

Comments
 (0)