From d59e2b1b4b650f84ceebae7e0c4ba707de414569 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 17 Jul 2019 07:11:14 +0930 Subject: [PATCH] developer: add --dev-force-bip32-seed to force a specific BIP32 seed. Signed-off-by: Rusty Russell --- hsmd/hsm_wire.csv | 1 + hsmd/hsmd.c | 20 +++++++++++++++++++- lightningd/hsm_control.c | 5 +++-- lightningd/lightningd.c | 1 + lightningd/lightningd.h | 3 +++ lightningd/options.c | 15 ++++++++++++++- tests/test_misc.py | 16 ++++++++++++++++ 7 files changed, 57 insertions(+), 4 deletions(-) diff --git a/hsmd/hsm_wire.csv b/hsmd/hsm_wire.csv index f43eed8b2..d323ef4ec 100644 --- a/hsmd/hsm_wire.csv +++ b/hsmd/hsm_wire.csv @@ -10,6 +10,7 @@ hsmstatus_client_bad_request,,msg,len*u8 hsm_init,11 hsm_init,,bip32_key_version,struct bip32_key_version hsm_init,,dev_force_privkey,?struct privkey +hsm_init,,dev_force_bip32_seed,?struct secret #include hsm_init_reply,111 diff --git a/hsmd/hsmd.c b/hsmd/hsmd.c index fcb135cb7..097f44468 100644 --- a/hsmd/hsmd.c +++ b/hsmd/hsmd.c @@ -75,6 +75,8 @@ static struct bip32_key_version bip32_key_version; #if DEVELOPER /* If they specify --dev-force-privkey it ends up in here. */ static struct privkey *dev_force_privkey; +/* If they specify --dev-force-bip32-seed it ends up in here. */ +static struct secret *dev_force_bip32_seed; #endif /*~ We keep track of clients, but there's not much to keep. */ @@ -403,6 +405,18 @@ static void populate_secretstuff(void) bip32_key_version.bip32_privkey_version, 0, &master_extkey) != WALLY_OK); +#if DEVELOPER + /* In DEVELOPER mode, we can override with --dev-force-bip32-seed */ + if (dev_force_bip32_seed) { + if (bip32_key_from_seed(dev_force_bip32_seed->data, + sizeof(dev_force_bip32_seed->data), + bip32_key_version.bip32_privkey_version, + 0, &master_extkey) != WALLY_OK) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Can't derive bip32 master key"); + } +#endif /* DEVELOPER */ + /* BIP 32: * * The default wallet layout @@ -557,6 +571,7 @@ static struct io_plan *init_hsm(struct io_conn *conn, struct node_id node_id; struct pubkey key; struct privkey *privkey; + struct secret *seed; /* This must be lightningd. */ assert(is_lightningd(c)); @@ -565,11 +580,13 @@ static struct io_plan *init_hsm(struct io_conn *conn, * definitions in hsm_client_wire.csv. The format of those files is * an extension of the simple comma-separated format output by the * BOLT tools/extract-formats.py tool. */ - if (!fromwire_hsm_init(NULL, msg_in, &bip32_key_version, &privkey)) + if (!fromwire_hsm_init(NULL, msg_in, &bip32_key_version, + &privkey, &seed)) return bad_req(conn, c, msg_in); #if DEVELOPER dev_force_privkey = privkey; + dev_force_bip32_seed = seed; #endif maybe_create_new_hsm(); load_hsm(); @@ -1622,6 +1639,7 @@ static struct io_plan *handle_memleak(struct io_conn *conn, memleak_scan_region(memtable, status_conn, tal_bytelen(status_conn)); memleak_scan_region(memtable, dev_force_privkey, 0); + memleak_scan_region(memtable, dev_force_bip32_seed, 0); found_leak = dump_memleak(memtable); reply = towire_hsm_dev_memleak_reply(NULL, found_leak); diff --git a/lightningd/hsm_control.c b/lightningd/hsm_control.c index 6211648a8..bbc04f66c 100644 --- a/lightningd/hsm_control.c +++ b/lightningd/hsm_control.c @@ -96,9 +96,10 @@ void hsm_init(struct lightningd *ld) if (!wire_sync_write(ld->hsm_fd, towire_hsm_init(tmpctx, &ld->topology->bitcoind->chainparams->bip32_key_version, #if DEVELOPER - ld->dev_force_privkey + ld->dev_force_privkey, + ld->dev_force_bip32_seed #else - NULL + NULL, NULL #endif ))) err(1, "Writing init msg to hsm"); diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 3a941613c..b81ab449a 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -119,6 +119,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx) ld->dev_allow_localhost = false; ld->dev_gossip_time = 0; ld->dev_force_privkey = NULL; + ld->dev_force_bip32_seed = NULL; #endif /*~ These are CCAN lists: an embedded double-linked list. It's not diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index 78ff5ed78..918edae1b 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -208,6 +208,9 @@ struct lightningd { /* This is the forced private key for the node. */ struct privkey *dev_force_privkey; + + /* This is the forced bip32 seed for the node. */ + struct secret *dev_force_bip32_seed; #endif /* DEVELOPER */ /* tor support */ diff --git a/lightningd/options.c b/lightningd/options.c index 1cb070f6a..17555c11a 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -449,6 +449,17 @@ static char *opt_force_privkey(const char *optarg, struct lightningd *ld) return NULL; } +static char *opt_force_bip32_seed(const char *optarg, struct lightningd *ld) +{ + tal_free(ld->dev_force_bip32_seed); + ld->dev_force_bip32_seed = tal(ld, struct secret); + if (!hex_decode(optarg, strlen(optarg), + ld->dev_force_bip32_seed, + sizeof(*ld->dev_force_bip32_seed))) + return tal_fmt(NULL, "Unable to parse secret '%s'", optarg); + return NULL; +} + static void dev_register_opts(struct lightningd *ld) { opt_register_noarg("--dev-no-reconnect", opt_set_invbool, @@ -487,7 +498,9 @@ static void dev_register_opts(struct lightningd *ld) "UNIX time to override gossipd to use."); opt_register_arg("--dev-force-privkey", opt_force_privkey, NULL, ld, "Force HSM to use this as node private key"); - } + opt_register_arg("--dev-force-bip32-seed", opt_force_bip32_seed, NULL, ld, + "Force HSM to use this as bip32 seed"); +} #endif static const struct config testnet_config = { diff --git a/tests/test_misc.py b/tests/test_misc.py index 35a87a73a..a912970af 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -1262,6 +1262,22 @@ def test_bitcoind_fail_first(node_factory, bitcoind, executor): f.result() +@unittest.skipIf(not DEVELOPER, "needs --dev-force-bip32-seed") +def test_dev_force_bip32_seed(node_factory): + l1 = node_factory.get_node(options={'dev-force-bip32-seed': '0000000000000000000000000000000000000000000000000000000000000001'}) + # First is m/0/0/1 .. + bech32 = l1.rpc.newaddr('bech32')['bech32'] + assert bech32 == "bcrt1qsdzqt93xsyewdjvagndw9523m27e52er5ca7hm" + bech32 = l1.rpc.newaddr('bech32')['bech32'] + assert bech32 == "bcrt1qlkt93775wmf33uacykc49v2j4tayn0yj25msjn" + bech32 = l1.rpc.newaddr('bech32')['bech32'] + assert bech32 == "bcrt1q2ng546gs0ylfxrvwx0fauzcvhuz655en4kwe2c" + bech32 = l1.rpc.newaddr('bech32')['bech32'] + assert bech32 == "bcrt1qrdpwrlrmrnvn535l5eldt64lxm8r2nwkv0ruxq" + bech32 = l1.rpc.newaddr('bech32')['bech32'] + assert bech32 == "bcrt1q622lwmdzxxterumd746eu3d3t40pq53p62zhlz" + + @unittest.skipIf(not DEVELOPER, "needs dev command") def test_dev_demux(node_factory): l1 = node_factory.get_node(may_fail=True, allow_broken_log=True)