Files
lightning/devtools/checkchannels.c
Rusty Russell 8b1aa3ef8b lightningd: move basic parameter parsing into common/configdir
lightning-cli is going to need to know what network we're on, so
it will need to parse the config files.  Move the code which does
the initial bootstrap parsing into common, as well as the config
file parsing core.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2019-11-23 22:42:34 +00:00

240 lines
7.7 KiB
C

#include "config.h"
#include <assert.h>
#include <bitcoin/privkey.h>
#include <bitcoin/script.h>
#include <bitcoin/short_channel_id.h>
#include <bitcoin/tx.h>
#include <ccan/crypto/hkdf_sha256/hkdf_sha256.h>
#include <ccan/err/err.h>
#include <ccan/mem/mem.h>
#include <ccan/opt/opt.h>
#include <ccan/tal/grab_file/grab_file.h>
#include <ccan/tal/path/path.h>
#include <common/configdir.h>
#include <common/node_id.h>
#include <common/utils.h>
#include <inttypes.h>
#include <sqlite3.h>
static void hsm_channel_secret_base(struct secret *channel_seed_base,
const struct secret *hsm_secret)
{
hkdf_sha256(channel_seed_base, sizeof(struct secret), NULL, 0,
hsm_secret, sizeof(*hsm_secret),
/*~ Initially, we didn't support multiple channels per
* peer at all: a channel had to be completely forgotten
* before another could exist. That was slightly relaxed,
* but the phrase "peer seed" is wired into the seed
* generation here, so we need to keep it that way for
* existing clients, rather than using "channel seed". */
"peer seed", strlen("peer seed"));
}
static void get_channel_seed(const struct secret *hsm_secret,
const struct node_id *peer_id, u64 dbid,
struct secret *channel_seed)
{
struct secret channel_base;
u8 input[sizeof(peer_id->k) + sizeof(dbid)];
/*~ Again, "per-peer" should be "per-channel", but Hysterical Raisins */
const char *info = "per-peer seed";
/*~ We use the DER encoding of the pubkey, because it's platform
* independent. Since the dbid is unique, however, it's completely
* unnecessary, but again, existing users can't be broken. */
/* FIXME: lnd has a nicer BIP32 method for deriving secrets which we
* should migrate to. */
hsm_channel_secret_base(&channel_base, hsm_secret);
memcpy(input, peer_id->k, sizeof(peer_id->k));
BUILD_ASSERT(sizeof(peer_id->k) == PUBKEY_CMPR_LEN);
/*~ For all that talk about platform-independence, note that this
* field is endian-dependent! But let's face it, little-endian won.
* In related news, we don't support EBCDIC or middle-endian. */
memcpy(input + PUBKEY_CMPR_LEN, &dbid, sizeof(dbid));
hkdf_sha256(channel_seed, sizeof(*channel_seed),
input, sizeof(input),
&channel_base, sizeof(channel_base),
info, strlen(info));
}
struct keys {
struct privkey f, r, h, p, d;
struct sha256 shaseed;
};
static void derive_keys(const struct secret *seed, struct keys *keys)
{
hkdf_sha256(keys, sizeof(*keys), NULL, 0, seed, sizeof(*seed),
"c-lightning", strlen("c-lightning"));
}
static void derive_funding_key(const struct secret *hsm_secret,
const struct node_id *peer_id, u64 dbid,
struct pubkey *funding_pubkey)
{
struct secret channel_seed;
struct keys keys;
get_channel_seed(hsm_secret, peer_id, dbid, &channel_seed);
derive_keys(&channel_seed, &keys);
if (!pubkey_from_privkey(&keys.f, funding_pubkey))
abort();
}
static void sqlite3_column_pubkey(struct sqlite3_stmt *stmt, int pos, struct pubkey *dest)
{
bool ok;
assert(sqlite3_column_bytes(stmt, pos) == PUBKEY_CMPR_LEN);
ok = pubkey_from_der(sqlite3_column_blob(stmt, pos), PUBKEY_CMPR_LEN, dest);
assert(ok);
}
static void sqlite3_column_short_channel_id(struct sqlite3_stmt *stmt,
int pos,
struct short_channel_id *dest)
{
const char *source = sqlite3_column_blob(stmt, pos);
size_t sourcelen = sqlite3_column_bytes(stmt, pos);
if (!short_channel_id_from_str(source, sourcelen, dest))
abort();
}
static void copy_column(void *dst, size_t size,
struct sqlite3_stmt *stmt,
int pos)
{
assert(sqlite3_column_bytes(stmt, pos) == size);
memcpy(dst, sqlite3_column_blob(stmt, pos), size);
}
int main(int argc, char *argv[])
{
char *config_dir, *config_filename, *rpc_filename, *hsmfile, *dbfile;
sqlite3 *sql;
sqlite3_stmt *stmt;
int flags = SQLITE_OPEN_READONLY, dberr;
struct secret *hsm_secret;
bool verbose = false;
size_t num, num_ok;
setup_locale();
wally_init(0);
secp256k1_ctx = wally_get_secp_context();
setup_option_allocators();
initial_config_opts(NULL, argc, argv,
&config_filename, &config_dir, &rpc_filename);
opt_register_noarg("-v|--verbose", opt_set_bool, &verbose,
"Print everything");
opt_parse(&argc, argv, opt_log_stderr_exit);
if (argc != 1)
errx(1, "no arguments accepted");
hsmfile = path_join(config_dir, config_dir, "hsm_secret");
dbfile = path_join(config_dir, config_dir, "lightningd.sqlite3");
dberr = sqlite3_open_v2(dbfile, &sql, flags, NULL);
if (dberr != SQLITE_OK)
errx(1, "failed to open database %s: %s", dbfile,
sqlite3_errstr(dberr));
hsm_secret = grab_file(hsmfile, hsmfile);
if (!hsm_secret)
err(1, "failed to read %s", hsmfile);
dberr = sqlite3_prepare_v2(sql,
"SELECT channels.id, peers.node_id, channels.short_channel_id, channels.funding_tx_id, channels.funding_tx_outnum, channels.funding_satoshi, channels.fundingkey_remote, utxoset.scriptpubkey, utxoset.satoshis FROM channels INNER JOIN peers ON channels.peer_id = peers.id LEFT JOIN utxoset on channels.funding_tx_id = utxoset.txid AND channels.funding_tx_outnum = utxoset.outnum;", -1, &stmt, NULL); /* Raw: false positive! */
if (dberr != SQLITE_OK)
errx(1, "failed to prepare query for %s: %s", dbfile,
sqlite3_errstr(dberr));
num = num_ok = 0;
while ((dberr = sqlite3_step(stmt)) == SQLITE_ROW) {
u64 dbid;
struct node_id peer_id;
struct short_channel_id scid;
struct bitcoin_txid funding_txid;
u32 funding_outnum;
u64 funding_satoshis;
struct pubkey remote_fundingkey, local_fundingkey;
u8 *scriptpubkey;
u64 utxo_satoshis;
const tal_t *ctx = tal(dbfile, char);
char txid_hex[65];
u8 *wscript, *expect_scriptpubkey;
num++;
dbid = sqlite3_column_int64(stmt, 0);
copy_column(&peer_id, sizeof(peer_id), stmt, 1);
sqlite3_column_short_channel_id(stmt, 2, &scid);
copy_column(&funding_txid, sizeof(funding_txid), stmt, 3);
if (!bitcoin_txid_to_hex(&funding_txid,
txid_hex, sizeof(txid_hex)))
abort();
funding_outnum = sqlite3_column_int(stmt, 4);
funding_satoshis = sqlite3_column_int64(stmt, 5);
sqlite3_column_pubkey(stmt, 6, &remote_fundingkey);
printf("Channel %s with peer %s: funding %s/%u: ",
short_channel_id_to_str(ctx, &scid),
node_id_to_hexstr(ctx, &peer_id),
txid_hex, funding_outnum);
fflush(stdout);
/* UTXO DNE */
if (sqlite3_column_type(stmt, 7) == SQLITE_NULL) {
printf("*** FATAL *** unknown funding output\n");
continue;
}
scriptpubkey = tal_dup_arr(ctx, u8,
sqlite3_column_blob(stmt, 7),
sqlite3_column_bytes(stmt, 7), 0);
utxo_satoshis = sqlite3_column_int64(stmt, 8);
derive_funding_key(hsm_secret, &peer_id, dbid,
&local_fundingkey);
wscript = bitcoin_redeem_2of2(ctx, &local_fundingkey, &remote_fundingkey);
expect_scriptpubkey = scriptpubkey_p2wsh(ctx, wscript);
if (!memeq(expect_scriptpubkey, tal_bytelen(expect_scriptpubkey),
scriptpubkey, tal_bytelen(scriptpubkey))) {
printf("*** FATAL *** outscript %s should be %s\n",
tal_hex(ctx, scriptpubkey),
tal_hex(ctx, expect_scriptpubkey));
continue;
}
if (utxo_satoshis != funding_satoshis) {
printf("*** FATAL *** amount %"PRIu64" should be %"PRIu64,
funding_satoshis, utxo_satoshis);
continue;
}
if (verbose)
printf("scriptpubkey = %s, expected %s,"
" amount = %"PRIu64", expected %"PRIu64": ",
tal_hex(ctx, scriptpubkey),
tal_hex(ctx, expect_scriptpubkey),
funding_satoshis, utxo_satoshis);
printf("OK\n");
num_ok++;
tal_free(ctx);
}
if (dberr != SQLITE_DONE)
errx(1, "failed iterating database: %s",
sqlite3_errstr(dberr));
if (num_ok == num)
printf("\nCheck passed!\n");
else
errx(1, "%zu channels incorrect.", num - num_ok);
tal_free(config_dir);
}