From dbfac68c3f2c253b575ce0b350fcda0a9bba66b7 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 18 Aug 2017 14:13:52 +0930 Subject: [PATCH] lightningd: keep last valid tx, and its signature. This avoids us having to reconstruct it. Signed-off-by: Rusty Russell --- lightningd/peer_control.c | 2 ++ lightningd/peer_control.h | 4 ++++ wallet/db.c | 2 ++ wallet/wallet.c | 33 ++++++++++++++++++++++++++++++++- wallet/wallet_tests.c | 36 +++++++++++++++++++++++++++++++++--- 5 files changed, 73 insertions(+), 4 deletions(-) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 08364506c..4d4c56599 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -489,6 +489,8 @@ void add_peer(struct lightningd *ld, u64 unique_id, peer->our_msatoshi = NULL; peer->state = UNINITIALIZED; peer->channel_info = NULL; + peer->last_tx = NULL; + peer->last_sig = NULL; peer->last_was_revoke = false; peer->last_sent_commit = NULL; peer->remote_shutdown_scriptpubkey = NULL; diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h index 831d4d490..1d1e8667e 100644 --- a/lightningd/peer_control.h +++ b/lightningd/peer_control.h @@ -76,6 +76,10 @@ struct peer { /* Amount going to us, not counting unfinished HTLCs; if we have one. */ u64 *our_msatoshi; + /* Last tx they gave us (if any). */ + struct bitcoin_tx *last_tx; + secp256k1_ecdsa_signature *last_sig; + /* Keys for channel. */ struct channel_info *channel_info; diff --git a/wallet/db.c b/wallet/db.c index 20ee7063b..e42cde961 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -72,6 +72,8 @@ char *dbmigrations[] = { " shutdown_keyidx_local INTEGER," " last_sent_commit_state INTEGER," " last_sent_commit_id INTEGER," + " last_tx BLOB," + " last_sig BLOB," " closing_fee_received INTEGER," " closing_sig_received BLOB," " PRIMARY KEY (id)" diff --git a/wallet/wallet.c b/wallet/wallet.c index 221a6476b..8a8550b94 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -377,6 +377,14 @@ static u8 *sqlite3_column_varhexblob(tal_t *ctx, sqlite3_stmt *stmt, int col) return tal_hexdata(ctx, source, sourcelen); } +static struct bitcoin_tx *sqlite3_column_tx(const tal_t *ctx, + sqlite3_stmt *stmt, int col) +{ + return bitcoin_tx_from_hex(ctx, + sqlite3_column_blob(stmt, col), + sqlite3_column_bytes(stmt, col)); +} + static bool wallet_peer_load(struct wallet *w, const u64 id, struct peer *peer) { bool ok = true; @@ -506,6 +514,17 @@ static bool wallet_stmt2channel(struct wallet *w, sqlite3_stmt *stmt, col += 2; } + /* Do we have last_tx? If so, populate. */ + if (sqlite3_column_type(stmt, col) != SQLITE_NULL) { + chan->peer->last_tx = sqlite3_column_tx(chan->peer, stmt, col++); + chan->peer->last_sig = tal(chan->peer, secp256k1_ecdsa_signature); + sqlite3_column_sig(stmt, col++, chan->peer->last_sig); + } else { + chan->peer->last_tx = tal_free(chan->peer->last_tx); + chan->peer->last_sig = tal_free(chan->peer->last_sig); + col += 2; + } + chan->peer->closing_fee_received = sqlite3_column_int64(stmt, col++); if (sqlite3_column_type(stmt, col) != SQLITE_NULL) { if (!chan->peer->closing_sig_received) { @@ -515,7 +534,7 @@ static bool wallet_stmt2channel(struct wallet *w, sqlite3_stmt *stmt, } else { col++; } - assert(col == 34); + assert(col == 36); return ok; } @@ -540,6 +559,7 @@ bool wallet_channel_load(struct wallet *w, const u64 id, "old_per_commit_remote, feerate_per_kw, shachain_remote_id, " "shutdown_scriptpubkey_remote, shutdown_keyidx_local, " "last_sent_commit_state, last_sent_commit_id, " + "last_tx, last_sig, " "closing_fee_received, closing_sig_received FROM channels WHERE " "id=%" PRIu64 ";"; @@ -573,6 +593,14 @@ static char* db_serialize_pubkey(const tal_t *ctx, struct pubkey *pk) return tal_hex(ctx, der); } +static char* db_serialize_tx(const tal_t *ctx, const struct bitcoin_tx *tx) +{ + if (!tx) + return "NULL"; + + return tal_fmt(ctx, "'%s'", tal_hex(ctx, linearize_tx(ctx, tx))); +} + bool wallet_channel_config_save(struct wallet *w, struct channel_config *cc) { bool ok = true; @@ -674,6 +702,7 @@ bool wallet_channel_save(struct wallet *w, struct wallet_channel *chan){ " shutdown_scriptpubkey_remote='%s'," " shutdown_keyidx_local=%"PRIu64"," " channel_config_local=%"PRIu64"," + " last_tx=%s, last_sig=%s, " " closing_fee_received=%"PRIu64"," " closing_sig_received=%s" " WHERE id=%"PRIu64, @@ -696,6 +725,8 @@ bool wallet_channel_save(struct wallet *w, struct wallet_channel *chan){ p->remote_shutdown_scriptpubkey?tal_hex(tmpctx, p->remote_shutdown_scriptpubkey):"", p->local_shutdown_idx, p->our_config.id, + db_serialize_tx(tmpctx, p->last_tx), + db_serialize_signature(tmpctx, p->last_sig), p->closing_fee_received, db_serialize_signature(tmpctx, p->closing_sig_received), chan->id); diff --git a/wallet/wallet_tests.c b/wallet/wallet_tests.c index 9f977e2d6..34a484514 100644 --- a/wallet/wallet_tests.c +++ b/wallet/wallet_tests.c @@ -113,6 +113,18 @@ static bool test_shachain_crud(void) return true; } +static bool bitcoin_tx_eq(const struct bitcoin_tx *tx1, + const struct bitcoin_tx *tx2) +{ + u8 *lin1, *lin2; + bool eq; + lin1 = linearize_tx(NULL, tx1); + lin2 = linearize_tx(lin1, tx2); + eq = memeq(lin1, tal_len(lin1), lin2, tal_len(lin2)); + tal_free(lin1); + return eq; +} + static bool channelseq(struct wallet_channel *c1, struct wallet_channel *c2) { struct peer *p1 = c1->peer, *p2 = c2->peer; @@ -154,6 +166,16 @@ static bool channelseq(struct wallet_channel *c1, struct wallet_channel *c2) CHECK(lc1->id == lc2->id); } + CHECK((p1->last_tx != NULL) == (p2->last_tx != NULL)); + if(p1->last_tx) { + CHECK(bitcoin_tx_eq(p1->last_tx, p2->last_tx)); + } + CHECK((p1->last_sig != NULL) == (p2->last_sig != NULL)); + if(p1->last_sig) { + CHECK(memeq(p1->last_sig, sizeof(*p1->last_sig), + p2->last_sig, sizeof(*p2->last_sig))); + } + CHECK((p1->closing_sig_received != NULL) == (p2->closing_sig_received != NULL)); if(p1->closing_sig_received) { CHECK(memeq(p1->closing_sig_received, @@ -192,6 +214,7 @@ static bool test_channel_crud(const tal_t *ctx) p.id = pk; p.unique_id = 42; p.our_msatoshi = NULL; + p.last_tx = NULL; memset(&ci.their_config, 0, sizeof(struct channel_config)); ci.remote_fundingkey = pk; ci.theirbase.revocation = pk; @@ -200,7 +223,7 @@ static bool test_channel_crud(const tal_t *ctx) ci.remote_per_commit = pk; ci.old_remote_per_commit = pk; - /* Variant 1: insert with null for scid, funding_tx_id, and channel_info */ + /* Variant 1: insert with null for scid, funding_tx_id, channel_info, last_tx */ CHECK_MSG(wallet_channel_save(w, &c1), tal_fmt(w, "Insert into DB: %s", w->db->err)); CHECK_MSG(wallet_channel_load(w, c1.id, c2), tal_fmt(w, "Load from DB: %s", w->db->err)); CHECK_MSG(channelseq(&c1, c2), "Compare loaded with saved (v1)"); @@ -245,12 +268,19 @@ static bool test_channel_crud(const tal_t *ctx) CHECK_MSG(wallet_channel_load(w, c1.id, c2), tal_fmt(w, "Load from DB: %s", w->db->err)); CHECK_MSG(channelseq(&c1, c2), "Compare loaded with saved (v6)"); - /* Variant 7: update with closing_sig */ - p.closing_sig_received = sig; + /* Variant 7: update with last_tx (taken from BOLT #3) */ + p.last_tx = bitcoin_tx_from_hex(w, "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b820b584a488489000000000038b02b8003a00f0000000000002200208c48d15160397c9731df9bc3b236656efb6665fbfe92b4a6878e88a499f741c4c0c62d0000000000160014ccf1af2f2aabee14bb40fa3851ab2301de843110ae8f6a00000000002200204adb4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e040047304402206a2679efa3c7aaffd2a447fd0df7aba8792858b589750f6a1203f9259173198a022008d52a0e77a99ab533c36206cb15ad7aeb2aa72b93d4b571e728cb5ec2f6fe260147304402206d6cb93969d39177a09d5d45b583f34966195b77c7e585cf47ac5cce0c90cefb022031d71ae4e33a4e80df7f981d696fbdee517337806a3c7138b7491e2cbb077a0e01475221023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae3e195220", strlen("02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b820b584a488489000000000038b02b8003a00f0000000000002200208c48d15160397c9731df9bc3b236656efb6665fbfe92b4a6878e88a499f741c4c0c62d0000000000160014ccf1af2f2aabee14bb40fa3851ab2301de843110ae8f6a00000000002200204adb4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e040047304402206a2679efa3c7aaffd2a447fd0df7aba8792858b589750f6a1203f9259173198a022008d52a0e77a99ab533c36206cb15ad7aeb2aa72b93d4b571e728cb5ec2f6fe260147304402206d6cb93969d39177a09d5d45b583f34966195b77c7e585cf47ac5cce0c90cefb022031d71ae4e33a4e80df7f981d696fbdee517337806a3c7138b7491e2cbb077a0e01475221023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae3e195220")); + p.last_sig = sig; CHECK_MSG(wallet_channel_save(w, &c1), tal_fmt(w, "Insert into DB: %s", w->db->err)); CHECK_MSG(wallet_channel_load(w, c1.id, c2), tal_fmt(w, "Load from DB: %s", w->db->err)); CHECK_MSG(channelseq(&c1, c2), "Compare loaded with saved (v7)"); + /* Variant 8: update with closing_sig */ + p.closing_sig_received = sig; + CHECK_MSG(wallet_channel_save(w, &c1), tal_fmt(w, "Insert into DB: %s", w->db->err)); + CHECK_MSG(wallet_channel_load(w, c1.id, c2), tal_fmt(w, "Load from DB: %s", w->db->err)); + CHECK_MSG(channelseq(&c1, c2), "Compare loaded with saved (v8)"); + tal_free(w); return true; }