wallet: we can assume local_alias field is non-null.
What changed, and why it matters
This commit fixes a database migration in Core Lightning that was supposed to ensure every channel has a local alias (a routing identifier), but it missed channels that didn't yet have a stable channel ID (SCID). The patch moves and broadens the migration so all channels without a local alias get one, and then simplifies the code to always expect the field to be present. It is a data-integrity bug fix rather than an obvious remote exploit, but if a channel lacked a local alias it could lead to inconsistent routing behavior or crashes in later code paths that assume the alias exists.
Treat as a low-severity data-integrity fix. Users running versions between the original migration and this fix should ensure the updated migration runs and backfills alias_local for all channels, especially those without an SCID. Monitor for any crashes or routing anomalies related to missing local aliases. No immediate emergency response is warranted absent evidence of remote exploitability.
Security signals we found
Incomplete database migration that left some rows with a NULL field the code later assumes is non-null
Code simplification removes NULL handling for alias_local, increasing reliance on the migration's completeness
Potential data-integrity / availability issue if a channel without local_alias is loaded or saved
No explicit security framing by the vendor; appears as a correctness/robustness fix
Evidence from the diff
The change moves migrate_initialize_alias_local from an earlier migration slot to a later one and removes the ‘scid IS NOT NULL’ filter, so channels without an SCID are also backfilled with a local alias. wallet_stmt2channel and wallet_stmt2closed_channel now use db_col_short_channel_id (non-nullable) for alias_local instead of db_col_optional_scid, and wallet_channel_save always binds a non-null local alias. The test is updated to set a local alias on the test channel. The commit message frames this as a follow-up fix to an incomplete migration and a code simplification based on the now-valid non-null invariant.
Changed components
wallet/db.cwallet/wallet.cwallet/test/run-wallet.cInspect captured patch +11 / −10
diff --git a/wallet/db.c b/wallet/db.c
index bc7b0256..bfed9455 100644
--- a/wallet/db.c
+++ b/wallet/db.c
@@ -1026,7 +1026,7 @@ static struct migration dbmigrations[] = {
{SQL("ALTER TABLE channels ADD remote_htlc_maximum_msat BIGINT DEFAULT NULL;"), NULL},
{SQL("ALTER TABLE channels ADD remote_htlc_minimum_msat BIGINT DEFAULT NULL;"), NULL},
{SQL("ALTER TABLE channels ADD last_stable_connection BIGINT DEFAULT 0;"), NULL},
- {NULL, migrate_initialize_alias_local},
+ {NULL, NULL}, /* old migrate_initialize_alias_local */
{SQL("CREATE TABLE addresses ("
" keyidx BIGINT,"
" addrtype INTEGER)"), NULL},
@@ -1044,6 +1044,7 @@ static struct migration dbmigrations[] = {
{NULL, migrate_initialize_channel_htlcs_wait_indexes_and_fixup_forwards},
{SQL("ALTER TABLE channel_funding_inflights ADD i_sent_sigs INTEGER DEFAULT 0"), NULL},
{SQL("ALTER TABLE channels ADD old_scids BLOB DEFAULT NULL;"), NULL},
+ {NULL, migrate_initialize_alias_local},
};
/**
@@ -2013,8 +2014,7 @@ static void migrate_initialize_alias_local(struct lightningd *ld,
u64 *ids = tal_arr(tmpctx, u64, 0);
stmt = db_prepare_v2(db, SQL("SELECT id FROM channels"
- " WHERE scid IS NOT NULL"
- " AND alias_local IS NULL;"));
+ " WHERE alias_local IS NULL;"));
db_query_prepared(stmt);
while (db_step(stmt))
tal_arr_expand(&ids, db_col_u64(stmt, "id"));
diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c
index 0e96b9d1..f8384643 100644
--- a/wallet/test/run-wallet.c
+++ b/wallet/test/run-wallet.c
@@ -1794,6 +1794,7 @@ static bool test_channel_crud(struct lightningd *ld, const tal_t *ctx)
struct pubkey pk;
struct node_id id;
struct changed_htlc *last_commit;
+ struct short_channel_id local_alias;
secp256k1_ecdsa_signature *sig = tal(w, secp256k1_ecdsa_signature);
u8 *scriptpubkey = tal_arr(ctx, u8, 100);
secp256k1_ecdsa_signature *node_sig1 = tal(w, secp256k1_ecdsa_signature);
@@ -1850,6 +1851,8 @@ static bool test_channel_crud(struct lightningd *ld, const tal_t *ctx)
/* Init channel inflights */
list_head_init(&c1.inflights);
c1.type = type;
+ local_alias = random_scid();
+ c1.alias[LOCAL] = &local_alias;
db_begin_transaction(w->db);
CHECK(!wallet_err);
diff --git a/wallet/wallet.c b/wallet/wallet.c
index 7d9f2d36..ce7c5248 100644
--- a/wallet/wallet.c
+++ b/wallet/wallet.c
@@ -1819,7 +1819,8 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm
scid = db_col_optional_scid(tmpctx, stmt, "scid");
old_scids = db_col_short_channel_id_arr(tmpctx, stmt, "old_scids");
- alias[LOCAL] = db_col_optional_scid(tmpctx, stmt, "alias_local");
+ alias[LOCAL] = tal(tmpctx, struct short_channel_id);
+ *alias[LOCAL] = db_col_short_channel_id(stmt, "alias_local");
alias[REMOTE] = db_col_optional_scid(tmpctx, stmt, "alias_remote");
ok &= wallet_shachain_load(w, db_col_u64(stmt, "shachain_remote_id"),
@@ -2099,7 +2100,8 @@ static struct closed_channel *wallet_stmt2closed_channel(const tal_t *ctx,
cc->peer_id = db_col_optional(cc, stmt, "p.node_id", node_id);
db_col_channel_id(stmt, "full_channel_id", &cc->cid);
cc->scid = db_col_optional_scid(cc, stmt, "scid");
- cc->alias[LOCAL] = db_col_optional_scid(cc, stmt, "alias_local");
+ cc->alias[LOCAL] = tal(cc, struct short_channel_id);
+ *cc->alias[LOCAL] = db_col_short_channel_id(stmt, "alias_local");
cc->alias[REMOTE] = db_col_optional_scid(cc, stmt, "alias_remote");
cc->opener = db_col_int(stmt, "funder");
cc->closer = db_col_int(stmt, "closer");
@@ -2654,11 +2656,7 @@ void wallet_channel_save(struct wallet *w, struct channel *chan)
db_bind_amount_msat(stmt, &chan->htlc_minimum_msat);
db_bind_amount_msat(stmt, &chan->htlc_maximum_msat);
- if (chan->alias[LOCAL] != NULL)
- db_bind_short_channel_id(stmt, *chan->alias[LOCAL]);
- else
- db_bind_null(stmt);
-
+ db_bind_short_channel_id(stmt, *chan->alias[LOCAL]);
if (chan->alias[REMOTE] != NULL)
db_bind_short_channel_id(stmt, *chan->alias[REMOTE]);
else
Why this scored 26/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.