From 71b8a07c564b1846b0c2c591ad42b4509a38ce58 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 18 Aug 2016 14:25:13 +0930 Subject: [PATCH] db.c: database API. These tables could use a rework, as they largely reflect our internal state. But it's a start. Signed-off-by: Rusty Russell --- INSTALL.md | 3 +- Makefile | 2 +- daemon/Makefile | 2 + daemon/db.c | 1531 +++++++++++++++++++++++++++++++++++++++++++ daemon/db.h | 46 ++ daemon/htlc.c | 10 + daemon/htlc.h | 14 +- daemon/htlc_state.h | 4 +- daemon/lightningd.c | 6 +- daemon/lightningd.h | 3 + daemon/secrets.c | 44 ++ daemon/secrets.h | 10 + daemon/wallet.c | 20 + daemon/wallet.h | 3 + names.c | 12 + names.h | 1 + 16 files changed, 1703 insertions(+), 8 deletions(-) create mode 100644 daemon/db.c create mode 100644 daemon/db.h diff --git a/INSTALL.md b/INSTALL.md index 38abed1a3..688c7ce70 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -6,6 +6,7 @@ You will need several development libraries: * protobuf-c: version 1.1.0 or above. * libsodium: for crypto. * libbase58: for bitcoin's base58 encoding. +* libsqlite3: for database support. You will also need a version of bitcoind with segregated witness support, such as the 0.13 or above. @@ -15,7 +16,7 @@ To Build on Ubuntu 16.04 Get dependencies: ``` -sudo apt-get install libprotobuf-c-dev libsodium-dev libbase58-dev +sudo apt-get install libprotobuf-c-dev libsodium-dev libbase58-dev libsqlite3-dev ``` Clone lightning and initialize submodules: diff --git a/Makefile b/Makefile index 8e0c98812..287472ee0 100644 --- a/Makefile +++ b/Makefile @@ -176,7 +176,7 @@ CWARNFLAGS := -Werror -Wall -Wundef -Wmissing-prototypes -Wmissing-declarations CDEBUGFLAGS := -g -fstack-protector CFLAGS := $(CWARNFLAGS) $(CDEBUGFLAGS) -I $(CCANDIR) -I secp256k1/include/ -I . $(FEATURES) -LDLIBS := -lprotobuf-c -lgmp -lsodium -lbase58 +LDLIBS := -lprotobuf-c -lgmp -lsodium -lbase58 -lsqlite3 $(PROGRAMS): CFLAGS+=-I. default: $(PROGRAMS) daemon-all diff --git a/daemon/Makefile b/daemon/Makefile index f423e5530..825b08c51 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -20,6 +20,7 @@ DAEMON_SRC := \ daemon/commit_tx.c \ daemon/controlled_time.c \ daemon/cryptopkt.c \ + daemon/db.c \ daemon/dns.c \ daemon/htlc.c \ daemon/jsonrpc.c \ @@ -58,6 +59,7 @@ DAEMON_HEADERS := \ daemon/configdir.h \ daemon/controlled_time.h \ daemon/cryptopkt.h \ + daemon/db.h \ daemon/dns.h \ daemon/htlc.h \ daemon/htlc_state.h \ diff --git a/daemon/db.c b/daemon/db.c new file mode 100644 index 000000000..0f2fc6329 --- /dev/null +++ b/daemon/db.c @@ -0,0 +1,1531 @@ +#include "bitcoin/pullpush.h" +#include "commit_tx.h" +#include "db.h" +#include "htlc.h" +#include "lightningd.h" +#include "log.h" +#include "names.h" +#include "netaddr.h" +#include "routing.h" +#include "secrets.h" +#include "utils.h" +#include "wallet.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DB_FILE "lightning.sqlite3" + +/* They don't use stdint types. */ +#define PRIuSQLITE64 "llu" + +struct db { + bool in_transaction; + sqlite3 *sql; +}; + +static void close_db(struct db *db) +{ + sqlite3_close(db->sql); +} + +/* We want a string, not an 'unsigned char *' thanks! */ +static const char *sqlite3_column_str(sqlite3_stmt *stmt, int iCol) +{ + return cast_signed(const char *, sqlite3_column_text(stmt, iCol)); +} + +#define SQL_PUBKEY "BINARY(33)" +#define SQL_PRIVKEY "BINARY(32)" +#define SQL_SIGNATURE "BINARY(64)" +#define SQL_TXID "BINARY(32)" +#define SQL_RHASH "BINARY(32)" +#define SQL_SHA256 "BINARY(32)" +#define SQL_R "BINARY(32)" + +/* 8 + 4 + (8 + 32) * (64 + 1) */ +#define SHACHAIN_SIZE 2612 +#define SQL_SHACHAIN "BINARY(2612)" + +/* FIXME: Should be fixed size. */ +#define SQL_ROUTING "BLOB" + +static char *PRINTF_FMT(3,4) + db_exec(const tal_t *ctx, + struct lightningd_state *dstate, const char *fmt, ...) +{ + va_list ap; + char *cmd, *errmsg; + int err; + + va_start(ap, fmt); + cmd = tal_vfmt(ctx, fmt, ap); + va_end(ap); + + err = sqlite3_exec(dstate->db->sql, cmd, NULL, NULL, &errmsg); + if (err != SQLITE_OK) { + char *e = tal_fmt(ctx, "%s:%s:%s", + sqlite3_errstr(err), cmd, errmsg); + sqlite3_free(errmsg); + tal_free(cmd); + return e; + } + tal_free(cmd); + return NULL; +} + +static char *sql_hex_or_null(const tal_t *ctx, const void *buf, size_t len) +{ + char *r; + + if (!buf) + return "NULL"; + r = tal_arr(ctx, char, 3 + hex_str_size(len)); + r[0] = 'x'; + r[1] = '\''; + hex_encode(buf, len, r+2, hex_str_size(len)); + r[2+hex_str_size(len)-1] = '\''; + r[2+hex_str_size(len)] = '\0'; + return r; +} + +static void from_sql_blob(sqlite3_stmt *stmt, int idx, void *p, size_t n) +{ + if (sqlite3_column_bytes(stmt, idx) != n) + fatal("db:wrong bytes %i not %zu", + sqlite3_column_bytes(stmt, idx), n); + memcpy(p, sqlite3_column_blob(stmt, idx), n); +} + +static u8 *tal_sql_blob(const tal_t *ctx, sqlite3_stmt *stmt, int idx) +{ + u8 *p; + + if (sqlite3_column_type(stmt, idx) == SQLITE_NULL) + return NULL; + + p = tal_arr(ctx, u8, sqlite3_column_bytes(stmt, idx)); + from_sql_blob(stmt, idx, p, tal_count(p)); + return p; +} + +static void pubkey_from_sql(secp256k1_context *secpctx, + sqlite3_stmt *stmt, int idx, struct pubkey *pk) +{ + if (!pubkey_from_der(secpctx, sqlite3_column_blob(stmt, idx), + sqlite3_column_bytes(stmt, idx), pk)) + fatal("db:bad pubkey length %i", + sqlite3_column_bytes(stmt, idx)); +} + +static void sha256_from_sql(sqlite3_stmt *stmt, int idx, struct sha256 *sha) +{ + from_sql_blob(stmt, idx, sha, sizeof(*sha)); +} + +static void sig_from_sql(secp256k1_context *secpctx, + sqlite3_stmt *stmt, int idx, + struct bitcoin_signature *sig) +{ + u8 compact[64]; + + from_sql_blob(stmt, idx, compact, sizeof(compact)); + if (secp256k1_ecdsa_signature_parse_compact(secpctx, &sig->sig.sig, + compact) != 1) + fatal("db:bad signature blob"); + sig->stype = SIGHASH_ALL; +} + +static char *sig_to_sql(const tal_t *ctx, + secp256k1_context *secpctx, + const struct bitcoin_signature *sig) +{ + u8 compact[64]; + + if (!sig) + return sql_hex_or_null(ctx, NULL, 0); + + assert(sig->stype == SIGHASH_ALL); + secp256k1_ecdsa_signature_serialize_compact(secpctx, compact, + &sig->sig.sig); + return sql_hex_or_null(ctx, compact, sizeof(compact)); +} + +static void db_load_wallet(struct lightningd_state *dstate) +{ + int err; + sqlite3_stmt *stmt; + + err = sqlite3_prepare_v2(dstate->db->sql, "SELECT * FROM wallet;", -1, + &stmt, NULL); + + if (err != SQLITE_OK) + fatal("db_load_wallet:prepare gave %s:%s", + sqlite3_errstr(err), sqlite3_errmsg(dstate->db->sql)); + + while ((err = sqlite3_step(stmt)) != SQLITE_DONE) { + struct privkey privkey; + if (err != SQLITE_ROW) + fatal("db_load_wallet:step gave %s:%s", + sqlite3_errstr(err), + sqlite3_errmsg(dstate->db->sql)); + if (sqlite3_column_count(stmt) != 1) + fatal("db_load_wallet:step gave %i cols, not 1", + sqlite3_column_count(stmt)); + from_sql_blob(stmt, 0, &privkey, sizeof(privkey)); + if (!restore_wallet_address(dstate, &privkey)) + fatal("db_load_wallet:bad privkey"); + } + err = sqlite3_finalize(stmt); + if (err != SQLITE_OK) + fatal("db_load_wallet:finalize gave %s:%s", + sqlite3_errstr(err), + sqlite3_errmsg(dstate->db->sql)); +} + +void db_add_wallet_privkey(struct lightningd_state *dstate, + const struct privkey *privkey) +{ + char *ctx = tal(dstate, char); + char *err; + + log_debug(dstate->base_log, "%s", __func__); + err = db_exec(ctx, dstate, + "INSERT INTO wallet VALUES (x'%s');", + tal_hexstr(ctx, privkey, sizeof(*privkey))); + if (err) + fatal("db_add_wallet_privkey:%s", err); +} + +static void load_peer_address(struct peer *peer) +{ + int err; + sqlite3_stmt *stmt; + sqlite3 *sql = peer->dstate->db->sql; + char *ctx = tal(peer, char); + const char *select; + bool addr_set = false; + + select = tal_fmt(ctx, + "SELECT * FROM peer_address WHERE peer = x'%s';", + pubkey_to_hexstr(ctx, peer->dstate->secpctx, peer->id)); + + err = sqlite3_prepare_v2(sql, select, -1, &stmt, NULL); + if (err != SQLITE_OK) + fatal("load_peer_address:prepare gave %s:%s", + sqlite3_errstr(err), sqlite3_errmsg(sql)); + + while ((err = sqlite3_step(stmt)) != SQLITE_DONE) { + if (err != SQLITE_ROW) + fatal("load_peer_address:step gave %s:%s", + sqlite3_errstr(err), sqlite3_errmsg(sql)); + if (addr_set) + fatal("load_peer_address: two addresses for '%s'", + select); + if (!netaddr_from_blob(sqlite3_column_blob(stmt, 1), + sqlite3_column_bytes(stmt, 1), + &peer->addr)) + fatal("load_peer_address: unparsable addresses for '%s'", + select); + addr_set = true; + peer->log = new_log(peer, peer->dstate->log_record, "%s%s:", + log_prefix(peer->dstate->base_log), + netaddr_name(peer, &peer->addr)); + } + + if (!addr_set) + fatal("load_peer_address: no addresses for '%s'", select); + + tal_free(ctx); +} + +static void load_peer_secrets(struct peer *peer) +{ + int err; + sqlite3_stmt *stmt; + sqlite3 *sql = peer->dstate->db->sql; + char *ctx = tal(peer, char); + const char *select; + bool secrets_set = false; + + select = tal_fmt(ctx, + "SELECT * FROM peer_secrets WHERE peer = x'%s';", + pubkey_to_hexstr(ctx, peer->dstate->secpctx, peer->id)); + + err = sqlite3_prepare_v2(sql, select, -1, &stmt, NULL); + if (err != SQLITE_OK) + fatal("load_peer_secrets:prepare gave %s:%s", + sqlite3_errstr(err), sqlite3_errmsg(sql)); + + while ((err = sqlite3_step(stmt)) != SQLITE_DONE) { + if (err != SQLITE_ROW) + fatal("load_peer_secrets:step gave %s:%s", + sqlite3_errstr(err), sqlite3_errmsg(sql)); + if (secrets_set) + fatal("load_peer_secrets: two secrets for '%s'", + select); + peer_set_secrets_from_db(peer, + sqlite3_column_blob(stmt, 1), + sqlite3_column_bytes(stmt, 1), + sqlite3_column_blob(stmt, 2), + sqlite3_column_bytes(stmt, 2), + sqlite3_column_blob(stmt, 3), + sqlite3_column_bytes(stmt, 3)); + secrets_set = true; + } + + if (!secrets_set) + fatal("load_peer_secrets: no secrets for '%s'", select); + tal_free(ctx); +} + +static void load_peer_anchor(struct peer *peer) +{ + int err; + sqlite3_stmt *stmt; + sqlite3 *sql = peer->dstate->db->sql; + char *ctx = tal(peer, char); + const char *select; + bool anchor_set = false; + + /* CREATE TABLE anchors (peer "SQL_PUBKEY", txid "SQL_TXID", idx INT, amount INT, ok_depth INT, min_depth INT, bool ours); */ + select = tal_fmt(ctx, + "SELECT * FROM anchors WHERE peer = x'%s';", + pubkey_to_hexstr(ctx, peer->dstate->secpctx, peer->id)); + + err = sqlite3_prepare_v2(sql, select, -1, &stmt, NULL); + if (err != SQLITE_OK) + fatal("load_peer_anchor:prepare gave %s:%s", + sqlite3_errstr(err), sqlite3_errmsg(sql)); + + while ((err = sqlite3_step(stmt)) != SQLITE_DONE) { + if (err != SQLITE_ROW) + fatal("load_peer_anchor:step gave %s:%s", + sqlite3_errstr(err), sqlite3_errmsg(sql)); + if (anchor_set) + fatal("load_peer_anchor: two anchors for '%s'", + select); + from_sql_blob(stmt, 1, + &peer->anchor.txid, sizeof(peer->anchor.txid)); + peer->anchor.index = sqlite3_column_int64(stmt, 2); + peer->anchor.satoshis = sqlite3_column_int64(stmt, 3); + peer->anchor.ours = sqlite3_column_int(stmt, 6); + + /* FIXME: Do timeout! */ + peer_watch_anchor(peer, + sqlite3_column_int(stmt, 4), + BITCOIN_ANCHOR_DEPTHOK, INPUT_NONE); + peer->anchor.min_depth = sqlite3_column_int(stmt, 5); + anchor_set = true; + } + + if (!anchor_set) + fatal("load_peer_anchor: no anchor for '%s'", select); + tal_free(ctx); +} + +static void load_peer_visible_state(struct peer *peer) +{ + int err; + sqlite3_stmt *stmt; + sqlite3 *sql = peer->dstate->db->sql; + char *ctx = tal(peer, char); + const char *select; + bool visible_set = false; + + /* "CREATE TABLE their_visible_state (peer "SQL_PUBKEY", offered_anchor BOOLEAN, commitkey "SQL_PUBKEY", finalkey "SQL_PUBKEY", locktime INT, mindepth INT, commit_fee_rate INT, next_revocation_hash "SQL_SHA256", PRIMARY KEY(peer)); */ + select = tal_fmt(ctx, + "SELECT * FROM their_visible_state WHERE peer = x'%s';", + pubkey_to_hexstr(ctx, peer->dstate->secpctx, peer->id)); + + err = sqlite3_prepare_v2(sql, select, -1, &stmt, NULL); + if (err != SQLITE_OK) + fatal("load_peer_visible_state:prepare gave %s:%s", + sqlite3_errstr(err), sqlite3_errmsg(sql)); + + while ((err = sqlite3_step(stmt)) != SQLITE_DONE) { + if (err != SQLITE_ROW) + fatal("load_peer_visible_state:step gave %s:%s", + sqlite3_errstr(err), sqlite3_errmsg(sql)); + + if (sqlite3_column_count(stmt) != 8) + fatal("load_peer_visible_state:step gave %i cols, not 8", + sqlite3_column_count(stmt)); + + if (visible_set) + fatal("load_peer_visible_state: two states for %s", select); + visible_set = true; + + if (sqlite3_column_int64(stmt, 1)) + peer->remote.offer_anchor = CMD_OPEN_WITH_ANCHOR; + else + peer->remote.offer_anchor = CMD_OPEN_WITHOUT_ANCHOR; + pubkey_from_sql(peer->dstate->secpctx, stmt, 2, + &peer->remote.commitkey); + pubkey_from_sql(peer->dstate->secpctx, stmt, 3, + &peer->remote.finalkey); + peer->remote.locktime.locktime = sqlite3_column_int(stmt, 4); + peer->remote.mindepth = sqlite3_column_int(stmt, 5); + peer->remote.commit_fee_rate = sqlite3_column_int64(stmt, 6); + sha256_from_sql(stmt, 7, &peer->remote.next_revocation_hash); + log_debug(peer->log, "%s:next_revocation_hash=%s", + __func__, + tal_hexstr(ctx, &peer->remote.next_revocation_hash, + sizeof(peer->remote.next_revocation_hash))); + + /* Now we can fill in anchor witnessscript. */ + peer->anchor.witnessscript + = bitcoin_redeem_2of2(peer, peer->dstate->secpctx, + &peer->local.commitkey, + &peer->remote.commitkey); + } + + if (!visible_set) + fatal("load_peer_visible_state: no result '%s'", select); + + err = sqlite3_finalize(stmt); + if (err != SQLITE_OK) + fatal("load_peer_visible_state:finalize gave %s:%s", + sqlite3_errstr(err), sqlite3_errmsg(sql)); + tal_free(ctx); +} + +static void load_peer_commit_info(struct peer *peer) +{ + int err; + sqlite3_stmt *stmt; + sqlite3 *sql = peer->dstate->db->sql; + char *ctx = tal(peer, char); + const char *select; + + select = tal_fmt(ctx, + "SELECT * FROM commit_info WHERE peer = x'%s';", + pubkey_to_hexstr(ctx, peer->dstate->secpctx, peer->id)); + + err = sqlite3_prepare_v2(sql, select, -1, &stmt, NULL); + if (err != SQLITE_OK) + fatal("load_peer_commit_info:prepare gave %s:%s", + sqlite3_errstr(err), sqlite3_errmsg(sql)); + + while ((err = sqlite3_step(stmt)) != SQLITE_DONE) { + struct commit_info **cip, *ci; + + if (err != SQLITE_ROW) + fatal("load_peer_commit_info:step gave %s:%s", + sqlite3_errstr(err), sqlite3_errmsg(sql)); + + /* peer "SQL_PUBKEY", side TEXT, commit_num INT, revocation_hash "SQL_SHA256", sig "SQL_SIGNATURE", xmit_order INT, prev_revocation_hash "SQL_SHA256", */ + if (sqlite3_column_count(stmt) != 7) + fatal("load_peer_commit_info:step gave %i cols, not 7", + sqlite3_column_count(stmt)); + + if (streq(sqlite3_column_str(stmt, 1), "OURS")) + cip = &peer->local.commit; + else { + if (!streq(sqlite3_column_str(stmt, 1), "THEIRS")) + fatal("load_peer_commit_info:bad side %s", + sqlite3_column_str(stmt, 1)); + cip = &peer->remote.commit; + /* This is a hack where we temporarily store their + * previous revocation hash before we get their + * revocation. */ + if (sqlite3_column_type(stmt, 6) != SQLITE_NULL) { + peer->their_prev_revocation_hash + = tal(peer, struct sha256); + sha256_from_sql(stmt, 6, + peer->their_prev_revocation_hash); + } + } + + /* Do we already have this one? */ + if (*cip) + fatal("load_peer_commit_info:duplicate side %s", + sqlite3_column_str(stmt, 1)); + + *cip = ci = new_commit_info(peer, sqlite3_column_int64(stmt, 2)); + sha256_from_sql(stmt, 3, &ci->revocation_hash); + ci->order = sqlite3_column_int64(stmt, 4); + + if (sqlite3_column_type(stmt, 5) == SQLITE_NULL) + ci->sig = NULL; + else { + ci->sig = tal(ci, struct bitcoin_signature); + sig_from_sql(peer->dstate->secpctx, stmt, 5, ci->sig); + } + + /* Set once we have updated HTLCs. */ + ci->cstate = NULL; + ci->tx = NULL; + } + + err = sqlite3_finalize(stmt); + if (err != SQLITE_OK) + fatal("load_peer_commit_info:finalize gave %s:%s", + sqlite3_errstr(err), sqlite3_errmsg(sql)); + tal_free(ctx); + + if (!peer->local.commit) + fatal("load_peer_commit_info:no local commit info found"); + if (!peer->remote.commit) + fatal("load_peer_commit_info:no remote commit info found"); +} + +/* This htlc no longer committed; either resolved or failed. */ +static void htlc_resolved(struct channel_state *cstate, const struct htlc *htlc) +{ + if (htlc->r) + cstate_fulfill_htlc(cstate, htlc); + else + cstate_fail_htlc(cstate, htlc); +} + +/* As we load the HTLCs, we apply them to get the final channel_state. + * We also get the last used htlc id. + * This is slow, but sure. */ +static void load_peer_htlcs(struct peer *peer) +{ + int err; + sqlite3_stmt *stmt; + sqlite3 *sql = peer->dstate->db->sql; + char *ctx = tal(peer, char); + const char *select; + bool to_them_only, to_us_only; + + select = tal_fmt(ctx, + "SELECT * FROM htlcs WHERE peer = x'%s' ORDER BY id;", + pubkey_to_hexstr(ctx, peer->dstate->secpctx, peer->id)); + + err = sqlite3_prepare_v2(sql, select, -1, &stmt, NULL); + if (err != SQLITE_OK) + fatal("load_peer_htlcs:prepare gave %s:%s", + sqlite3_errstr(err), sqlite3_errmsg(sql)); + + peer->local.commit->cstate = initial_cstate(peer, + peer->anchor.satoshis, + peer->local.commit_fee_rate, + peer->local.offer_anchor + == CMD_OPEN_WITH_ANCHOR ? + OURS : THEIRS); + peer->remote.commit->cstate = initial_cstate(peer, + peer->anchor.satoshis, + peer->remote.commit_fee_rate, + peer->local.offer_anchor + == CMD_OPEN_WITH_ANCHOR ? + OURS : THEIRS); + + /* We rebuild cstate by running *every* HTLC through. */ + while ((err = sqlite3_step(stmt)) != SQLITE_DONE) { + struct htlc *htlc; + struct sha256 rhash; + enum htlc_state hstate; + + if (err != SQLITE_ROW) + fatal("load_peer_htlcs:step gave %s:%s", + sqlite3_errstr(err), sqlite3_errmsg(sql)); + + if (sqlite3_column_count(stmt) != 10) + fatal("load_peer_htlcs:step gave %i cols, not 10", + sqlite3_column_count(stmt)); + /* CREATE TABLE htlcs (peer "SQL_PUBKEY", id INT, state TEXT, msatoshis INT, expiry INT, rhash "SQL_RHASH", r "SQL_R", routing "SQL_ROUTING", src_peer "SQL_PUBKEY", src_id INT, PRIMARY KEY(peer, id)); */ + sha256_from_sql(stmt, 5, &rhash); + + hstate = htlc_state_from_name(sqlite3_column_str(stmt, 2)); + if (hstate == HTLC_STATE_INVALID) + fatal("load_peer_htlcs:invalid state %s", + sqlite3_column_str(stmt, 2)); + htlc = peer_new_htlc(peer, + sqlite3_column_int64(stmt, 1), + sqlite3_column_int64(stmt, 3), + &rhash, + sqlite3_column_int64(stmt, 4), + sqlite3_column_blob(stmt, 7), + sqlite3_column_bytes(stmt, 7), + NULL, + hstate); + + if (sqlite3_column_type(stmt, 6) != SQLITE_NULL) { + htlc->r = tal(htlc, struct rval); + from_sql_blob(stmt, 6, htlc->r, sizeof(*htlc->r)); + } + + log_debug(peer->log, "Loaded %s HTLC %"PRIu64" (%s)", + htlc_owner(htlc) == LOCAL ? "local" : "remote", + htlc->id, htlc_state_name(htlc->state)); + + if (htlc_owner(htlc) == LOCAL + && htlc->id >= peer->htlc_id_counter) + peer->htlc_id_counter = htlc->id + 1; + + /* Update cstate with this HTLC. */ + if (htlc_has(htlc, HTLC_LOCAL_F_WAS_COMMITTED)) { + log_debug(peer->log, " Local committed"); + if (!cstate_add_htlc(peer->local.commit->cstate, htlc)) + fatal("load_peer_htlcs:can't add local HTLC"); + + if (!htlc_has(htlc, HTLC_LOCAL_F_COMMITTED)) { + log_debug(peer->log, " Local %s", + htlc->r ? "resolved" : "failed"); + htlc_resolved(peer->local.commit->cstate, htlc); + } + } + + if (htlc_has(htlc, HTLC_REMOTE_F_WAS_COMMITTED)) { + log_debug(peer->log, " Remote committed"); + if (!cstate_add_htlc(peer->remote.commit->cstate, htlc)) + fatal("load_peer_htlcs:can't add remote HTLC"); + if (!htlc_has(htlc, HTLC_REMOTE_F_COMMITTED)) { + log_debug(peer->log, " Remote %s", + htlc->r ? "resolved" : "failed"); + htlc_resolved(peer->remote.commit->cstate, htlc); + } + } + } + + err = sqlite3_finalize(stmt); + if (err != SQLITE_OK) + fatal("load_peer_htlcs:finalize gave %s:%s", + sqlite3_errstr(err), sqlite3_errmsg(sql)); + + /* Update commit->tx and commit->map */ + peer->local.commit->tx = create_commit_tx(peer->local.commit, + peer, + &peer->local.commit->revocation_hash, + peer->local.commit->cstate, + LOCAL, &to_them_only); + bitcoin_txid(peer->local.commit->tx, &peer->local.commit->txid); + + peer->remote.commit->tx = create_commit_tx(peer->remote.commit, + peer, + &peer->remote.commit->revocation_hash, + peer->remote.commit->cstate, + REMOTE, &to_us_only); + bitcoin_txid(peer->remote.commit->tx, &peer->remote.commit->txid); + + peer->remote.staging_cstate = copy_cstate(peer, peer->remote.commit->cstate); + peer->local.staging_cstate = copy_cstate(peer, peer->local.commit->cstate); + log_debug(peer->log, "Local staging: pay %u/%u fee %u/%u htlcs %u/%u", + peer->local.staging_cstate->side[OURS].pay_msat, + peer->local.staging_cstate->side[THEIRS].pay_msat, + peer->local.staging_cstate->side[OURS].fee_msat, + peer->local.staging_cstate->side[THEIRS].fee_msat, + peer->local.staging_cstate->side[OURS].num_htlcs, + peer->local.staging_cstate->side[THEIRS].num_htlcs); + log_debug(peer->log, "Remote staging: pay %u/%u fee %u/%u htlcs %u/%u", + peer->remote.staging_cstate->side[OURS].pay_msat, + peer->remote.staging_cstate->side[THEIRS].pay_msat, + peer->remote.staging_cstate->side[OURS].fee_msat, + peer->remote.staging_cstate->side[THEIRS].fee_msat, + peer->remote.staging_cstate->side[OURS].num_htlcs, + peer->remote.staging_cstate->side[THEIRS].num_htlcs); + + tal_free(ctx); +} + +/* FIXME: A real database person would do this in a single clause along + * with loading the htlcs in the first place! */ +static void connect_htlc_src(struct lightningd_state *dstate) +{ + sqlite3 *sql = dstate->db->sql; + int err; + sqlite3_stmt *stmt; + char *ctx = tal(dstate, char); + const char *select; + + select = tal_fmt(ctx, + "SELECT peer,id,state,src_peer,src_id FROM htlcs WHERE src_peer IS NOT NULL AND state <> 'RCVD_REMOVE_ACK_REVOCATION' AND state <> 'SENT_REMOVE_ACK_REVOCATION';"); + + err = sqlite3_prepare_v2(sql, select, -1, &stmt, NULL); + if (err != SQLITE_OK) + fatal("connect_htlc_src:%s gave %s:%s", + select, sqlite3_errstr(err), sqlite3_errmsg(sql)); + + while ((err = sqlite3_step(stmt)) != SQLITE_DONE) { + struct pubkey id; + struct peer *peer; + struct htlc *htlc; + enum htlc_state s; + + if (err != SQLITE_ROW) + fatal("connect_htlc_src:step gave %s:%s", + sqlite3_errstr(err), sqlite3_errmsg(sql)); + + pubkey_from_sql(dstate->secpctx, stmt, 0, &id); + peer = find_peer(dstate, &id); + if (!peer) + continue; + + s = htlc_state_from_name(sqlite3_column_str(stmt, 2)); + if (s == HTLC_STATE_INVALID) + fatal("connect_htlc_src:unknown state %s", + sqlite3_column_str(stmt, 2)); + + htlc = htlc_get(&peer->htlcs, sqlite3_column_int64(stmt, 1), + htlc_state_owner(s)); + if (!htlc) + fatal("connect_htlc_src:unknown htlc %"PRIuSQLITE64" state %s", + sqlite3_column_int64(stmt, 1), + sqlite3_column_str(stmt, 2)); + + pubkey_from_sql(dstate->secpctx, stmt, 4, &id); + peer = find_peer(dstate, &id); + if (!peer) + fatal("connect_htlc_src:unknown src peer %s", + tal_hexstr(dstate, &id, sizeof(id))); + + /* Source must be a HTLC they offered. */ + htlc->src = htlc_get(&peer->htlcs, + sqlite3_column_int64(stmt, 4), + REMOTE); + if (!htlc->src) + fatal("connect_htlc_src:unknown src htlc"); + } + + err = sqlite3_finalize(stmt); + if (err != SQLITE_OK) + fatal("load_peer_htlcs:finalize gave %s:%s", + sqlite3_errstr(err), + sqlite3_errmsg(dstate->db->sql)); + tal_free(ctx); +} + +/* FIXME: Expose pull/push and use that here. */ +static const char *linearize_shachain(const tal_t *ctx, + const struct shachain *shachain) +{ + size_t i; + u8 *p = tal_arr(ctx, u8, 0); + const char *str; + + push_le64(shachain->min_index, push, &p); + push_le32(shachain->num_valid, push, &p); + for (i = 0; i < shachain->num_valid; i++) { + push_le64(shachain->known[i].index, push, &p); + push(&shachain->known[i].hash, sizeof(shachain->known[i].hash), + &p); + } + for (i = shachain->num_valid; i < ARRAY_SIZE(shachain->known); i++) { + static u8 zeroes[sizeof(shachain->known[0].hash)]; + push_le64(0, push, &p); + push(zeroes, sizeof(zeroes), &p); + } + + assert(tal_count(p) == SHACHAIN_SIZE); + str = tal_hexstr(ctx, p, tal_count(p)); + tal_free(p); + return str; +} + +static bool delinearize_shachain(struct shachain *shachain, + const void *data, size_t len) +{ + size_t i; + const u8 *p = data; + + shachain->min_index = pull_le64(&p, &len); + shachain->num_valid = pull_le32(&p, &len); + for (i = 0; i < ARRAY_SIZE(shachain->known); i++) { + shachain->known[i].index = pull_le64(&p, &len); + pull(&p, &len, &shachain->known[i].hash, + sizeof(shachain->known[i].hash)); + } + return p && len == 0; +} + +static void load_peer_shachain(struct peer *peer) +{ + int err; + sqlite3_stmt *stmt; + sqlite3 *sql = peer->dstate->db->sql; + char *ctx = tal(peer, char); + bool shachain_found = false; + const char *select; + + select = tal_fmt(ctx, + "SELECT * FROM shachain WHERE peer = x'%s';", + pubkey_to_hexstr(ctx, peer->dstate->secpctx, peer->id)); + + err = sqlite3_prepare_v2(sql, select, -1, &stmt, NULL); + if (err != SQLITE_OK) + fatal("load_peer_shachain:prepare gave %s:%s", + sqlite3_errstr(err), sqlite3_errmsg(sql)); + + while ((err = sqlite3_step(stmt)) != SQLITE_DONE) { + const char *hexstr; + + if (err != SQLITE_ROW) + fatal("load_peer_shachain:step gave %s:%s", + sqlite3_errstr(err), sqlite3_errmsg(sql)); + + /* shachain (peer "SQL_PUBKEY", shachain BINARY(%zu) */ + if (sqlite3_column_count(stmt) != 2) + fatal("load_peer_shachain:step gave %i cols, not 2", + sqlite3_column_count(stmt)); + + if (shachain_found) + fatal("load_peer_shachain:multiple shachains?"); + + hexstr = tal_hexstr(ctx, sqlite3_column_blob(stmt, 1), + sqlite3_column_bytes(stmt, 1)); + if (!delinearize_shachain(&peer->their_preimages, + sqlite3_column_blob(stmt, 1), + sqlite3_column_bytes(stmt, 1))) + fatal("load_peer_shachain:invalid shachain %s", + hexstr); + shachain_found = true; + } + + if (!shachain_found) + fatal("load_peer_shachain:no shachain"); + tal_free(ctx); +} + +/* We may not have one, and that's OK. */ +static void load_peer_closing(struct peer *peer) +{ + int err; + sqlite3_stmt *stmt; + sqlite3 *sql = peer->dstate->db->sql; + char *ctx = tal(peer, char); + bool closing_found = false; + const char *select; + + select = tal_fmt(ctx, + "SELECT * FROM closing WHERE peer = x'%s';", + pubkey_to_hexstr(ctx, peer->dstate->secpctx, peer->id)); + + err = sqlite3_prepare_v2(sql, select, -1, &stmt, NULL); + if (err != SQLITE_OK) + fatal("load_peer_closing:prepare gave %s:%s", + sqlite3_errstr(err), sqlite3_errmsg(sql)); + + while ((err = sqlite3_step(stmt)) != SQLITE_DONE) { + if (err != SQLITE_ROW) + fatal("load_peer_closing:step gave %s:%s", + sqlite3_errstr(err), sqlite3_errmsg(sql)); + + /* CREATE TABLE closing (peer "SQL_PUBKEY", our_fee INTEGER, their_fee INTEGER, their_sig "SQL_SIGNATURE", our_script BLOB, their_script BLOB, shutdown_order INTEGER, closing_order INTEGER, sigs_in INTEGER, PRIMARY KEY(peer)); */ + if (sqlite3_column_count(stmt) != 9) + fatal("load_peer_closing:step gave %i cols, not 9", + sqlite3_column_count(stmt)); + + if (closing_found) + fatal("load_peer_closing:multiple closing?"); + + peer->closing.our_fee = sqlite3_column_int64(stmt, 1); + peer->closing.their_fee = sqlite3_column_int64(stmt, 2); + if (sqlite3_column_type(stmt, 3) == SQLITE_NULL) + peer->closing.their_sig = NULL; + else { + peer->closing.their_sig = tal(peer, + struct bitcoin_signature); + sig_from_sql(peer->dstate->secpctx, stmt, 3, + peer->closing.their_sig); + } + peer->closing.our_script = tal_sql_blob(peer, stmt, 4); + peer->closing.their_script = tal_sql_blob(peer, stmt, 5); + peer->closing.shutdown_order = sqlite3_column_int64(stmt, 6); + peer->closing.closing_order = sqlite3_column_int64(stmt, 7); + peer->closing.sigs_in = sqlite3_column_int64(stmt, 8); + closing_found = true; + } + tal_free(ctx); +} + +/* FIXME: much of this is redundant. */ +static void restore_peer_local_visible_state(struct peer *peer) +{ + if (peer->remote.offer_anchor == CMD_OPEN_WITH_ANCHOR) + peer->local.offer_anchor = CMD_OPEN_WITHOUT_ANCHOR; + else + peer->local.offer_anchor = CMD_OPEN_WITH_ANCHOR; + + /* peer->local.commitkey and peer->local.finalkey set by + * peer_set_secrets_from_db(). */ + memcheck(&peer->local.commitkey, sizeof(peer->local.commitkey)); + memcheck(&peer->local.finalkey, sizeof(peer->local.finalkey)); + /* These set in new_peer */ + memcheck(&peer->local.locktime, sizeof(peer->local.locktime)); + memcheck(&peer->local.mindepth, sizeof(peer->local.mindepth)); + /* This set in db_load_peers */ + memcheck(&peer->local.commit_fee_rate, + sizeof(peer->local.commit_fee_rate)); + + peer_get_revocation_hash(peer, + peer->local.commit->commit_num + 1, + &peer->local.next_revocation_hash); + + if (state_is_normal(peer->state)) + peer->nc = add_connection(peer->dstate, + &peer->dstate->id, peer->id, + peer->dstate->config.fee_base, + peer->dstate->config.fee_per_satoshi, + peer->dstate->config.min_htlc_expiry, + peer->dstate->config.min_htlc_expiry); + + peer->their_commitsigs = peer->local.commit->commit_num + 1; + /* If they created anchor, they didn't send a sig for first commit */ + if (!peer->anchor.ours) + peer->their_commitsigs--; + + peer->order_counter = 0; + if (peer->local.commit->order + 1 > peer->order_counter) + peer->order_counter = peer->local.commit->order + 1; + if (peer->remote.commit->order + 1 > peer->order_counter) + peer->order_counter = peer->remote.commit->order + 1; + if (peer->closing.closing_order + 1 > peer->order_counter) + peer->order_counter = peer->closing.closing_order + 1; + if (peer->closing.shutdown_order + 1 > peer->order_counter) + peer->order_counter = peer->closing.shutdown_order + 1; +} + +static void db_load_peers(struct lightningd_state *dstate) +{ + int err; + sqlite3_stmt *stmt; + struct peer *peer; + + err = sqlite3_prepare_v2(dstate->db->sql, "SELECT * FROM peers;", -1, + &stmt, NULL); + + if (err != SQLITE_OK) + fatal("db_load_peers:prepare gave %s:%s", + sqlite3_errstr(err), sqlite3_errmsg(dstate->db->sql)); + + while ((err = sqlite3_step(stmt)) != SQLITE_DONE) { + enum state state; + + if (err != SQLITE_ROW) + fatal("db_load_peers:step gave %s:%s", + sqlite3_errstr(err), + sqlite3_errmsg(dstate->db->sql)); + if (sqlite3_column_count(stmt) != 4) + fatal("db_load_peers:step gave %i cols, not 4", + sqlite3_column_count(stmt)); + state = name_to_state(sqlite3_column_str(stmt, 1)); + if (state == STATE_MAX) + fatal("db_load_peers:unknown state %s", + sqlite3_column_str(stmt, 1)); + peer = new_peer(dstate, state, sqlite3_column_int(stmt, 2) ? + CMD_OPEN_WITH_ANCHOR : CMD_OPEN_WITHOUT_ANCHOR); + peer->htlc_id_counter = 0; + peer->id = tal(peer, struct pubkey); + pubkey_from_sql(dstate->secpctx, stmt, 0, peer->id); + peer->local.commit_fee_rate = sqlite3_column_int64(stmt, 3); + log_debug(dstate->base_log, "%s:%s:", + __func__, state_name(peer->state)); + log_add_struct(dstate->base_log, "%s", struct pubkey, peer->id); + } + err = sqlite3_finalize(stmt); + if (err != SQLITE_OK) + fatal("db_load_peers:finalize gave %s:%s", + sqlite3_errstr(err), + sqlite3_errmsg(dstate->db->sql)); + + list_for_each(&dstate->peers, peer, list) { + load_peer_address(peer); + load_peer_secrets(peer); + load_peer_closing(peer); + peer->anchor.min_depth = 0; + if (peer->state >= STATE_OPEN_WAITING_OURANCHOR + && !state_is_error(peer->state)) { + load_peer_anchor(peer); + load_peer_visible_state(peer); + load_peer_shachain(peer); + load_peer_commit_info(peer); + load_peer_htlcs(peer); + restore_peer_local_visible_state(peer); + } + } + + connect_htlc_src(dstate); +} + +static void db_load(struct lightningd_state *dstate) +{ + db_load_wallet(dstate); + + db_load_peers(dstate); +} + +void db_init(struct lightningd_state *dstate) +{ + int err; + char *errmsg; + bool created = false; + + if (SQLITE_VERSION_NUMBER != sqlite3_libversion_number()) + fatal("SQLITE version mistmatch: compiled %u, now %u", + SQLITE_VERSION_NUMBER, sqlite3_libversion_number()); + + dstate->db = tal(dstate, struct db); + + err = sqlite3_open_v2(DB_FILE, &dstate->db->sql, + SQLITE_OPEN_READWRITE, NULL); + if (err != SQLITE_OK) { + log_unusual(dstate->base_log, + "Error opening %s (%s), trying to create", + DB_FILE, sqlite3_errstr(err)); + err = sqlite3_open_v2(DB_FILE, &dstate->db->sql, + SQLITE_OPEN_READWRITE + | SQLITE_OPEN_CREATE, NULL); + if (err != SQLITE_OK) + fatal("failed creating %s: %s", + DB_FILE, sqlite3_errstr(err)); + created = true; + } + + tal_add_destructor(dstate->db, close_db); + dstate->db->in_transaction = false; + + if (!created) { + db_load(dstate); + return; + } + + /* Set up tables. */ + errmsg = db_exec(dstate, dstate, + "CREATE TABLE wallet (privkey "SQL_PRIVKEY");" + "CREATE TABLE anchors (peer "SQL_PUBKEY", txid "SQL_TXID", idx INT, amount INT, ok_depth INT, min_depth INT, bool ours, PRIMARY KEY(peer));" + "CREATE TABLE htlcs (peer "SQL_PUBKEY", id INT, state TEXT, msatoshis INT, expiry INT, rhash "SQL_RHASH", r "SQL_R", routing "SQL_ROUTING", src_peer "SQL_PUBKEY", src_id INT, PRIMARY KEY(peer, id));" + "CREATE TABLE commit_info (peer "SQL_PUBKEY", side TEXT, commit_num INT, revocation_hash "SQL_SHA256", xmit_order INT, sig "SQL_SIGNATURE", prev_revocation_hash "SQL_SHA256", PRIMARY KEY(peer, side));" + "CREATE TABLE shachain (peer "SQL_PUBKEY", shachain BINARY(%zu), PRIMARY KEY(peer));" + "CREATE TABLE their_visible_state (peer "SQL_PUBKEY", offered_anchor BOOLEAN, commitkey "SQL_PUBKEY", finalkey "SQL_PUBKEY", locktime INT, mindepth INT, commit_fee_rate INT, next_revocation_hash "SQL_SHA256", PRIMARY KEY(peer));" + "CREATE TABLE their_commitments (peer "SQL_PUBKEY", txid "SQL_SHA256", INT commit_num, PRIMARY KEY(peer, txid));" + "CREATE TABLE peer_secrets (peer "SQL_PUBKEY", commitkey "SQL_PRIVKEY", finalkey "SQL_PRIVKEY", revocation_seed "SQL_SHA256", PRIMARY KEY(peer));" + "CREATE TABLE peer_address (peer "SQL_PUBKEY", addr BLOB, PRIMARY KEY(peer));" + "CREATE TABLE closing (peer "SQL_PUBKEY", our_fee INTEGER, their_fee INTEGER, their_sig "SQL_SIGNATURE", our_script BLOB, their_script BLOB, shutdown_order INTEGER, closing_order INTEGER, sigs_in INTEGER, PRIMARY KEY(peer));" + "CREATE TABLE peers (peer "SQL_PUBKEY", state TEXT, offered_anchor BOOLEAN, our_feerate INT, PRIMARY KEY(peer));", + sizeof(struct shachain)); + + if (errmsg) { + unlink(DB_FILE); + fatal("%s", errmsg); + } +} + +bool db_set_anchor(struct peer *peer) +{ + const char *errmsg, *ctx = tal(peer, char); + const char *peerid; + + assert(peer->dstate->db->in_transaction); + peerid = pubkey_to_hexstr(ctx, peer->dstate->secpctx, peer->id); + log_debug(peer->log, "%s(%s)", __func__, peerid); + + errmsg = db_exec(ctx, peer->dstate, + "INSERT INTO anchors VALUES (x'%s', x'%s', %u, %"PRIu64", %i, %u, %u);", + peerid, + tal_hexstr(ctx, &peer->anchor.txid, + sizeof(peer->anchor.txid)), + peer->anchor.index, + peer->anchor.satoshis, + peer->anchor.ok_depth, + peer->anchor.min_depth, + peer->anchor.ours); + if (errmsg) + goto out; + + errmsg = db_exec(ctx, peer->dstate, + "INSERT INTO commit_info VALUES(x'%s', 'OURS', 0, x'%s', %"PRIi64", %s, NULL);", + peerid, + tal_hexstr(ctx, &peer->local.commit->revocation_hash, + sizeof(peer->local.commit->revocation_hash)), + peer->local.commit->order, + sig_to_sql(ctx, peer->dstate->secpctx, + peer->local.commit->sig)); + if (errmsg) + goto out; + + errmsg = db_exec(ctx, peer->dstate, + "INSERT INTO commit_info VALUES(x'%s', 'THEIRS', 0, x'%s', %"PRIi64", %s, NULL);", + peerid, + tal_hexstr(ctx, &peer->remote.commit->revocation_hash, + sizeof(peer->remote.commit->revocation_hash)), + peer->remote.commit->order, + sig_to_sql(ctx, peer->dstate->secpctx, + peer->remote.commit->sig)); + + if (errmsg) + goto out; + + errmsg = db_exec(ctx, peer->dstate, "INSERT INTO shachain VALUES (x'%s', x'%s');", + peerid, + linearize_shachain(ctx, &peer->their_preimages)); + +out: + if (errmsg) + log_broken(peer->log, "%s:%s", __func__, errmsg); + + tal_free(ctx); + return !errmsg; +} + +bool db_set_visible_state(struct peer *peer) +{ + /* "CREATE TABLE their_visible_state (peer "SQL_PUBKEY", offered_anchor BOOLEAN, commitkey "SQL_PUBKEY", finalkey "SQL_PUBKEY", locktime INT, mindepth INT, commit_fee_rate INT, next_revocation_hash "SQL_SHA256", PRIMARY KEY(peer));" */ + const char *errmsg, *ctx = tal(peer, char); + const char *peerid = pubkey_to_hexstr(ctx, peer->dstate->secpctx, peer->id); + + log_debug(peer->log, "%s(%s)", __func__, peerid); + if (!db_start_transaction(peer)) { + tal_free(ctx); + return false; + } + + errmsg = db_exec(ctx, peer->dstate, + "INSERT INTO their_visible_state VALUES (x'%s', %u, x'%s', x'%s', %u, %u, %"PRIu64", x'%s');", + peerid, + peer->remote.offer_anchor == CMD_OPEN_WITH_ANCHOR, + pubkey_to_hexstr(ctx, peer->dstate->secpctx, + &peer->remote.commitkey), + pubkey_to_hexstr(ctx, peer->dstate->secpctx, + &peer->remote.finalkey), + peer->remote.locktime.locktime, + peer->remote.mindepth, + peer->remote.commit_fee_rate, + tal_hexstr(ctx, &peer->remote.next_revocation_hash, + sizeof(peer->remote.next_revocation_hash))); + if (errmsg) + goto out; + + if (!db_commit_transaction(peer)) + errmsg = "Commit failed"; + +out: + if (errmsg) { + log_broken(peer->log, "%s:%s", __func__, errmsg); + db_abort_transaction(peer); + } + + tal_free(ctx); + return !errmsg; +} + +bool db_update_next_revocation_hash(struct peer *peer) +{ + const char *errmsg, *ctx = tal(peer, char); + const char *peerid = pubkey_to_hexstr(ctx, peer->dstate->secpctx, peer->id); + + log_debug(peer->log, "%s(%s):%s", __func__, peerid, + tal_hexstr(ctx, &peer->remote.next_revocation_hash, + sizeof(peer->remote.next_revocation_hash))); + assert(peer->dstate->db->in_transaction); + errmsg = db_exec(ctx, peer->dstate, + "UPDATE their_visible_state SET next_revocation_hash=x'%s' WHERE peer=x'%s';", + tal_hexstr(ctx, &peer->remote.next_revocation_hash, + sizeof(peer->remote.next_revocation_hash)), + peerid); + if (errmsg) + log_broken(peer->log, "%s:%s", __func__, errmsg); + tal_free(ctx); + return !errmsg; +} + +bool db_create_peer(struct peer *peer) +{ + const char *errmsg, *ctx = tal(peer, char); + const char *peerid = pubkey_to_hexstr(ctx, peer->dstate->secpctx, peer->id); + + log_debug(peer->log, "%s(%s)", __func__, peerid); + if (!db_start_transaction(peer)) { + tal_free(ctx); + return false; + } + errmsg = db_exec(ctx, peer->dstate, + "INSERT INTO peers VALUES (x'%s', '%s', %u, %"PRIi64");", + peerid, + state_name(peer->state), + peer->local.offer_anchor == CMD_OPEN_WITH_ANCHOR, + peer->local.commit_fee_rate); + if (errmsg) + goto out; + + errmsg = db_exec(ctx, peer->dstate, + "INSERT INTO peer_secrets VALUES (x'%s', %s);", + peerid, peer_secrets_for_db(ctx, peer)); + if (errmsg) + goto out; + + errmsg = db_exec(ctx, peer->dstate, + "INSERT INTO peer_address VALUES (x'%s', x'%s');", + peerid, + netaddr_to_hex(ctx, &peer->addr)); + + if (errmsg) + goto out; + + if (!db_commit_transaction(peer)) + errmsg = "Commit failed"; + +out: + if (errmsg) { + log_broken(peer->log, "%s:%s", __func__, errmsg); + db_abort_transaction(peer); + } + + tal_free(ctx); + return !errmsg; +} + +bool db_start_transaction(struct peer *peer) +{ + const char *errmsg, *ctx = tal(peer, char); + const char *peerid = pubkey_to_hexstr(ctx, peer->dstate->secpctx, peer->id); + + log_debug(peer->log, "%s(%s)", __func__, peerid); + assert(!peer->dstate->db->in_transaction); + errmsg = db_exec(ctx, peer->dstate, "BEGIN IMMEDIATE;"); + if (!errmsg) + peer->dstate->db->in_transaction = true; + else + log_broken(peer->log, "%s:%s", __func__, errmsg); + tal_free(ctx); + return !errmsg; +} + +void db_abort_transaction(struct peer *peer) +{ + const char *errmsg, *ctx = tal(peer, char); + const char *peerid = pubkey_to_hexstr(ctx, peer->dstate->secpctx, peer->id); + + log_debug(peer->log, "%s(%s)", __func__, peerid); + assert(peer->dstate->db->in_transaction); + peer->dstate->db->in_transaction = false; + errmsg = db_exec(ctx, peer->dstate, "ROLLBACK;"); + if (errmsg) + log_broken(peer->log, "%s:%s", __func__, errmsg); + tal_free(ctx); +} + +bool db_commit_transaction(struct peer *peer) +{ + const char *errmsg, *ctx = tal(peer, char); + const char *peerid = pubkey_to_hexstr(ctx, peer->dstate->secpctx, peer->id); + + log_debug(peer->log, "%s(%s)", __func__, peerid); + assert(peer->dstate->db->in_transaction); + peer->dstate->db->in_transaction = false; + errmsg = db_exec(ctx, peer->dstate, "COMMIT;"); + if (errmsg) + log_broken(peer->log, "%s:%s", __func__, errmsg); + tal_free(ctx); + return !errmsg; +} + +bool db_new_htlc(struct peer *peer, const struct htlc *htlc) +{ + const char *errmsg, *ctx = tal(peer, char); + const char *peerid = pubkey_to_hexstr(ctx, peer->dstate->secpctx, peer->id); + + log_debug(peer->log, "%s(%s)", __func__, peerid); + assert(peer->dstate->db->in_transaction); + + if (htlc->src) { + errmsg = db_exec(ctx, peer->dstate, + "INSERT INTO htlcs VALUES" + " (x'%s', %"PRIu64", '%s', %"PRIu64", %u, x'%s', NULL, x'%s', x'%s', %"PRIu64");", + pubkey_to_hexstr(ctx, peer->dstate->secpctx, peer->id), + htlc->id, + htlc_state_name(htlc->state), + htlc->msatoshis, + abs_locktime_to_blocks(&htlc->expiry), + tal_hexstr(ctx, &htlc->rhash, sizeof(htlc->rhash)), + tal_hexstr(ctx, htlc->routing, tal_count(htlc->routing)), + peerid, + htlc->src->id); + } else { + errmsg = db_exec(ctx, peer->dstate, + "INSERT INTO htlcs VALUES" + " (x'%s', %"PRIu64", '%s', %"PRIu64", %u, x'%s', NULL, x'%s', NULL, NULL);", + peerid, + htlc->id, + htlc_state_name(htlc->state), + htlc->msatoshis, + abs_locktime_to_blocks(&htlc->expiry), + tal_hexstr(ctx, &htlc->rhash, sizeof(htlc->rhash)), + tal_hexstr(ctx, htlc->routing, tal_count(htlc->routing))); + } + + if (errmsg) + log_broken(peer->log, "%s:%s", __func__, errmsg); + tal_free(ctx); + return !errmsg; +} + +bool db_update_htlc_state(struct peer *peer, const struct htlc *htlc, + enum htlc_state oldstate) +{ + const char *errmsg, *ctx = tal(peer, char); + const char *peerid = pubkey_to_hexstr(ctx, peer->dstate->secpctx, peer->id); + + log_debug(peer->log, "%s(%s): %"PRIu64" %s->%s", __func__, peerid, + htlc->id, htlc_state_name(oldstate), + htlc_state_name(htlc->state)); + assert(peer->dstate->db->in_transaction); + errmsg = db_exec(ctx, peer->dstate, + "UPDATE htlcs SET state='%s' WHERE peer=x'%s' AND id=%"PRIu64" AND state='%s';", + htlc_state_name(htlc->state), peerid, + htlc->id, htlc_state_name(oldstate)); + + if (errmsg) + log_broken(peer->log, "%s:%s", __func__, errmsg); + tal_free(ctx); + return !errmsg; +} + +bool db_update_state(struct peer *peer) +{ + const char *errmsg, *ctx = tal(peer, char); + const char *peerid = pubkey_to_hexstr(ctx, peer->dstate->secpctx, peer->id); + + log_debug(peer->log, "%s(%s)", __func__, peerid); + + assert(peer->dstate->db->in_transaction); + errmsg = db_exec(ctx, peer->dstate, + "UPDATE peers SET state='%s' WHERE peer=x'%s';", + state_name(peer->state), peerid); + + if (errmsg) + log_broken(peer->log, "%s:%s", __func__, errmsg); + tal_free(ctx); + return !errmsg; +} + +bool db_htlc_fulfilled(struct peer *peer, const struct htlc *htlc) +{ + const char *errmsg, *ctx = tal(peer, char); + const char *peerid = pubkey_to_hexstr(ctx, peer->dstate->secpctx, peer->id); + + log_debug(peer->log, "%s(%s)", __func__, peerid); + + /* When called from their_htlc_added() and it's a payment to + * us, we are in a transaction. When called due to + * PKT_UPDATE_FULFILL_HTLC we are not. */ + errmsg = db_exec(ctx, peer->dstate, + "UPDATE htlcs SET r=x'%s' WHERE peer=x'%s' AND id=%"PRIu64" AND state='%s';", + tal_hexstr(ctx, htlc->r, sizeof(*htlc->r)), + peerid, + htlc->id, + htlc_state_name(htlc->state)); + + if (errmsg) + log_broken(peer->log, "%s:%s", __func__, errmsg); + tal_free(ctx); + return !errmsg; +} + +bool db_new_commit_info(struct peer *peer, enum channel_side side, + const struct sha256 *prev_rhash) +{ + struct commit_info *ci; + const char *sidestr; + const char *errmsg, *ctx = tal(peer, char); + const char *peerid = pubkey_to_hexstr(ctx, peer->dstate->secpctx, peer->id); + + log_debug(peer->log, "%s(%s)", __func__, peerid); + + assert(peer->dstate->db->in_transaction); + if (side == OURS) { + sidestr = "OURS"; + ci = peer->local.commit; + } else { + sidestr = "THEIRS"; + ci = peer->remote.commit; + } + + // CREATE TABLE commit_info (peer "SQL_PUBKEY", side TEXT, commit_num INT, revocation_hash "SQL_SHA256", xmit_order INT, sig "SQL_SIGNATURE", prev_revocation_hash "SQL_SHA256", PRIMARY KEY(peer, side)); + errmsg = db_exec(ctx, peer->dstate, "UPDATE commit_info SET commit_num=%"PRIu64", revocation_hash=x'%s', sig=%s, xmit_order=%"PRIi64", prev_revocation_hash=%s WHERE peer=x'%s' AND side='%s';", + ci->commit_num, + tal_hexstr(ctx, &ci->revocation_hash, + sizeof(ci->revocation_hash)), + sig_to_sql(ctx, peer->dstate->secpctx, ci->sig), + ci->order, + sql_hex_or_null(ctx, prev_rhash, sizeof(*prev_rhash)), + peerid, sidestr); + if (errmsg) + log_broken(peer->log, "%s:%s", __func__, errmsg); + tal_free(ctx); + return !errmsg; +} + +/* FIXME: Is this strictly necessary? */ +bool db_remove_their_prev_revocation_hash(struct peer *peer) +{ + const char *errmsg, *ctx = tal(peer, char); + const char *peerid = pubkey_to_hexstr(ctx, peer->dstate->secpctx, peer->id); + + log_debug(peer->log, "%s(%s)", __func__, peerid); + + assert(peer->dstate->db->in_transaction); + + // CREATE TABLE commit_info (peer "SQL_PUBKEY", side TEXT, commit_num INT, revocation_hash "SQL_SHA256", xmit_order INT, sig "SQL_SIGNATURE", prev_revocation_hash "SQL_SHA256", PRIMARY KEY(peer, side)); + errmsg = db_exec(ctx, peer->dstate, "UPDATE commit_info SET prev_revocation_hash=NULL WHERE peer=x'%s' AND side='THEIRS' and prev_revocation_hash IS NOT NULL;", + peerid); + if (errmsg) + log_broken(peer->log, "%s:%s", __func__, errmsg); + tal_free(ctx); + return !errmsg; +} + + +bool db_save_shachain(struct peer *peer) +{ + const char *errmsg, *ctx = tal(peer, char); + const char *peerid = pubkey_to_hexstr(ctx, peer->dstate->secpctx, peer->id); + + log_debug(peer->log, "%s(%s)", __func__, peerid); + + assert(peer->dstate->db->in_transaction); + // "CREATE TABLE shachain (peer "SQL_PUBKEY", shachain BINARY(%zu), PRIMARY KEY(peer));" + errmsg = db_exec(ctx, peer->dstate, "UPDATE shachain SET shachain=x'%s' WHERE peer=x'%s';", + linearize_shachain(ctx, &peer->their_preimages), + peerid); + if (errmsg) + log_broken(peer->log, "%s:%s", __func__, errmsg); + tal_free(ctx); + return !errmsg; +} + +bool db_add_commit_map(struct peer *peer, + const struct sha256_double *txid, u64 commit_num) +{ + const char *errmsg, *ctx = tal(peer, char); + const char *peerid = pubkey_to_hexstr(ctx, peer->dstate->secpctx, peer->id); + + log_debug(peer->log, "%s(%s),commit_num=%"PRIu64, __func__, peerid, + commit_num); + + assert(peer->dstate->db->in_transaction); + // "CREATE TABLE their_commitments (peer "SQL_PUBKEY", txid "SQL_SHA256", INT commit_num, PRIMARY KEY(peer, txid));" + errmsg = db_exec(ctx, peer->dstate, "INSERT INTO their_commitments VALUES (x'%s', x'%s', %"PRIu64");", + peerid, + tal_hexstr(ctx, txid, sizeof(*txid)), + commit_num); + if (errmsg) + log_broken(peer->log, "%s:%s", __func__, errmsg); + tal_free(ctx); + return !errmsg; +} + +void db_forget_peer(struct peer *peer) +{ + const char *errmsg, *ctx = tal(peer, char); + const char *peerid = pubkey_to_hexstr(ctx, peer->dstate->secpctx, peer->id); + + log_debug(peer->log, "%s(%s)", __func__, peerid); + + assert(peer->state == STATE_CLOSED); + + assert(!peer->dstate->db->in_transaction); + + /* FIXME: Implement cleanup of other tables, too. */ + errmsg = db_exec(ctx, peer->dstate, "DELETE from peers WHERE peer=x'%s';", + peerid); + + if (errmsg) + fatal("%s:%s", __func__, errmsg); + tal_free(ctx); +} + +bool db_begin_shutdown(struct peer *peer) +{ + const char *errmsg, *ctx = tal(peer, char); + const char *peerid = pubkey_to_hexstr(ctx, peer->dstate->secpctx, peer->id); + + log_debug(peer->log, "%s(%s)", __func__, peerid); + + assert(peer->dstate->db->in_transaction); + // "CREATE TABLE closing (peer "SQL_PUBKEY", our_fee INTEGER, their_fee INTEGER, their_sig "SQL_SIGNATURE", our_script BLOB, their_script BLOB, shutdown_order INTEGER, closing_order INTEGER, sigs_in INTEGER, PRIMARY KEY(peer));" + errmsg = db_exec(ctx, peer->dstate, "INSERT INTO closing VALUES (x'%s', 0, 0, NULL, NULL, NULL, 0, 0, 0);", + peerid); + if (errmsg) + log_broken(peer->log, "%s:%s", __func__, errmsg); + tal_free(ctx); + return !errmsg; +} + +bool db_set_our_closing_script(struct peer *peer) +{ + const char *errmsg, *ctx = tal(peer, char); + const char *peerid = pubkey_to_hexstr(ctx, peer->dstate->secpctx, peer->id); + + log_debug(peer->log, "%s(%s)", __func__, peerid); + + assert(peer->dstate->db->in_transaction); + // "CREATE TABLE closing (peer "SQL_PUBKEY", our_fee INTEGER, their_fee INTEGER, their_sig "SQL_SIGNATURE", our_script BLOB, their_script BLOB, shutdown_order INTEGER, closing_order INTEGER, sigs_in INTEGER, PRIMARY KEY(peer));" + errmsg = db_exec(ctx, peer->dstate, "UPDATE closing SET our_script=x'%s',shutdown_order=%"PRIu64" WHERE peer=x'%s';", + tal_hexstr(ctx, peer->closing.our_script, + tal_count(peer->closing.our_script)), + peer->closing.shutdown_order, + peerid); + if (errmsg) + log_broken(peer->log, "%s:%s", __func__, errmsg); + tal_free(ctx); + return !errmsg; +} + +bool db_set_their_closing_script(struct peer *peer) +{ + const char *errmsg, *ctx = tal(peer, char); + const char *peerid = pubkey_to_hexstr(ctx, peer->dstate->secpctx, peer->id); + + log_debug(peer->log, "%s(%s)", __func__, peerid); + + assert(!peer->dstate->db->in_transaction); + // "CREATE TABLE closing (peer "SQL_PUBKEY", our_fee INTEGER, their_fee INTEGER, their_sig "SQL_SIGNATURE", our_script BLOB, their_script BLOB, shutdown_order INTEGER, closing_order INTEGER, sigs_in INTEGER, PRIMARY KEY(peer));" + errmsg = db_exec(ctx, peer->dstate, "UPDATE closing SET their_script=x'%s' WHERE peer=x'%s';", + tal_hexstr(ctx, peer->closing.their_script, + tal_count(peer->closing.their_script)), + peerid); + if (errmsg) + log_broken(peer->log, "%s:%s", __func__, errmsg); + tal_free(ctx); + return !errmsg; +} + +/* For first time, we are in transaction to make it atomic with peer->state + * update. Later calls are not. */ +bool db_update_our_closing(struct peer *peer) +{ + const char *errmsg, *ctx = tal(peer, char); + const char *peerid = pubkey_to_hexstr(ctx, peer->dstate->secpctx, peer->id); + + log_debug(peer->log, "%s(%s)", __func__, peerid); + + // "CREATE TABLE closing (peer "SQL_PUBKEY", our_fee INTEGER, their_fee INTEGER, their_sig "SQL_SIGNATURE", our_script BLOB, their_script BLOB, shutdown_order INTEGER, closing_order INTEGER, sigs_in INTEGER, PRIMARY KEY(peer));" + errmsg = db_exec(ctx, peer->dstate, "UPDATE closing SET our_fee=%"PRIu64", closing_order=%"PRIi64" WHERE peer=x'%s';", + peer->closing.our_fee, + peer->closing.closing_order, + peerid); + if (errmsg) + log_broken(peer->log, "%s:%s", __func__, errmsg); + tal_free(ctx); + return !errmsg; +} + +bool db_update_their_closing(struct peer *peer) +{ + const char *errmsg, *ctx = tal(peer, char); + const char *peerid = pubkey_to_hexstr(ctx, peer->dstate->secpctx, peer->id); + + log_debug(peer->log, "%s(%s)", __func__, peerid); + + assert(!peer->dstate->db->in_transaction); + // "CREATE TABLE closing (peer "SQL_PUBKEY", our_fee INTEGER, their_fee INTEGER, their_sig "SQL_SIGNATURE", our_script BLOB, their_script BLOB, shutdown_order INTEGER, closing_order INTEGER, sigs_in INTEGER, PRIMARY KEY(peer));" + errmsg = db_exec(ctx, peer->dstate, "UPDATE closing SET their_fee=%"PRIu64", their_sig=x'%s', sigs_in=%u WHERE peer=x'%s';", + peer->closing.their_fee, + tal_hexstr(ctx, peer->closing.their_sig, + tal_count(peer->closing.their_sig)), + peer->closing.sigs_in, + peerid); + if (errmsg) + log_broken(peer->log, "%s:%s", __func__, errmsg); + tal_free(ctx); + return !errmsg; +} diff --git a/daemon/db.h b/daemon/db.h new file mode 100644 index 000000000..a4d234d66 --- /dev/null +++ b/daemon/db.h @@ -0,0 +1,46 @@ +#ifndef LIGHTNING_DAEMON_DB_H +#define LIGHTNING_DAEMON_DB_H +#include "config.h" +#include "peer.h" +#include + +void db_init(struct lightningd_state *dstate); + +bool db_create_peer(struct peer *peer); +bool db_set_anchor(struct peer *peer); +bool db_set_visible_state(struct peer *peer); + +bool db_start_transaction(struct peer *peer); +void db_abort_transaction(struct peer *peer); +bool db_commit_transaction(struct peer *peer); + +void db_add_wallet_privkey(struct lightningd_state *dstate, + const struct privkey *privkey); + +/* Must NOT be inside transaction. */ +bool db_htlc_fulfilled(struct peer *peer, const struct htlc *htlc); +bool db_set_our_closing_script(struct peer *peer); +bool db_set_their_closing_script(struct peer *peer); +bool db_update_our_closing(struct peer *peer); +bool db_update_their_closing(struct peer *peer); + +/* FIXME: save error handling until db_commit_transaction for calls + * which have to be inside transaction anyway. */ + +/* Must be inside transaction. */ +bool db_new_htlc(struct peer *peer, const struct htlc *htlc); +bool db_update_htlc_state(struct peer *peer, const struct htlc *htlc, + enum htlc_state oldstate); +bool db_new_commit_info(struct peer *peer, enum channel_side side, + const struct sha256 *prev_rhash); +bool db_remove_their_prev_revocation_hash(struct peer *peer); +bool db_update_next_revocation_hash(struct peer *peer); +bool db_save_shachain(struct peer *peer); +bool db_update_state(struct peer *peer); +bool db_begin_shutdown(struct peer *peer); + +bool db_add_commit_map(struct peer *peer, + const struct sha256_double *txid, u64 commit_num); + +void db_forget_peer(struct peer *peer); +#endif /* LIGHTNING_DAEMON_DB_H */ diff --git a/daemon/htlc.c b/daemon/htlc.c index cf6ce698e..795e1bbfc 100644 --- a/daemon/htlc.c +++ b/daemon/htlc.c @@ -15,6 +15,16 @@ const char *htlc_state_name(enum htlc_state s) return "unknown"; } +enum htlc_state htlc_state_from_name(const char *name) +{ + size_t i; + + for (i = 0; enum_htlc_state_names[i].name; i++) + if (streq(enum_htlc_state_names[i].name, name)) + return enum_htlc_state_names[i].v; + return HTLC_STATE_INVALID; +} + /* This is the flags for each state. */ static const int per_state_bits[] = { [SENT_ADD_HTLC] = HTLC_ADDING + HTLC_LOCAL_F_OWNER diff --git a/daemon/htlc.h b/daemon/htlc.h index abdd78df1..e42747f8b 100644 --- a/daemon/htlc.h +++ b/daemon/htlc.h @@ -72,6 +72,7 @@ struct htlc { }; const char *htlc_state_name(enum htlc_state s); +enum htlc_state htlc_state_from_name(const char *name); void htlc_changestate(struct htlc *h, enum htlc_state oldstate, enum htlc_state newstate); @@ -82,21 +83,26 @@ static inline bool htlc_has(const struct htlc *h, int flag) return htlc_state_flags(h->state) & flag; } -static inline enum htlc_side htlc_owner(const struct htlc *h) +static inline enum htlc_side htlc_state_owner(enum htlc_state state) { - if (h->state < RCVD_ADD_HTLC) { - assert((htlc_state_flags(h->state) + if (state < RCVD_ADD_HTLC) { + assert((htlc_state_flags(state) & (HTLC_REMOTE_F_OWNER|HTLC_LOCAL_F_OWNER)) == HTLC_LOCAL_F_OWNER); return LOCAL; } else { - assert((htlc_state_flags(h->state) + assert((htlc_state_flags(state) & (HTLC_REMOTE_F_OWNER|HTLC_LOCAL_F_OWNER)) == HTLC_REMOTE_F_OWNER); return REMOTE; } } +static inline enum htlc_side htlc_owner(const struct htlc *h) +{ + return htlc_state_owner(h->state); +} + /* FIXME: Transitional function. */ static inline enum channel_side htlc_channel_side(const struct htlc *h) { diff --git a/daemon/htlc_state.h b/daemon/htlc_state.h index dfcfd9627..bfd125180 100644 --- a/daemon/htlc_state.h +++ b/daemon/htlc_state.h @@ -29,6 +29,8 @@ enum htlc_state { SENT_REMOVE_COMMIT, RCVD_REMOVE_REVOCATION, RCVD_REMOVE_ACK_COMMIT, - SENT_REMOVE_ACK_REVOCATION + SENT_REMOVE_ACK_REVOCATION, + + HTLC_STATE_INVALID }; #endif /* LIGHTNING_DAEMON_HTLC_STATE_H */ diff --git a/daemon/lightningd.c b/daemon/lightningd.c index f04160cf5..2cfc8b2a0 100644 --- a/daemon/lightningd.c +++ b/daemon/lightningd.c @@ -2,6 +2,7 @@ #include "chaintopology.h" #include "configdir.h" #include "controlled_time.h" +#include "db.h" #include "jsonrpc.h" #include "lightningd.h" #include "log.h" @@ -329,7 +330,10 @@ int main(int argc, char *argv[]) /* Set up node ID and private key. */ secrets_init(dstate); new_node(dstate, &dstate->id); - + + /* Read or create database. */ + db_init(dstate); + /* Initialize block topology. */ setup_topology(dstate); diff --git a/daemon/lightningd.h b/daemon/lightningd.h index 6823be8a5..672fd168a 100644 --- a/daemon/lightningd.h +++ b/daemon/lightningd.h @@ -72,6 +72,9 @@ struct lightningd_state { /* Configuration settings. */ struct config config; + /* The database where we keep our stuff. */ + struct db *db; + /* Any pending timers. */ struct timers timers; diff --git a/daemon/secrets.c b/daemon/secrets.c index 784c3b0b1..3ea4384d6 100644 --- a/daemon/secrets.c +++ b/daemon/secrets.c @@ -5,12 +5,14 @@ #include "log.h" #include "peer.h" #include "secrets.h" +#include "utils.h" #include #include #include #include #include #include +#include #include #include #include @@ -175,6 +177,48 @@ void peer_get_revocation_hash(const struct peer *peer, u64 index, sha256(rhash, preimage.u.u8, sizeof(preimage.u.u8)); } +const char *peer_secrets_for_db(const tal_t *ctx, struct peer *peer) +{ + const struct peer_secrets *ps = peer->secrets; + return tal_fmt(ctx, "x'%s', x'%s', x'%s'", + tal_hexstr(ctx, &ps->commit, sizeof(ps->commit)), + tal_hexstr(ctx, &ps->final, sizeof(ps->final)), + tal_hexstr(ctx, &ps->revocation_seed, + sizeof(ps->revocation_seed))); +} + +void peer_set_secrets_from_db(struct peer *peer, + const void *commit_privkey, + size_t commit_privkey_len, + const void *final_privkey, + size_t final_privkey_len, + const void *revocation_seed, + size_t revocation_seed_len) +{ + struct peer_secrets *ps = tal(peer, struct peer_secrets); + + assert(!peer->secrets); + peer->secrets = ps; + + if (commit_privkey_len != sizeof(ps->commit) + || final_privkey_len != sizeof(ps->final) + || revocation_seed_len != sizeof(ps->revocation_seed)) + fatal("peer_set_secrets_from_db: bad lengths %zu/%zu/%zu", + commit_privkey_len, final_privkey_len, + revocation_seed_len); + + memcpy(&ps->commit, commit_privkey, commit_privkey_len); + memcpy(&ps->final, final_privkey, final_privkey_len); + memcpy(&ps->revocation_seed, revocation_seed, revocation_seed_len); + + if (!pubkey_from_privkey(peer->dstate->secpctx, &ps->commit, + &peer->local.commitkey)) + fatal("peer_set_secrets_from_db:bad commit privkey"); + if (!pubkey_from_privkey(peer->dstate->secpctx, &ps->final, + &peer->local.finalkey)) + fatal("peer_set_secrets_from_db:bad final privkey"); +} + void secrets_init(struct lightningd_state *dstate) { int fd; diff --git a/daemon/secrets.h b/daemon/secrets.h index b1fdf6869..79b791c65 100644 --- a/daemon/secrets.h +++ b/daemon/secrets.h @@ -45,6 +45,16 @@ void peer_sign_steal_input(const struct peer *peer, const u8 *witnessscript, struct signature *sig); +const char *peer_secrets_for_db(const tal_t *ctx, struct peer *peer); + +void peer_set_secrets_from_db(struct peer *peer, + const void *commit_privkey, + size_t commit_privkey_len, + const void *final_privkey, + size_t final_privkey_len, + const void *revocation_seed, + size_t revocation_seed_len); + void peer_secrets_init(struct peer *peer); void peer_get_revocation_hash(const struct peer *peer, u64 index, diff --git a/daemon/wallet.c b/daemon/wallet.c index f56584f20..d8d0ba229 100644 --- a/daemon/wallet.c +++ b/daemon/wallet.c @@ -21,6 +21,26 @@ struct wallet { struct ripemd160 p2sh; }; +bool restore_wallet_address(struct lightningd_state *dstate, + const struct privkey *privkey) +{ + struct wallet *w = tal(dstate, struct wallet); + u8 *redeemscript; + struct sha256 h; + + w->privkey = *privkey; + if (!pubkey_from_privkey(dstate->secpctx, &w->privkey, &w->pubkey)) + return false; + + redeemscript = bitcoin_redeem_p2wpkh(w, dstate->secpctx, &w->pubkey); + sha256(&h, redeemscript, tal_count(redeemscript)); + ripemd160(&w->p2sh, h.u.u8, sizeof(h)); + + list_add_tail(&dstate->wallet, &w->list); + tal_free(redeemscript); + return true; +} + static void new_keypair(struct lightningd_state *dstate, struct privkey *privkey, struct pubkey *pubkey) { diff --git a/daemon/wallet.h b/daemon/wallet.h index ffa8509f7..ebcd59f81 100644 --- a/daemon/wallet.h +++ b/daemon/wallet.h @@ -7,6 +7,9 @@ struct lightningd_state; struct bitcoin_tx; struct bitcoin_tx_output; +bool restore_wallet_address(struct lightningd_state *dstate, + const struct privkey *privkey); + void wallet_add_signed_input(struct lightningd_state *dstate, const struct wallet *w, struct bitcoin_tx *tx, diff --git a/names.c b/names.c index 47e51ce0d..196957500 100644 --- a/names.c +++ b/names.c @@ -1,4 +1,5 @@ #include "names.h" +#include /* Indented for 'check-source' because it has to be included after names.h */ #include "gen_state_names.h" #include "gen_pkt_names.h" @@ -13,6 +14,17 @@ const char *state_name(enum state s) return "unknown"; } +enum state name_to_state(const char *name) +{ + size_t i; + + for (i = 0; enum_state_names[i].name; i++) + if (streq(name, enum_state_names[i].name)) + return enum_state_names[i].v; + + return STATE_MAX; +} + const char *input_name(enum state_input in) { size_t i; diff --git a/names.h b/names.h index 2b0a42439..3c7cfb690 100644 --- a/names.h +++ b/names.h @@ -5,6 +5,7 @@ #include "state_types.h" const char *state_name(enum state s); +enum state name_to_state(const char *name); const char *input_name(enum state_input in); const char *pkt_name(Pkt__PktCase pkt); #endif /* LIGHTNING_NAMES_H */