lightningd: introduce some changes for dual open id randomness even with CLN_DEV_ENTROPY_SEED.
What changed, and why it matters
This commit fixes a developer-only edge case. When running Core Lightning with a special developer mode flag (CLN_DEV_ENTROPY_SEED) that forces the same randomness seed for testing, multiple channel-opening sub-processes could accidentally pick the same internal IDs. That could cause a PSBT (transaction-building data structure) collision when combining contributions. The fix tweaks the seed slightly for each new sub-process so IDs differ, then restores the original seed afterward. It is not a normal user security bug and does not expose funds or keys.
No urgent action needed. Treat as a minor robustness improvement for developer/regression testing. Reviewers may want to confirm the seed is always restored on error paths and that atol() on a 64-bit seed string cannot overflow or mis-parse non-numeric seeds.
Security signals we found
developer-mode-only code path
entropy seed collision avoidance
dual-funding PSBT ID collision mitigation
environment variable mutation around subdaemon spawn
Evidence from the diff
The patch adds dev_setup_dualopend_seed() and dev_restore_seed() in lightningd/dual_open_control.c. If the node is in developer mode and the CLN_DEV_ENTROPY_SEED environment variable is set, the code increments the seed by a static counter before spawning each lightning_dualopend subd, then restores the original seed after spawn. This prevents duplicate per-peer channel IDs when the same entropy seed is reused across dual-funding channel opens. The change is gated on ld->developer and the presence of the dev-only env variable.
Changed components
lightningd/dual_open_control.clightning_dualopend subdaemon spawn pathCLN_DEV_ENTROPY_SEED developer toolingInspect captured patch +37 / −0
diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c
index ad55ca8..cb7c28b 100644
--- a/lightningd/dual_open_control.c
+++ b/lightningd/dual_open_control.c
@@ -30,6 +30,7 @@
#include <lightningd/peer_fd.h>
#include <lightningd/plugin_hook.h>
#include <openingd/dualopend_wiregen.h>
+#include <stdio.h>
#include <unistd.h>
struct commit_rcvd {
@@ -4087,6 +4088,35 @@ static void dualopen_errmsg(struct channel *channel,
err_for_them ? "sent" : "received", desc);
}
+/* This is a hack for CLN_DEV_ENTROPY_SEED. We cannot actually use
+ * the same seed for each dualopend, or they choose the same ids, and we
+ * clash when combining the PSBTs (this is phenomenally unlikey normally).
+ * So we set it (for the child) to an incrementing value. */
+static const char *dev_setup_dualopend_seed(const tal_t *ctx, struct lightningd *ld)
+{
+ static u64 seed_incr = 0;
+ char seedstr[STR_MAX_CHARS(u64)];
+ const char *old_seed;
+
+ if (!ld->developer)
+ return NULL;
+
+ old_seed = getenv("CLN_DEV_ENTROPY_SEED");
+ if (!old_seed)
+ return NULL;
+
+ old_seed = tal_strdup(tmpctx, old_seed);
+ seed_incr++;
+ snprintf(seedstr, sizeof(seedstr), "%"PRIu64, atol(old_seed) + seed_incr);
+ setenv("CLN_DEV_ENTROPY_SEED", seedstr, 1);
+ return old_seed;
+}
+
+static void dev_restore_seed(const char *old_seed)
+{
+ if (old_seed)
+ setenv("CLN_DEV_ENTROPY_SEED", old_seed, 1);
+}
bool peer_start_dualopend(struct peer *peer,
struct peer_fd *peer_fd,
@@ -4096,6 +4126,7 @@ bool peer_start_dualopend(struct peer *peer,
u32 max_to_self_delay;
struct amount_msat min_effective_htlc_capacity;
const u8 *msg;
+ const char *dev_old_seed;
hsmfd = hsm_get_client_fd(peer->ld, &peer->id, channel->unsaved_dbid,
HSM_PERM_COMMITMENT_POINT
@@ -4109,6 +4140,7 @@ bool peer_start_dualopend(struct peer *peer,
return false;
}
+ dev_old_seed = dev_setup_dualopend_seed(tmpctx, peer->ld);
channel->owner = new_channel_subd(channel,
peer->ld,
"lightning_dualopend",
@@ -4121,6 +4153,7 @@ bool peer_start_dualopend(struct peer *peer,
channel_set_billboard,
take(&peer_fd->fd),
take(&hsmfd), NULL);
+ dev_restore_seed(dev_old_seed);
if (!channel->owner) {
channel_internal_error(channel,
@@ -4174,6 +4207,7 @@ bool peer_restart_dualopend(struct peer *peer,
int hsmfd;
u32 *local_shutdown_script_wallet_index;
u8 *msg;
+ const char *dev_old_seed;
if (channel_state_uncommitted(channel->state))
return peer_start_dualopend(peer, peer_fd, channel);
@@ -4193,6 +4227,7 @@ bool peer_restart_dualopend(struct peer *peer,
return false;
}
+ dev_old_seed = dev_setup_dualopend_seed(tmpctx, peer->ld);
channel_set_owner(channel,
new_channel_subd(channel, peer->ld,
"lightning_dualopend",
@@ -4205,6 +4240,8 @@ bool peer_restart_dualopend(struct peer *peer,
channel_set_billboard,
take(&peer_fd->fd),
take(&hsmfd), NULL));
+ dev_restore_seed(dev_old_seed);
+
if (!channel->owner) {
log_broken(channel->log, "Could not subdaemon channel: %s",
strerror(errno));
Why this scored 23/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.