wallet: use correct derivation for elements when using mnemonic hsm_secret.
What changed, and why it matters
This change fixes how Core Lightning derives Bitcoin/Elements addresses when creating change outputs or PSBT outputs from a wallet seed phrase (mnemonic). Previously, on Elements sidechains, the code derived the public key one way and then built a SegWit address from it directly. Now it uses a dedicated helper that follows the same derivation path used elsewhere (including the future Taproot path). The practical risk is that change outputs could have been sent to addresses the wallet did not fully recognize or could not later spend from, which can lock up funds.
Review the implementation of p2wpkh_for_keyidx to confirm it uses the same derivation path as the wallet's address generation and key lookup. Test that existing Elements wallets with mnemonic hsm_secret can re-derive and spend previously created change outputs. Consider whether a migration or recovery tool is needed for any outputs created with the old derivation before this patch.
Security signals we found
Change output / PSBT output address derivation mismatch on Elements
Use of lower-level bip32_pubkey + manual scriptpubkey_p2wpkh replaced by unified p2wpkh_for_keyidx
Commit message frames change as 'correct derivation' for mnemonic-backed hsm_secret
Potential fund-recovery risk if wallet cannot re-derive the change path
Evidence from the diff
In wallet/reservation.c, two code paths (finish_psbt and json_addpsbtoutput) previously called bip32_pubkey() to derive a raw pubkey and then scriptpubkey_p2wpkh() to build a P2WPKH output script for Elements. The patch replaces that with p2wpkh_for_keyidx(), which internally derives the key through the same key-index helper used for p2tr_for_keyidx(). The commit message says this makes Elements use the ‘correct derivation’ when hsm_secret is backed by a mnemonic, and aligns the scheme with the non-Elements Taproot path for future compatibility. The diff itself does not show a vulnerability description, exploit, or security advisory; it only shows a derivation-path correction.
Changed components
wallet/reservation.cElements (Liquid) sidechain change-output generationPSBT output addition (json_addpsbtoutput)HSM secret / BIP32 key derivation for mnemonic seedsInspect captured patch +2 / −6
diff --git a/wallet/reservation.c b/wallet/reservation.c
index dd1bebb2..b31870a6 100644
--- a/wallet/reservation.c
+++ b/wallet/reservation.c
@@ -359,7 +359,6 @@ static struct command_result *finish_psbt(struct command *cmd,
/* Should we add a change output? (Iff it can pay for itself!) */
change = change_amount(change, feerate_per_kw, weight);
if (amount_sat_greater(change, AMOUNT_SAT(0))) {
- struct pubkey pubkey;
s64 keyidx;
u8 *b32script;
enum addrtype type;
@@ -378,8 +377,7 @@ static struct command_result *finish_psbt(struct command *cmd,
" Keys exhausted.");
if (chainparams->is_elements) {
- bip32_pubkey(cmd->ld, &pubkey, keyidx);
- b32script = scriptpubkey_p2wpkh(tmpctx, &pubkey);
+ b32script = p2wpkh_for_keyidx(tmpctx, cmd->ld, keyidx);
} else {
b32script = p2tr_for_keyidx(tmpctx, cmd->ld, keyidx);
}
@@ -658,7 +656,6 @@ static struct command_result *json_addpsbtoutput(struct command *cmd,
u32 *locktime;
ssize_t outnum;
u32 weight;
- struct pubkey pubkey;
s64 keyidx;
const u8 *b32script;
bool *add_initiator_serial_ids;
@@ -718,8 +715,7 @@ static struct command_result *json_addpsbtoutput(struct command *cmd,
" Keys exhausted.");
if (chainparams->is_elements) {
- bip32_pubkey(cmd->ld, &pubkey, keyidx);
- b32script = scriptpubkey_p2wpkh(tmpctx, &pubkey);
+ b32script = p2wpkh_for_keyidx(tmpctx, cmd->ld, keyidx);
} else {
b32script = p2tr_for_keyidx(tmpctx, cmd->ld, keyidx);
}
Why this scored 57/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.