From c1d364c5fb3648adf2504020eeb6c90f50909771 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 20 Oct 2017 19:04:43 +0200 Subject: [PATCH] wallet: Migrate channel persistence to native sqlite3 binding Signed-off-by: Christian Decker --- wallet/db.c | 24 ++--- wallet/wallet.c | 275 +++++++++++++++++++++++------------------------- 2 files changed, 136 insertions(+), 163 deletions(-) diff --git a/wallet/db.c b/wallet/db.c index 009fd7452..225089ada 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -440,7 +440,7 @@ bool sqlite3_column_short_channel_id(sqlite3_stmt *stmt, int col, bool sqlite3_bind_tx(sqlite3_stmt *stmt, int col, const struct bitcoin_tx *tx) { u8 *ser = linearize_tx(NULL, tx); - sqlite3_bind_blob(stmt, col, tal_hex(ser, ser), 2*tal_len(ser), SQLITE_TRANSIENT); + sqlite3_bind_blob(stmt, col, ser, tal_len(ser), SQLITE_TRANSIENT); tal_free(ser); return true; } @@ -448,12 +448,10 @@ bool sqlite3_bind_tx(sqlite3_stmt *stmt, int col, const struct bitcoin_tx *tx) 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)); + const u8 *src = sqlite3_column_blob(stmt, col); + size_t len = sqlite3_column_bytes(stmt, col); + return pull_bitcoin_tx(ctx, &src, &len); } - bool sqlite3_bind_signature(sqlite3_stmt *stmt, int col, const secp256k1_ecdsa_signature *sig) { @@ -475,18 +473,8 @@ bool sqlite3_column_signature(sqlite3_stmt *stmt, int col, bool sqlite3_column_pubkey(sqlite3_stmt *stmt, int col, struct pubkey *dest) { - u8 buf[PUBKEY_DER_LEN]; - - if (sqlite3_column_bytes(stmt, col) == 2*PUBKEY_DER_LEN) { - /* FIXME: Remove the legacy path for hex-values */ - if (!sqlite3_column_hexval(stmt, col, buf, sizeof(buf))) - return false; - } else { - assert(sqlite3_column_bytes(stmt, col) == PUBKEY_DER_LEN); - memcpy(buf, sqlite3_column_blob(stmt, col), PUBKEY_DER_LEN); - } - - return pubkey_from_der(buf, sizeof(buf), dest); + assert(sqlite3_column_bytes(stmt, col) == PUBKEY_DER_LEN); + return pubkey_from_der(sqlite3_column_blob(stmt, col), PUBKEY_DER_LEN, dest); } bool sqlite3_bind_pubkey(sqlite3_stmt *stmt, int col, const struct pubkey *pk) diff --git a/wallet/wallet.c b/wallet/wallet.c index db9200ae2..4c790ccd4 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -342,21 +342,6 @@ bool wallet_shachain_load(struct wallet *wallet, u64 id, return true; } -static bool sqlite3_column_sig(sqlite3_stmt *stmt, int col, secp256k1_ecdsa_signature *sig) -{ - u8 buf[64]; - if (!sqlite3_column_hexval(stmt, col, buf, sizeof(buf))) - return false; - return secp256k1_ecdsa_signature_parse_compact(secp256k1_ctx, sig, buf) == 1; -} - -static u8 *sqlite3_column_varhexblob(tal_t *ctx, sqlite3_stmt *stmt, int col) -{ - const u8 *source = sqlite3_column_blob(stmt, col); - size_t sourcelen = sqlite3_column_bytes(stmt, col); - return tal_hexdata(ctx, source, sourcelen); -} - static bool wallet_peer_load(struct wallet *w, const u64 id, struct peer *peer) { bool ok = true; @@ -376,9 +361,8 @@ bool wallet_peer_by_nodeid(struct wallet *w, const struct pubkey *nodeid, { bool ok; tal_t *tmpctx = tal_tmpctx(w); - sqlite3_stmt *stmt = db_query( - __func__, w->db, "SELECT id, node_id FROM peers WHERE node_id='%s';", - pubkey_to_hexstr(tmpctx, nodeid)); + sqlite3_stmt *stmt = db_prepare(w->db, "SELECT id, node_id FROM peers WHERE node_id=?;"); + sqlite3_bind_pubkey(stmt, 1, nodeid); ok = stmt != NULL && sqlite3_step(stmt) == SQLITE_ROW; if (ok) { @@ -404,8 +388,6 @@ static bool wallet_stmt2channel(struct wallet *w, sqlite3_stmt *stmt, bool ok = true; int col = 0; struct channel_info *channel_info; - struct sha256_double temphash; - struct short_channel_id scid; u64 remote_config_id; if (!chan->peer) { @@ -415,11 +397,12 @@ static bool wallet_stmt2channel(struct wallet *w, sqlite3_stmt *stmt, chan->peer->dbid = sqlite3_column_int64(stmt, col++); wallet_peer_load(w, chan->peer->dbid, chan->peer); - if (sqlite3_column_short_channel_id(stmt, col++, &scid)) { + if (sqlite3_column_type(stmt, col) != SQLITE_NULL) { chan->peer->scid = tal(chan->peer, struct short_channel_id); - *chan->peer->scid = scid; + sqlite3_column_short_channel_id(stmt, col++, chan->peer->scid); } else { chan->peer->scid = NULL; + col++; } chan->peer->our_config.id = sqlite3_column_int64(stmt, col++); @@ -434,12 +417,14 @@ static bool wallet_stmt2channel(struct wallet *w, sqlite3_stmt *stmt, chan->peer->next_index[REMOTE] = sqlite3_column_int64(stmt, col++); chan->peer->next_htlc_id = sqlite3_column_int64(stmt, col++); - if (sqlite3_column_hexval(stmt, col++, &temphash, sizeof(temphash))) { + if (sqlite3_column_type(stmt, col) != SQLITE_NULL) { + assert(sqlite3_column_bytes(stmt, col) == 32); chan->peer->funding_txid = tal(chan->peer, struct sha256_double); - *chan->peer->funding_txid = temphash; + memcpy(chan->peer->funding_txid, sqlite3_column_blob(stmt, col), 32); } else { chan->peer->funding_txid = NULL; } + col++; chan->peer->funding_outnum = sqlite3_column_int(stmt, col++); chan->peer->funding_satoshi = sqlite3_column_int64(stmt, col++); @@ -484,7 +469,8 @@ static bool wallet_stmt2channel(struct wallet *w, sqlite3_stmt *stmt, /* Do we have a non-null remote_shutdown_scriptpubkey? */ if (sqlite3_column_type(stmt, col) != SQLITE_NULL) { - chan->peer->remote_shutdown_scriptpubkey = sqlite3_column_varhexblob(chan->peer, stmt, col++); + chan->peer->remote_shutdown_scriptpubkey = tal_arr(chan->peer, u8, sqlite3_column_bytes(stmt, col)); + memcpy(chan->peer->remote_shutdown_scriptpubkey, sqlite3_column_blob(stmt, col), sqlite3_column_bytes(stmt, col)); chan->peer->local_shutdown_idx = sqlite3_column_int64(stmt, col++); } else { chan->peer->remote_shutdown_scriptpubkey = tal_free(chan->peer->remote_shutdown_scriptpubkey); @@ -508,7 +494,7 @@ static bool wallet_stmt2channel(struct wallet *w, sqlite3_stmt *stmt, 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); + sqlite3_column_signature(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); @@ -581,54 +567,34 @@ bool wallet_channels_load_active(struct wallet *w, struct list_head *peers) return ok; } -static char* db_serialize_signature(const tal_t *ctx, secp256k1_ecdsa_signature* sig) -{ - u8 buf[64]; - if (!sig || secp256k1_ecdsa_signature_serialize_compact(secp256k1_ctx, buf, sig) != 1) - return "null"; - return tal_fmt(ctx, "'%s'", tal_hexstr(ctx, buf, sizeof(buf))); -} - -static char* db_serialize_pubkey(const tal_t *ctx, struct pubkey *pk) -{ - u8 *der; - if (!pk) - return "NULL"; - der = tal_arr(ctx, u8, PUBKEY_DER_LEN); - pubkey_to_der(der, 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; + sqlite3_stmt *stmt; /* Is this an update? If not insert a stub first */ if (!cc->id) { - ok &= db_exec(__func__, w->db, - "INSERT INTO channel_configs DEFAULT VALUES;"); + stmt = db_prepare( + w->db,"INSERT INTO channel_configs DEFAULT VALUES;"); + ok &= db_exec_prepared(w->db, stmt); cc->id = sqlite3_last_insert_rowid(w->db->sql); } - ok &= db_exec( - __func__, w->db, "UPDATE channel_configs SET" - " dust_limit_satoshis=%" PRIu64 "," - " max_htlc_value_in_flight_msat=%" PRIu64 "," - " channel_reserve_satoshis=%" PRIu64 "," - " htlc_minimum_msat=%" PRIu64 "," - " to_self_delay=%d," - " max_accepted_htlcs=%d" - " WHERE id=%" PRIu64 ";", - cc->dust_limit_satoshis, cc->max_htlc_value_in_flight_msat, - cc->channel_reserve_satoshis, cc->htlc_minimum_msat, - cc->to_self_delay, cc->max_accepted_htlcs, cc->id); + stmt = db_prepare(w->db, "UPDATE channel_configs SET" + " dust_limit_satoshis=?," + " max_htlc_value_in_flight_msat=?," + " channel_reserve_satoshis=?," + " htlc_minimum_msat=?," + " to_self_delay=?," + " max_accepted_htlcs=?" + " WHERE id=?;"); + sqlite3_bind_int64(stmt, 1, cc->dust_limit_satoshis); + sqlite3_bind_int64(stmt, 2, cc->max_htlc_value_in_flight_msat); + sqlite3_bind_int64(stmt, 3, cc->channel_reserve_satoshis); + sqlite3_bind_int64(stmt, 4, cc->htlc_minimum_msat); + sqlite3_bind_int(stmt, 5, cc->to_self_delay); + sqlite3_bind_int(stmt, 6, cc->max_accepted_htlcs); + sqlite3_bind_int64(stmt, 7, cc->id); + ok &= db_exec_prepared(w->db, stmt); return ok; } @@ -663,20 +629,23 @@ bool wallet_channel_save(struct wallet *w, struct wallet_channel *chan){ bool ok = true; struct peer *p = chan->peer; tal_t *tmpctx = tal_tmpctx(w); - - if (p->dbid == 0) { - /* Need to store the peer first */ - ok &= db_exec(__func__, w->db, - "INSERT INTO peers (node_id) VALUES ('%s');", - db_serialize_pubkey(tmpctx, &chan->peer->id)); - p->dbid = sqlite3_last_insert_rowid(w->db->sql); - } + sqlite3_stmt *stmt; db_begin_transaction(w->db); + if (p->dbid == 0) { + /* Need to store the peer first */ + stmt = db_prepare(w->db, "INSERT INTO peers (node_id) VALUES (?);"); + sqlite3_bind_pubkey(stmt, 1, &chan->peer->id); + db_exec_prepared(w->db, stmt); + p->dbid = sqlite3_last_insert_rowid(w->db->sql); + } + /* Insert a stub, that we can update, unifies INSERT and UPDATE paths */ if (chan->id == 0) { - ok &= db_exec(__func__, w->db, "INSERT INTO channels (peer_id) VALUES (%"PRIu64");", p->dbid); + stmt = db_prepare(w->db, "INSERT INTO channels (peer_id) VALUES (?);"); + sqlite3_bind_int64(stmt, 1, p->dbid); + db_exec_prepared(w->db, stmt); chan->id = sqlite3_last_insert_rowid(w->db->sql); } @@ -688,83 +657,99 @@ bool wallet_channel_save(struct wallet *w, struct wallet_channel *chan){ ok &= wallet_channel_config_save(w, &p->our_config); /* Now do the real update */ - ok &= db_exec(__func__, w->db, "UPDATE channels SET" - " shachain_remote_id=%"PRIu64"," - " short_channel_id=%s," - " state=%d," - " funder=%d," - " channel_flags=%d," - " minimum_depth=%d," - " next_index_local=%"PRIu64"," - " next_index_remote=%"PRIu64"," - " next_htlc_id=%"PRIu64"," - " funding_tx_id=%s," - " funding_tx_outnum=%d," - " funding_satoshi=%"PRIu64"," - " funding_locked_remote=%d," - " push_msatoshi=%"PRIu64"," - " msatoshi_local=%s," - " shutdown_scriptpubkey_remote='%s'," - " shutdown_keyidx_local=%"PRId64"," - " channel_config_local=%"PRIu64"," - " last_tx=%s, last_sig=%s" - " WHERE id=%"PRIu64, - p->their_shachain.id, - p->scid?tal_fmt(tmpctx,"'%s'", short_channel_id_to_str(tmpctx, p->scid)):"null", - p->state, - p->funder, - p->channel_flags, - p->minimum_depth, - p->next_index[LOCAL], - p->next_index[REMOTE], - p->next_htlc_id, - p->funding_txid?tal_fmt(tmpctx, "'%s'", tal_hexstr(tmpctx, p->funding_txid, sizeof(struct sha256_double))):"null", - p->funding_outnum, - p->funding_satoshi, - p->remote_funding_locked, - p->push_msat, - p->our_msatoshi?tal_fmt(tmpctx, "%"PRIu64, *p->our_msatoshi):"NULL", - 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), - chan->id); + stmt = db_prepare(w->db, tal_fmt(w, "UPDATE channels SET" + " shachain_remote_id=?," + " short_channel_id=?," + " state=?," + " funder=?," + " channel_flags=?," + " minimum_depth=?," + " next_index_local=?," + " next_index_remote=?," + " next_htlc_id=?," + " funding_tx_id=?," + " funding_tx_outnum=?," + " funding_satoshi=?," + " funding_locked_remote=?," + " push_msatoshi=?," + " msatoshi_local=?," + " shutdown_scriptpubkey_remote=?," + " shutdown_keyidx_local=?," + " channel_config_local=?," + " last_tx=?, last_sig=?" + " WHERE id=?")); + sqlite3_bind_int64(stmt, 1, p->their_shachain.id); + if (p->scid) + sqlite3_bind_short_channel_id(stmt, 2, p->scid); + sqlite3_bind_int(stmt, 3, p->state); + sqlite3_bind_int(stmt, 4, p->funder); + sqlite3_bind_int(stmt, 5, p->channel_flags); + sqlite3_bind_int(stmt, 6, p->minimum_depth); + + sqlite3_bind_int64(stmt, 7, p->next_index[LOCAL]); + sqlite3_bind_int64(stmt, 8, p->next_index[REMOTE]); + sqlite3_bind_int64(stmt, 9, p->next_htlc_id); + + if (p->funding_txid) + sqlite3_bind_blob(stmt, 10, p->funding_txid, sizeof(*p->funding_txid), SQLITE_TRANSIENT); + + sqlite3_bind_int(stmt, 11, p->funding_outnum); + sqlite3_bind_int64(stmt, 12, p->funding_satoshi); + sqlite3_bind_int(stmt, 13, p->remote_funding_locked); + sqlite3_bind_int64(stmt, 14, p->push_msat); + + if (p->our_msatoshi) + sqlite3_bind_int64(stmt, 15, *p->our_msatoshi); + + if (p->remote_shutdown_scriptpubkey) + sqlite3_bind_blob(stmt, 16, p->remote_shutdown_scriptpubkey, + tal_len(p->remote_shutdown_scriptpubkey), + SQLITE_TRANSIENT); + + sqlite3_bind_int64(stmt, 17, p->local_shutdown_idx); + sqlite3_bind_int64(stmt, 18, p->our_config.id); + if (p->last_tx) + sqlite3_bind_tx(stmt, 19, p->last_tx); + if (p->last_sig) + sqlite3_bind_signature(stmt, 20, p->last_sig); + sqlite3_bind_int64(stmt, 21, chan->id); + db_exec_prepared(w->db, stmt); if (chan->peer->channel_info) { ok &= wallet_channel_config_save(w, &p->channel_info->their_config); - ok &= db_exec(__func__, w->db, - "UPDATE channels SET" - " fundingkey_remote='%s'," - " revocation_basepoint_remote='%s'," - " payment_basepoint_remote='%s'," - " delayed_payment_basepoint_remote='%s'," - " per_commit_remote='%s'," - " old_per_commit_remote='%s'," - " feerate_per_kw=%d," - " channel_config_remote=%"PRIu64 - " WHERE id=%"PRIu64, - db_serialize_pubkey(tmpctx, &p->channel_info->remote_fundingkey), - db_serialize_pubkey(tmpctx, &p->channel_info->theirbase.revocation), - db_serialize_pubkey(tmpctx, &p->channel_info->theirbase.payment), - db_serialize_pubkey(tmpctx, &p->channel_info->theirbase.delayed_payment), - db_serialize_pubkey(tmpctx, &p->channel_info->remote_per_commit), - db_serialize_pubkey(tmpctx, &p->channel_info->old_remote_per_commit), - p->channel_info->feerate_per_kw, - p->channel_info->their_config.id, - chan->id); + stmt = db_prepare(w->db, "UPDATE channels SET" + " fundingkey_remote=?," + " revocation_basepoint_remote=?," + " payment_basepoint_remote=?," + " delayed_payment_basepoint_remote=?," + " per_commit_remote=?," + " old_per_commit_remote=?," + " feerate_per_kw=?," + " channel_config_remote=?" + " WHERE id=?"); + sqlite3_bind_pubkey(stmt, 1, &p->channel_info->remote_fundingkey); + sqlite3_bind_pubkey(stmt, 2, &p->channel_info->theirbase.revocation); + sqlite3_bind_pubkey(stmt, 3, &p->channel_info->theirbase.payment); + sqlite3_bind_pubkey(stmt, 4, &p->channel_info->theirbase.delayed_payment); + sqlite3_bind_pubkey(stmt, 5, &p->channel_info->remote_per_commit); + sqlite3_bind_pubkey(stmt, 6, &p->channel_info->old_remote_per_commit); + sqlite3_bind_int(stmt, 7, p->channel_info->feerate_per_kw); + sqlite3_bind_int64(stmt, 8, p->channel_info->their_config.id); + sqlite3_bind_int64(stmt, 9, chan->id); + ok &= db_exec_prepared(w->db, stmt); } /* If we have a last_sent_commit, store it */ if (chan->peer->last_sent_commit) { - ok &= db_exec(__func__, w->db, - "UPDATE channels SET" - " last_sent_commit_state=%d," - " last_sent_commit_id=%"PRIu64 - " WHERE id=%"PRIu64, - p->last_sent_commit->newstate, - p->last_sent_commit->id, - chan->id); + stmt = db_prepare(w->db, + "UPDATE channels SET" + " last_sent_commit_state=?," + " last_sent_commit_id=?" + " WHERE id=?"); + sqlite3_bind_int(stmt, 1, p->last_sent_commit->newstate); + sqlite3_bind_int64(stmt, 2, p->last_sent_commit->id); + sqlite3_bind_int64(stmt, 3, chan->id); + ok &= db_exec_prepared(w->db, stmt); } if (ok)