From 1b3e881d5a24e5603c938772050988f97ed79f9e Mon Sep 17 00:00:00 2001 From: Sangbida Chaudhuri <101164840+sangbida@users.noreply.github.com> Date: Fri, 24 Oct 2025 13:57:46 +1030 Subject: [PATCH] hsmd: find correct P2TR key for utxo In the case where we receive a taproot utxo we want to be able to tell if it was derived using a BIP32 seed or a BIP86 seed. Considering we will only be supporting BI86 type wallet addresses for mnemonics we can check if the out secret is 64 bytes long and if it is we can use our BIP86 for the withdrawal. --- common/hsm_secret.c | 11 +++++++++++ common/hsm_secret.h | 13 +++++++++++++ hsmd/hsmd.c | 4 ++-- hsmd/libhsmd.c | 30 ++++++++++++++++++++++++++++-- 4 files changed, 54 insertions(+), 4 deletions(-) diff --git a/common/hsm_secret.c b/common/hsm_secret.c index f377d91b5..f14c7e19f 100644 --- a/common/hsm_secret.c +++ b/common/hsm_secret.c @@ -557,3 +557,14 @@ size_t hsm_secret_size(const struct hsm_secret *hsm) return tal_bytelen(hsm->secret_data); return sizeof(hsm->secret); } + +bool is_mnemonic_secret(size_t secret_len) +{ + return secret_len == HSM_SECRET_MNEMONIC_SIZE; +} + +bool use_bip86_derivation(size_t secret_len) +{ + /* BIP86 was introduced alongside mnemonic support, so they're available together */ + return is_mnemonic_secret(secret_len); +} diff --git a/common/hsm_secret.h b/common/hsm_secret.h index d1d1f56c2..66c158f3a 100644 --- a/common/hsm_secret.h +++ b/common/hsm_secret.h @@ -57,6 +57,19 @@ const u8 *hsm_secret_bytes(const struct hsm_secret *hsm); */ size_t hsm_secret_size(const struct hsm_secret *hsm); +/** + * Check if this HSM secret is mnemonic-based (64-byte seed). + * Returns true for mnemonic-derived secrets, false for legacy 32-byte secrets. + */ +bool is_mnemonic_secret(size_t secret_len); + +/** + * Check if we should use BIP86 derivation for this HSM secret. + * BIP86 was introduced alongside mnemonic support, so they're available together. + * Returns true if mnemonic-based secret is available, false otherwise. + */ +bool use_bip86_derivation(size_t secret_len); + /** * Checks whether the hsm_secret data requires a passphrase to decrypt. * Handles legacy, encrypted, and mnemonic-based formats. diff --git a/hsmd/hsmd.c b/hsmd/hsmd.c index 616d7fe64..5e2306534 100644 --- a/hsmd/hsmd.c +++ b/hsmd/hsmd.c @@ -696,7 +696,7 @@ static struct io_plan *handle_derive_bip86_key(struct io_conn *conn, return bad_req(conn, c, msg_in); /* Check if we have a mnemonic-based HSM secret */ - if (hsm_secret_size(&hsm_secret) != 64) { + if (!use_bip86_derivation(hsm_secret_size(&hsm_secret))) { return bad_req_fmt(conn, c, msg_in, "BIP86 derivation requires mnemonic-based HSM secret"); } @@ -724,7 +724,7 @@ static struct io_plan *handle_check_bip86_pubkey(struct io_conn *conn, return bad_req(conn, c, msg_in); /* Check if we have a mnemonic-based HSM secret */ - if (hsm_secret_size(&hsm_secret) != 64) { + if (!use_bip86_derivation(hsm_secret_size(&hsm_secret))) { return bad_req_fmt(conn, c, msg_in, "BIP86 derivation requires mnemonic-based HSM secret"); } diff --git a/hsmd/libhsmd.c b/hsmd/libhsmd.c index b5b4f1c39..b48c15d18 100644 --- a/hsmd/libhsmd.c +++ b/hsmd/libhsmd.c @@ -2,10 +2,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -542,8 +544,32 @@ 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 { - /* Simple case: just get derive via HD-derivation */ - bitcoin_key(privkey, pubkey, utxo->keyindex); + /* Check if this is a BIP86 UTXO by examining the scriptPubkey */ + const size_t script_len = tal_bytelen(utxo->scriptPubkey); + bool is_bip86 = false; + + /* For P2TR scripts, we need to determine if it's BIP86 or regular P2TR + * But BIP86 derivation requires mnemonic-based secrets */ + if (is_p2tr(utxo->scriptPubkey, script_len, NULL) && + use_bip86_derivation(secretstuff.bip32_seed_len)) { + /* Try BIP86 derivation first and see if it matches */ + struct pubkey test_pubkey; + bip86_key(NULL, &test_pubkey, utxo->keyindex); + + /* Create P2TR scriptpubkey from BIP86 key and compare */ + const u8 *bip86_script = scriptpubkey_p2tr(tmpctx, &test_pubkey); + if (memeq(utxo->scriptPubkey, script_len, bip86_script, tal_bytelen(bip86_script))) { + is_bip86 = true; + } + } + + if (is_bip86) { + /* Use BIP86 derivation */ + bip86_key(privkey, pubkey, utxo->keyindex); + } else { + /* Simple case: just get derive via HD-derivation */ + bitcoin_key(privkey, pubkey, utxo->keyindex); + } } }