lightningd: allow --recover / recover JSON RPC to take mnemonic.
In fact, you *must* use mnemonic to successfully recover a modern node! Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> Changelog-Changed: JSON-RPC: `recover` takes a 12-word mnemonic for nodes created by v25.12 or later.
This commit is contained in:
@@ -240,17 +240,18 @@ static bool have_channels(struct lightningd *ld)
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct command_result *param_codex32_or_hex(struct command *cmd,
|
||||
const char *name,
|
||||
const char *buffer,
|
||||
const jsmntok_t *tok,
|
||||
const char **hsm_secret)
|
||||
static struct command_result *param_hsm_secret(struct command *cmd,
|
||||
const char *name,
|
||||
const char *buffer,
|
||||
const jsmntok_t *tok,
|
||||
const char **hsm_secret)
|
||||
{
|
||||
char *err;
|
||||
const u8 *payload;
|
||||
/* We parse here for sanity checking, but we just hand string to --recover */
|
||||
const struct hsm_secret *hsms;
|
||||
|
||||
*hsm_secret = json_strdup(cmd, buffer, tok);
|
||||
err = hsm_secret_arg(tmpctx, *hsm_secret, &payload);
|
||||
err = hsm_secret_arg(tmpctx, *hsm_secret, &hsms);
|
||||
if (err)
|
||||
return command_fail_badparam(cmd, name, buffer, tok, err);
|
||||
return NULL;
|
||||
@@ -279,10 +280,10 @@ static struct command_result *json_recover(struct command *cmd,
|
||||
const jsmntok_t *obj UNNEEDED,
|
||||
const jsmntok_t *params)
|
||||
{
|
||||
const char *hsm_secret, *dir;
|
||||
const char *dir, *hsm_secret;
|
||||
|
||||
if (!param_check(cmd, buffer, params,
|
||||
p_req("hsmsecret", param_codex32_or_hex, &hsm_secret),
|
||||
p_req("hsmsecret", param_hsm_secret, &hsm_secret),
|
||||
NULL))
|
||||
return command_param_failed();
|
||||
|
||||
|
||||
@@ -1280,41 +1280,53 @@ static char *opt_add_api_beg(const char *arg, struct lightningd *ld)
|
||||
|
||||
char *hsm_secret_arg(const tal_t *ctx,
|
||||
const char *arg,
|
||||
const u8 **hsm_secret)
|
||||
const struct hsm_secret **hsm_secret)
|
||||
{
|
||||
char *codex32_fail;
|
||||
struct codex32 *codex32;
|
||||
struct hsm_secret *hsms = tal(tmpctx, struct hsm_secret);
|
||||
|
||||
/* We accept hex, or codex32. hex is very very very unlikely to
|
||||
* give a valid codex32, so try that first */
|
||||
codex32 = codex32_decode(tmpctx, "cl", arg, &codex32_fail);
|
||||
if (codex32) {
|
||||
*hsm_secret = tal_steal(ctx, codex32->payload);
|
||||
hsms->type = HSM_SECRET_PLAIN;
|
||||
hsms->secret_data = tal_steal(hsms, codex32->payload);
|
||||
if (codex32->threshold != 0
|
||||
|| codex32->type != CODEX32_ENCODING_SECRET) {
|
||||
return "This is only one share of codex32!";
|
||||
}
|
||||
if (tal_count(hsms->secret_data) != 32)
|
||||
return "Invalid length: must be 32 bytes";
|
||||
/* Not codex32, was it hex? */
|
||||
} else if ((hsms->secret_data = tal_hexdata(hsms, arg, strlen(arg))) != NULL) {
|
||||
hsms->type = HSM_SECRET_PLAIN;
|
||||
if (tal_count(hsms->secret_data) != 32)
|
||||
return "Invalid length: must be 32 bytes";
|
||||
/* Not hex, is is a mnemonic? */
|
||||
} else {
|
||||
/* Not codex32, was it hex? */
|
||||
*hsm_secret = tal_hexdata(ctx, arg, strlen(arg));
|
||||
if (!*hsm_secret) {
|
||||
/* It's not hex! So give codex32 error */
|
||||
return codex32_fail;
|
||||
enum hsm_secret_error err = validate_mnemonic(arg);
|
||||
if (err != HSM_SECRET_OK) {
|
||||
/* If it looks kinda like a codex32, give that error. */
|
||||
if (strstarts(arg, "cl"))
|
||||
return codex32_fail;
|
||||
else
|
||||
return "Not a valid mnemonic, hex, or codex32 string";
|
||||
}
|
||||
hsms->type = HSM_SECRET_MNEMONIC_NO_PASS;
|
||||
hsms->mnemonic = tal_strdup(hsms, arg);
|
||||
}
|
||||
|
||||
if (tal_count(*hsm_secret) != 32)
|
||||
return "Invalid length: must be 32 bytes";
|
||||
|
||||
*hsm_secret = tal_steal(ctx, hsms);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *opt_set_codex32_or_hex(const char *arg, struct lightningd *ld)
|
||||
static char *opt_set_hsm_secret(const char *arg, struct lightningd *ld)
|
||||
{
|
||||
char *err;
|
||||
const u8 *payload;
|
||||
const struct hsm_secret *hsm_secret;
|
||||
|
||||
err = hsm_secret_arg(tmpctx, arg, &payload);
|
||||
err = hsm_secret_arg(tmpctx, arg, &hsm_secret);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -1329,10 +1341,36 @@ static char *opt_set_codex32_or_hex(const char *arg, struct lightningd *ld)
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
if (!write_all(fd, payload, tal_count(payload))) {
|
||||
switch (hsm_secret->type) {
|
||||
case HSM_SECRET_PLAIN:
|
||||
/* Legacy 32-byte format */
|
||||
if (!write_all(fd, hsm_secret->secret_data, tal_count(hsm_secret->secret_data))) {
|
||||
unlink_noerr("hsm_secret");
|
||||
return tal_fmt(tmpctx, "Writing HSM: %s",
|
||||
strerror(errno));
|
||||
}
|
||||
break;
|
||||
case HSM_SECRET_ENCRYPTED:
|
||||
case HSM_SECRET_MNEMONIC_WITH_PASS:
|
||||
return tal_fmt(tmpctx, "Recovery of encrypted/passworded secrets not supported");
|
||||
case HSM_SECRET_MNEMONIC_NO_PASS: {
|
||||
struct sha256 seed_hash;
|
||||
if (!derive_seed_hash(hsm_secret->mnemonic, NULL, &seed_hash)) {
|
||||
unlink_noerr("hsm_secret");
|
||||
return tal_fmt(tmpctx, "Deriving from mnemonic failed!");
|
||||
}
|
||||
/* Write seed hash (32 bytes) + mnemonic */
|
||||
if (!write_all(fd, &seed_hash, sizeof(seed_hash))
|
||||
|| !write_all(fd, hsm_secret->mnemonic, strlen(hsm_secret->mnemonic))) {
|
||||
unlink_noerr("hsm_secret");
|
||||
return tal_fmt(tmpctx, "Error writing to hsm_secret file: %s", strerror(errno));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case HSM_SECRET_INVALID:
|
||||
/* Shouldn't happen? */
|
||||
unlink_noerr("hsm_secret");
|
||||
return tal_fmt(tmpctx, "Writing HSM: %s",
|
||||
strerror(errno));
|
||||
return tal_fmt(tmpctx, "invalid hsm secret?");
|
||||
}
|
||||
|
||||
/*~ fsync (mostly!) ensures that the file has reached the disk. */
|
||||
@@ -1414,9 +1452,9 @@ static void register_opts(struct lightningd *ld)
|
||||
&ld->wallet_dsn,
|
||||
"Location of the wallet database.");
|
||||
|
||||
opt_register_early_arg("--recover", opt_set_codex32_or_hex, NULL,
|
||||
opt_register_early_arg("--recover", opt_set_hsm_secret, NULL,
|
||||
ld,
|
||||
"Populate hsm_secret with the given codex32 secret"
|
||||
"Populate hsm_secret with the given codex32/hex/mnemonic secret"
|
||||
" and starts the node in `offline` mode.");
|
||||
|
||||
/* This affects our features, so set early. */
|
||||
@@ -1856,7 +1894,7 @@ bool is_known_opt_cb_arg(char *(*cb_arg)(const char *, void *))
|
||||
|| cb_arg == (void *)opt_set_db_upgrade
|
||||
|| cb_arg == (void *)arg_log_to_file
|
||||
|| cb_arg == (void *)opt_add_accept_htlc_tlv
|
||||
|| cb_arg == (void *)opt_set_codex32_or_hex
|
||||
|| cb_arg == (void *)opt_set_hsm_secret
|
||||
|| cb_arg == (void *)opt_subd_dev_disconnect
|
||||
|| cb_arg == (void *)opt_set_crash_timeout
|
||||
|| cb_arg == (void *)opt_add_api_beg
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "config.h"
|
||||
#include <ccan/ccan/opt/opt.h>
|
||||
|
||||
struct hsm_secret;
|
||||
struct json_stream;
|
||||
struct lightningd;
|
||||
|
||||
@@ -16,7 +17,7 @@ void handle_opts(struct lightningd *ld);
|
||||
void setup_color_and_alias(struct lightningd *ld);
|
||||
|
||||
/**
|
||||
* hsm_secret_arg - parse an hsm_secret as hex or codex32
|
||||
* hsm_secret_arg - parse an hsm_secret as hex, codex32 or mnemonic.
|
||||
* @ctx: context to allocate @hsm_secret from
|
||||
* @arg: string to parse
|
||||
* @hsm_secret: set on success.
|
||||
@@ -25,7 +26,7 @@ void setup_color_and_alias(struct lightningd *ld);
|
||||
*/
|
||||
char *hsm_secret_arg(const tal_t *ctx,
|
||||
const char *arg,
|
||||
const u8 **hsm_secret);
|
||||
const struct hsm_secret **hsm_secret);
|
||||
|
||||
enum opt_autobool {
|
||||
OPT_AUTOBOOL_FALSE = 0,
|
||||
|
||||
@@ -32,7 +32,7 @@ u32 get_feerate_floor(const struct chain_topology *topo UNNEEDED)
|
||||
/* Generated stub for hsm_secret_arg */
|
||||
char *hsm_secret_arg(const tal_t *ctx UNNEEDED,
|
||||
const char *arg UNNEEDED,
|
||||
const u8 **hsm_secret UNNEEDED)
|
||||
const struct hsm_secret **hsm_secret UNNEEDED)
|
||||
{ fprintf(stderr, "hsm_secret_arg called!\n"); abort(); }
|
||||
/* Generated stub for lightningd_deprecated_in_ok */
|
||||
bool lightningd_deprecated_in_ok(struct lightningd *ld UNNEEDED,
|
||||
|
||||
Reference in New Issue
Block a user