lightningd: speed mapping from coinmoves table to channel for listcoinmoves.
What changed, and why it matters
This commit is a performance optimization, not a security fix. It replaces a slow loop that searched through every peer and channel to find a channel by its database ID with a fast hash table lookup. The change makes the listcoinmoves command respond faster on large nodes but does not address any vulnerability.
No security action required. Treat as a normal performance improvement.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch introduces a new channels_by_dbid hash table in struct lightningd, populated when channels are created or committed, and cleaned up via a tal destructor on channel free. It rewrites channel_by_dbid() from an O(peers × channels) linear scan to an O(1) hash-table lookup. Test code is updated to initialize the new map and to avoid duplicate-key clashes. There is no change to trust boundaries, input validation, cryptographic handling, or authorization.
Changed components
lightningd/channel.clightningd/channel.hlightningd/dual_open_control.clightningd/lightningd.clightningd/lightningd.hwallet/test/run-wallet.cInspect captured patch +55 / −15
diff --git a/lightningd/channel.c b/lightningd/channel.c
index d69865b2..354249a4 100644
--- a/lightningd/channel.c
+++ b/lightningd/channel.c
@@ -312,6 +312,20 @@ void channel_add_old_scid(struct channel *channel,
chanmap_add(channel->peer->ld, channel, old_scid);
}
+static void remove_from_dbid_map(struct channel *channel)
+{
+ if (!channel_dbid_map_del(channel->peer->ld->channels_by_dbid, channel))
+ abort();
+}
+
+void add_channel_to_dbid_map(struct lightningd *ld,
+ struct channel *channel)
+{
+ assert(channel->dbid != 0);
+ channel_dbid_map_add(ld->channels_by_dbid, channel);
+ tal_add_destructor(channel, remove_from_dbid_map);
+}
+
struct channel *new_unsaved_channel(struct peer *peer,
u32 feerate_base,
u32 feerate_ppm)
@@ -698,6 +712,7 @@ struct channel *new_channel(struct peer *peer, u64 dbid,
channel->rr_number = peer->ld->rr_counter++;
tal_add_destructor(channel, destroy_channel);
+ add_channel_to_dbid_map(peer->ld, channel);
list_head_init(&channel->inflights);
channel->closer = closer;
@@ -842,20 +857,7 @@ struct channel *any_channel_by_scid(struct lightningd *ld,
struct channel *channel_by_dbid(struct lightningd *ld, const u64 dbid)
{
- struct peer *p;
- struct channel *chan;
- struct peer_node_id_map_iter it;
-
- /* FIXME: Support lookup by id directly! */
- for (p = peer_node_id_map_first(ld->peers, &it);
- p;
- p = peer_node_id_map_next(ld->peers, &it)) {
- list_for_each(&p->channels, chan, list) {
- if (chan->dbid == dbid)
- return chan;
- }
- }
- return NULL;
+ return channel_dbid_map_get(ld->channels_by_dbid, dbid);
}
struct channel *channel_by_cid(struct lightningd *ld,
diff --git a/lightningd/channel.h b/lightningd/channel.h
index 0e4959e8..af2f18a5 100644
--- a/lightningd/channel.h
+++ b/lightningd/channel.h
@@ -784,6 +784,27 @@ static inline bool channel_state_open_uncommitted(enum channel_state state)
abort();
}
+static inline size_t channel_dbid_hash(u64 dbid)
+{
+ return siphash24(siphash_seed(), &dbid, sizeof(dbid));
+}
+
+static u64 channel_dbid(const struct channel *channel)
+{
+ assert(channel->dbid);
+ return channel->dbid;
+}
+
+static bool channel_dbid_eq(const struct channel *channel, u64 dbid)
+{
+ return channel->dbid == dbid;
+}
+/* Defines struct channel_dbid_map */
+HTABLE_DEFINE_NODUPS_TYPE(struct channel,
+ channel_dbid, channel_dbid_hash, channel_dbid_eq,
+ channel_dbid_map);
+
+void add_channel_to_dbid_map(struct lightningd *ld, struct channel *channel);
void channel_set_owner(struct channel *channel, struct subd *owner);
diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c
index fe995d3d..3ad5cfb1 100644
--- a/lightningd/dual_open_control.c
+++ b/lightningd/dual_open_control.c
@@ -1446,6 +1446,7 @@ wallet_commit_channel(struct lightningd *ld,
channel_info->old_remote_per_commit = channel_info->remote_per_commit;
/* Promote the unsaved_dbid to the dbid */
+ assert(channel->dbid == 0);
assert(channel->unsaved_dbid != 0);
channel->dbid = channel->unsaved_dbid;
channel->unsaved_dbid = 0;
@@ -1524,6 +1525,9 @@ wallet_commit_channel(struct lightningd *ld,
/* Now we finally put it in the database. */
wallet_channel_insert(ld->wallet, channel);
+ /* So we can find it by the newly-assigned dbid */
+ add_channel_to_dbid_map(ld, channel);
+
/* Open attempt to channel's inflights */
inflight = new_inflight(channel,
NULL,
diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c
index d76d5b60..452cee38 100644
--- a/lightningd/lightningd.c
+++ b/lightningd/lightningd.c
@@ -213,6 +213,10 @@ static struct lightningd *new_lightningd(const tal_t *ctx)
ld->channels_by_scid = tal(ld, struct channel_scid_map);
channel_scid_map_init(ld->channels_by_scid);
+ /*~ Coin movements in db are indexed by the channel dbid. */
+ ld->channels_by_dbid = tal(ld, struct channel_dbid_map);
+ channel_dbid_map_init(ld->channels_by_dbid);
+
/*~ For multi-part payments, we need to keep some incoming payments
* in limbo until we get all the parts, or we time them out. */
ld->htlc_sets = tal(ld, struct htlc_set_map);
diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h
index 44a76e18..306b8c29 100644
--- a/lightningd/lightningd.h
+++ b/lightningd/lightningd.h
@@ -218,6 +218,9 @@ struct lightningd {
/* Here are all our channels and their aliases */
struct channel_scid_map *channels_by_scid;
+ /* Open channels by dbid */
+ struct channel_dbid_map *channels_by_dbid;
+
/* Outstanding connect commands. */
struct list_head connects;
diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c
index 13aa9461..84ac059d 100644
--- a/wallet/test/run-wallet.c
+++ b/wallet/test/run-wallet.c
@@ -1358,6 +1358,8 @@ static struct wallet *create_test_wallet(struct lightningd *ld, const tal_t *ctx
/* Create fresh channels map */
ld->channels_by_scid = tal(ld, struct channel_scid_map);
channel_scid_map_init(ld->channels_by_scid);
+ ld->channels_by_dbid = tal(ld, struct channel_dbid_map);
+ channel_dbid_map_init(ld->channels_by_dbid);
return w;
}
@@ -2088,8 +2090,10 @@ static bool test_channel_inflight_crud(struct lightningd *ld, const tal_t *ctx)
/* do inflights get correctly added to the channel? */
wallet_inflight_add(w, inflight);
- /* Hack to remove scids from htable so we don't clash! */
+ /* Hack to remove scid & dbid from htables so we don't clash! */
chanmap_remove(ld, chan, *chan->alias[LOCAL]);
+ remove_from_dbid_map(chan);
+ tal_del_destructor(chan, remove_from_dbid_map);
/* do inflights get correctly loaded from the database? */
CHECK_MSG(c2 = wallet_channel_load(w, chan->dbid),
@@ -2385,6 +2389,8 @@ int main(int argc, const char *argv[])
list_head_init(&ld->wait_commands);
ld->closed_channels = tal(ld, struct closed_channel_map);
closed_channel_map_init(ld->closed_channels);
+ ld->channels_by_dbid = tal(ld, struct channel_dbid_map);
+ channel_dbid_map_init(ld->channels_by_dbid);
/* We do a runtime test here, so we still check compile! */
if (HAVE_SQLITE3) {
Why this scored 15/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.