hsmd: fix derivation for non-taproot addresses in modern mnemonic (25.12+) nodes.
What changed, and why it matters
This commit fixes a bug where Core Lightning nodes created with a modern mnemonic (version 25.12 or newer) could not correctly sign transactions that spend from non-Taproot on-chain addresses. The address generation code was updated to use a new key derivation method for all address types, but the signing code still only used that method for Taproot addresses. As a result, the private key used to sign did not match the public key in the address, causing transaction broadcast failures with an OP_EQUALVERIFY script error. The fix makes the signing code use the new derivation for all address types when the node uses the modern mnemonic format.
Nodes created with v25.12+ mnemonics should upgrade to a release containing this fix if they expect to spend from non-Taproot on-chain addresses. Operators who generated non-Taproot addresses on such nodes and encountered broadcast failures should retry after upgrading. Review whether any funds sent to affected non-Taproot addresses are recoverable only after applying the fix.
Security signals we found
Key derivation mismatch between address generation and signing
Transaction broadcast failure due to invalid signature for scriptPubkey
OP_EQUALVERIFY script verification failure
Fix for regression introduced by prior derivation change
Evidence from the diff
In hsmd/libhsmd.c, hsm_key_for_utxo() derives the key used to sign a UTXO spend. A prior change introduced BIP86 derivation for Taproot (P2TR) outputs on modern mnemonic nodes, identified by use_bip86_derivation() based on bip32_seed length. The wallet’s address generation was updated to apply this derivation to all address types for such nodes, but the HSM signing path retained a guard that only applied BIP86 when the UTXO was P2TR. For non-Taproot UTXOs on modern-mnemonic nodes, the HSM therefore derived a legacy key, producing a signature that did not match the scriptPubkey, leading to mempool-script-verify-flag-failed (OP_EQUALVERIFY failure). The patch removes the is_p2tr() check so BIP86 derivation is used whenever use_bip86_derivation() is true, matching wallet behavior.
Changed components
hsmd/libhsmd.cHSM signing path for on-chain UTXOsNon-Taproot address spending for modern mnemonic (v25.12+) nodesInspect captured patch +2 / −3
diff --git a/hsmd/libhsmd.c b/hsmd/libhsmd.c
index 53988bf..4666012 100644
--- a/hsmd/libhsmd.c
+++ b/hsmd/libhsmd.c
@@ -540,9 +540,8 @@ static void hsm_key_for_utxo(struct privkey *privkey, struct pubkey *pubkey,
hsmd_status_debug("Derived public key %s from unilateral close",
fmt_pubkey(tmpctx, pubkey));
} else {
- /* Modern HSMs use bip86 for p2tr. */
- if (is_p2tr(utxo->scriptPubkey, tal_bytelen(utxo->scriptPubkey), NULL)
- && use_bip86_derivation(tal_bytelen(secretstuff.bip32_seed))) {
+ /* Modern HSMs use bip86. */
+ if (use_bip86_derivation(tal_bytelen(secretstuff.bip32_seed))) {
/* Use BIP86 derivation */
bip86_key(privkey, pubkey, utxo->keyindex);
} else {
Why this scored 62/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.