diff --git a/tools/Makefile b/tools/Makefile index a863f817c..125377f5d 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -17,7 +17,7 @@ tools/headerversions: FORCE tools/headerversions.o $(CCAN_OBJS) tools/check-bolt: tools/check-bolt.o $(CCAN_OBJS) $(TOOLS_COMMON_OBJS) -tools/hsmtool: tools/hsmtool.o $(CCAN_OBJS) $(TOOLS_COMMON_OBJS) $(BITCOIN_OBJS) common/amount.o common/bech32.o common/bigsize.o common/derive_basepoints.o common/node_id.o common/type_to_string.o wire/fromwire.o wire/towire.o +tools/hsmtool: tools/hsmtool.o $(CCAN_OBJS) $(TOOLS_COMMON_OBJS) $(BITCOIN_OBJS) common/amount.o common/bech32.o common/bigsize.o common/derive_basepoints.o common/descriptor_checksum.o common/node_id.o common/type_to_string.o wire/fromwire.o wire/towire.o tools/lightning-hsmtool: tools/hsmtool cp $< $@ diff --git a/tools/hsmtool.c b/tools/hsmtool.c index 8091ac181..a10b06fce 100644 --- a/tools/hsmtool.c +++ b/tools/hsmtool.c @@ -6,8 +6,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -39,6 +41,7 @@ static void show_usage(const char *progname) printf(" - guesstoremote " " [hsm_secret password]\n"); printf(" - generatehsm \n"); + printf(" - dumponchaindescriptors \n"); exit(0); } @@ -513,6 +516,58 @@ static int generate_hsm(const char *hsm_secret_path) return 0; } +static int dumponchaindescriptors(const char *hsm_secret_path, const char *passwd) +{ + struct secret hsm_secret; + u8 bip32_seed[BIP32_ENTROPY_LEN_256]; + u32 salt = 0; + struct ext_key master_extkey; + char *enc_xpub, *descriptor; + struct descriptor_checksum checksum; + + if (passwd) + get_encrypted_hsm_secret(&hsm_secret, hsm_secret_path, passwd); + else + get_hsm_secret(&hsm_secret, hsm_secret_path); + + /* We use m/0/0/k as the derivation tree for onchain funds. */ + + /* The root seed is derived from hsm_secret using hkdf.. */ + do { + hkdf_sha256(bip32_seed, sizeof(bip32_seed), + &salt, sizeof(salt), + &hsm_secret, sizeof(hsm_secret), + "bip32 seed", strlen("bip32 seed")); + salt++; + /* ..Which is used to derive m/ */ + } while (bip32_key_from_seed(bip32_seed, sizeof(bip32_seed), + /* An xpub can easily be converted to a tpub */ + BIP32_VER_MAIN_PRIVATE, + 0, &master_extkey) != WALLY_OK); + + if (bip32_key_to_base58(&master_extkey, BIP32_FLAG_KEY_PUBLIC, &enc_xpub) != WALLY_OK) + errx(ERROR_LIBWALLY, "Can't encode xpub"); + + /* Now we format the descriptor strings (we only ever create P2WPKH and + * P2SH-P2WPKH outputs). */ + + descriptor = tal_fmt(NULL, "wpkh(%s/0/0/*)", enc_xpub); + if (!descriptor_checksum(descriptor, strlen(descriptor), &checksum)) + errx(ERROR_LIBWALLY, "Can't derive descriptor checksum for wpkh"); + printf("%s#%s\n", descriptor, checksum.csum); + tal_free(descriptor); + + descriptor = tal_fmt(NULL, "sh(wpkh(%s/0/0/*))", enc_xpub); + if (!descriptor_checksum(descriptor, strlen(descriptor), &checksum)) + errx(ERROR_LIBWALLY, "Can't derive descriptor checksum for sh(wpkh)"); + printf("%s#%s\n", descriptor, checksum.csum); + tal_free(descriptor); + + wally_free_string(enc_xpub); + + return 0; +} + int main(int argc, char *argv[]) { const char *method; @@ -573,5 +628,12 @@ int main(int argc, char *argv[]) return generate_hsm(hsm_secret_path); } + if (streq(method, "dumponchaindescriptors")) { + if (argc < 3) + show_usage(argv[0]); + + return dumponchaindescriptors(argv[2], argc > 3 ? argv[3] : NULL); + } + show_usage(argv[0]); }