diff --git a/.travis.yml b/.travis.yml index f2b2a74b9509..b703ea33c612 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ notifications: email: false before_install: - sudo apt-get install -y libsqlite3-dev cppcheck valgrind gcc-4.8 + sudo apt-get install -y libsqlite3-dev cppcheck valgrind gcc-4.8 gettext env: - ARCH=64 SOURCE_CHECK_ONLY=true COPTFLAGS="-O3" diff --git a/.travis/build.sh b/.travis/build.sh index f49381da9ed8..63f67c42f463 100755 --- a/.travis/build.sh +++ b/.travis/build.sh @@ -29,8 +29,7 @@ if [ ! -f dependencies/bin/bitcoind ]; then fi pyenv global 3.7.1 -pip3 install --user --quiet mako -pip3 install --user --quiet -r tests/requirements.txt -r doc/requirements.txt +pip3 install --user --quiet -r requirements.txt -r tests/requirements.txt -r doc/requirements.txt pip3 install --quiet \ pytest-test-groups==1.0.3 diff --git a/configure b/configure index 3d5ae56d5773..3e1efd444c5a 100755 --- a/configure +++ b/configure @@ -259,6 +259,20 @@ int main(void) return 0; } /*END*/ +var=HAVE_SQLITE3 +desc=sqlite3 +style=DEFINES_EVERYTHING|EXECUTE|MAY_NOT_COMPILE +link=-lsqlite3 +code= +#include +#include + +int main(void) +{ + printf("%p\n", sqlite3_prepare_v2); + return 0; +} +/*END*/ var=HAVE_GCC desc=compiler is GCC style=OUTSIDE_MAIN diff --git a/devtools/sql-rewrite.py b/devtools/sql-rewrite.py new file mode 100755 index 000000000000..98d693bc2802 --- /dev/null +++ b/devtools/sql-rewrite.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python3 + +from mako.template import Template + +import sys + + +class Sqlite3Rewriter(object): + def rewrite(self, query): + return query + + +rewriters = { + "sqlite3": Sqlite3Rewriter(), +} + +template = Template("""#ifndef LIGHTNINGD_WALLET_GEN_DB_${f.upper()} +#define LIGHTNINGD_WALLET_GEN_DB_${f.upper()} + +#include +#include + +#if HAVE_${f.upper()} + +struct db_query db_${f}_queries[] = { + +% for elem in queries: + { + .name = "${elem['name']}", + .query = "${elem['query']}", + .placeholders = ${elem['placeholders']}, + .readonly = ${elem['readonly']}, + }, +% endfor +}; + +#define DB_${f.upper()}_QUERY_COUNT ${len(queries)} + +#endif /* HAVE_${f.upper()} */ + +#endif /* LIGHTNINGD_WALLET_GEN_DB_${f.upper()} */ +""") + + +def extract_queries(pofile): + # Given a po-file, extract all queries and their associated names, and + # return them as a list. + + def chunk(pofile): + # Chunk a given file into chunks separated by an empty line + with open(pofile, 'r') as f: + chunk = [] + for line in f: + line = line.strip() + if line.strip() == "": + yield chunk + chunk = [] + else: + chunk.append(line.strip()) + if chunk != []: + yield chunk + + queries = [] + for c in chunk(pofile): + name = c[0][3:] + + # Skip other comments + i = 1 + while c[i][0] == '#': + i += 1 + + # Strip header and surrounding quotes + query = c[i][7:][:-1] + + queries.append({ + 'name': name, + 'query': query, + 'placeholders': query.count('?'), + 'readonly': "true" if query.upper().startswith("SELECT") else "false", + }) + return queries + + +if __name__ == "__main__": + if len(sys.argv) != 3: + print("Usage:\n\t{} ".format(sys.argv[0])) + sys.exit(1) + + dialect = sys.argv[2] + + if dialect not in rewriters: + print("Unknown dialect {}. The following are available: {}".format( + dialect, + ", ".join(rewriters.keys()) + )) + sys.exit(1) + + rewriter = rewriters[dialect] + + queries = extract_queries(sys.argv[1]) + queries = rewriter.rewrite(queries) + + print(template.render(f=dialect, queries=queries)) diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 67f1900a9241..5e75a9831b0f 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -572,11 +572,7 @@ static void pidfile_create(const struct lightningd *ld) * extra sanity checks, and it's also a good point to free the tmpctx. */ static int io_poll_lightningd(struct pollfd *fds, nfds_t nfds, int timeout) { - /*~ In particular, we should *not* have left a database transaction - * open! */ - db_assert_no_outstanding_statements(); - - /* The other checks and freeing tmpctx are common to all daemons. */ + /* These checks and freeing tmpctx are common to all daemons. */ return daemon_poll(fds, nfds, timeout); } diff --git a/lightningd/subd.c b/lightningd/subd.c index 319598487364..5a9e83e46b45 100644 --- a/lightningd/subd.c +++ b/lightningd/subd.c @@ -555,7 +555,7 @@ static void destroy_subd(struct subd *sd) sd->channel = NULL; /* We can be freed both inside msg handling, or spontaneously. */ - outer_transaction = db->in_transaction; + outer_transaction = db_in_transaction(db); if (!outer_transaction) db_begin_transaction(db); if (sd->errcb) diff --git a/lightningd/test/run-find_my_abspath.c b/lightningd/test/run-find_my_abspath.c index 678e4ee4ad26..4d645e333cd1 100644 --- a/lightningd/test/run-find_my_abspath.c +++ b/lightningd/test/run-find_my_abspath.c @@ -45,9 +45,6 @@ void daemon_setup(const char *argv0 UNNEEDED, /* Generated stub for daemon_shutdown */ void daemon_shutdown(void) { fprintf(stderr, "daemon_shutdown called!\n"); abort(); } -/* Generated stub for db_assert_no_outstanding_statements */ -void db_assert_no_outstanding_statements(void) -{ fprintf(stderr, "db_assert_no_outstanding_statements called!\n"); abort(); } /* Generated stub for db_begin_transaction_ */ void db_begin_transaction_(struct db *db UNNEEDED, const char *location UNNEEDED) { fprintf(stderr, "db_begin_transaction_ called!\n"); abort(); } @@ -57,6 +54,9 @@ void db_commit_transaction(struct db *db UNNEEDED) /* Generated stub for db_get_intvar */ s64 db_get_intvar(struct db *db UNNEEDED, char *varname UNNEEDED, s64 defval UNNEEDED) { fprintf(stderr, "db_get_intvar called!\n"); abort(); } +/* Generated stub for db_in_transaction */ +bool db_in_transaction(struct db *db UNNEEDED) +{ fprintf(stderr, "db_in_transaction called!\n"); abort(); } /* Generated stub for fatal */ void fatal(const char *fmt UNNEEDED, ...) { fprintf(stderr, "fatal called!\n"); abort(); } diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 000000000000..5cca28d2b98e --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +sqlparse==0.3.0 +mako==1.0.14 +mrkd==0.1.5 diff --git a/tests/plugins/dblog.py b/tests/plugins/dblog.py index 97d1b8ed0ef9..ba2f40a9a533 100755 --- a/tests/plugins/dblog.py +++ b/tests/plugins/dblog.py @@ -16,9 +16,18 @@ def init(configuration, options, plugin): plugin.conn = sqlite3.connect(plugin.get_option('dblog-file'), isolation_level=None) plugin.log("replaying pre-init data:") + plugin.conn.execute("PRAGMA foreign_keys = ON;") + + print(plugin.sqlite_pre_init_cmds) + + plugin.conn.execute("BEGIN TRANSACTION;") + for c in plugin.sqlite_pre_init_cmds: plugin.conn.execute(c) plugin.log("{}".format(c)) + + plugin.conn.execute("COMMIT;") + plugin.initted = True plugin.log("initialized {}".format(configuration)) @@ -29,9 +38,15 @@ def db_write(plugin, writes, **kwargs): plugin.log("deferring {} commands".format(len(writes))) plugin.sqlite_pre_init_cmds += writes else: + print(writes) + plugin.conn.execute("BEGIN TRANSACTION;") + for c in writes: plugin.conn.execute(c) plugin.log("{}".format(c)) + + plugin.conn.execute("COMMIT;") + return True diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 863208a12f22..cbe43cdcdb61 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -299,10 +299,9 @@ def test_db_hook(node_factory, executor): # It should see the db being created, and sometime later actually get # initted. # This precedes startup, so needle already past - assert l1.daemon.is_in_log('plugin-dblog.py deferring 1 commands') + assert l1.daemon.is_in_log(r'plugin-dblog.py deferring \d+ commands') l1.daemon.logsearch_start = 0 l1.daemon.wait_for_log('plugin-dblog.py replaying pre-init data:') - l1.daemon.wait_for_log('plugin-dblog.py PRAGMA foreign_keys = ON;') l1.daemon.wait_for_log('plugin-dblog.py CREATE TABLE version \\(version INTEGER\\)') l1.daemon.wait_for_log("plugin-dblog.py initialized.* 'startup': True") diff --git a/wallet/Makefile b/wallet/Makefile index 6f5c9e28e9e0..53993c5b4058 100644 --- a/wallet/Makefile +++ b/wallet/Makefile @@ -11,7 +11,10 @@ WALLET_LIB_SRC := \ wallet/wallet.c \ wallet/walletrpc.c -WALLET_LIB_OBJS := $(WALLET_LIB_SRC:.c=.o) +WALLET_DB_DRIVERS := \ + wallet/db_sqlite3.c + +WALLET_LIB_OBJS := $(WALLET_LIB_SRC:.c=.o) $(WALLET_DB_DRIVERS:.c=.o) WALLET_LIB_HEADERS := $(WALLET_LIB_SRC:.c=.h) # Make sure these depend on everything. @@ -26,7 +29,25 @@ check-source-bolt: $(WALLET_LIB_SRC:%=bolt-check/%) $(WALLET_LIB_HEADERS:%=bolt- clean: wallet-clean +wallet/db_sqlite3.c: wallet/gen_db_sqlite3.c + +# The following files contain SQL-annotated statements that we need to extact +SQL_FILES := \ + wallet/db.c \ + wallet/invoices.c \ + wallet/wallet.c \ + wallet/test/run-db.c \ + wallet/test/run-wallet.c \ + +wallet/statements.po: $(SQL_FILES) + xgettext -kNAMED_SQL -kSQL --add-location --no-wrap --omit-header -o $@ $(SQL_FILES) + +wallet/gen_db_sqlite3.c: wallet/statements.po devtools/sql-rewrite.py + devtools/sql-rewrite.py wallet/statements.po sqlite3 > wallet/gen_db_sqlite3.c + wallet-clean: $(RM) $(WALLET_LIB_OBJS) + $(RM) wallet/statements.po + $(RM) wallet/gen_db_sqlite3.c include wallet/test/Makefile diff --git a/wallet/db.c b/wallet/db.c index 935f6753d7ea..40d2f139bafe 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -1,7 +1,6 @@ #include "db.h" #include -#include #include #include #include @@ -9,15 +8,11 @@ #include #include #include +#include #define DB_FILE "lightningd.sqlite3" #define NSEC_IN_SEC 1000000000 -/* For testing, we want to catch fatal messages. */ -#ifndef db_fatal -#define db_fatal fatal -#endif - struct migration { const char *sql; void (*func)(struct lightningd *ld, struct db *db); @@ -29,588 +24,565 @@ void migrate_pr2342_feerate_per_channel(struct lightningd *ld, struct db *db); * migrate existing databases from a previous state, based on the * string indices */ static struct migration dbmigrations[] = { - { "CREATE TABLE version (version INTEGER)", NULL }, - { "INSERT INTO version VALUES (1)", NULL }, - { "CREATE TABLE outputs ( \ - prev_out_tx CHAR(64), \ - prev_out_index INTEGER, \ - value INTEGER, \ - type INTEGER, \ - status INTEGER, \ - keyindex INTEGER, \ - PRIMARY KEY (prev_out_tx, prev_out_index) \ - );", NULL }, - { "CREATE TABLE vars (name VARCHAR(32), val VARCHAR(255), PRIMARY KEY (name));", NULL }, - { "CREATE TABLE shachains ( \ - id INTEGER, \ - min_index INTEGER, \ - num_valid INTEGER, \ - PRIMARY KEY (id));", NULL }, - { "CREATE TABLE shachain_known ( \ - shachain_id INTEGER REFERENCES shachains(id) ON DELETE CASCADE, \ - pos INTEGER, \ - idx INTEGER, \ - hash BLOB, \ - PRIMARY KEY (shachain_id, pos));", NULL }, - { "CREATE TABLE channels (" - " id INTEGER," /* chan->id */ - " peer_id INTEGER REFERENCES peers(id) ON DELETE CASCADE," - " short_channel_id BLOB," - " channel_config_local INTEGER," - " channel_config_remote INTEGER," - " state INTEGER," - " funder INTEGER," - " channel_flags INTEGER," - " minimum_depth INTEGER," - " next_index_local INTEGER," - " next_index_remote INTEGER," - " next_htlc_id INTEGER, " - " funding_tx_id BLOB," - " funding_tx_outnum INTEGER," - " funding_satoshi INTEGER," - " funding_locked_remote INTEGER," - " push_msatoshi INTEGER," - " msatoshi_local INTEGER," /* our_msatoshi */ - /* START channel_info */ - " fundingkey_remote BLOB," - " revocation_basepoint_remote BLOB," - " payment_basepoint_remote BLOB," - " htlc_basepoint_remote BLOB," - " delayed_payment_basepoint_remote BLOB," - " per_commit_remote BLOB," - " old_per_commit_remote BLOB," - " local_feerate_per_kw INTEGER," - " remote_feerate_per_kw INTEGER," - /* END channel_info */ - " shachain_remote_id INTEGER," - " shutdown_scriptpubkey_remote BLOB," - " 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)" - ");", NULL }, - { "CREATE TABLE peers (" - " id INTEGER," - " node_id BLOB UNIQUE," /* pubkey */ - " address TEXT," - " PRIMARY KEY (id)" - ");", NULL }, - { "CREATE TABLE channel_configs (" - " id INTEGER," - " dust_limit_satoshis INTEGER," - " max_htlc_value_in_flight_msat INTEGER," - " channel_reserve_satoshis INTEGER," - " htlc_minimum_msat INTEGER," - " to_self_delay INTEGER," - " max_accepted_htlcs INTEGER," - " PRIMARY KEY (id)" - ");", NULL }, - { "CREATE TABLE channel_htlcs (" - " id INTEGER," - " channel_id INTEGER REFERENCES channels(id) ON DELETE CASCADE," - " channel_htlc_id INTEGER," - " direction INTEGER," - " origin_htlc INTEGER," - " msatoshi INTEGER," - " cltv_expiry INTEGER," - " payment_hash BLOB," - " payment_key BLOB," - " routing_onion BLOB," - " failuremsg BLOB," - " malformed_onion INTEGER," - " hstate INTEGER," - " shared_secret BLOB," - " PRIMARY KEY (id)," - " UNIQUE (channel_id, channel_htlc_id, direction)" - ");", NULL }, - { "CREATE TABLE invoices (" - " id INTEGER," - " state INTEGER," - " msatoshi INTEGER," - " payment_hash BLOB," - " payment_key BLOB," - " label TEXT," - " PRIMARY KEY (id)," - " UNIQUE (label)," - " UNIQUE (payment_hash)" - ");", NULL }, - { "CREATE TABLE payments (" - " id INTEGER," - " timestamp INTEGER," - " status INTEGER," - " payment_hash BLOB," - " direction INTEGER," - " destination BLOB," - " msatoshi INTEGER," - " PRIMARY KEY (id)," - " UNIQUE (payment_hash)" - ");", NULL }, - /* Add expiry field to invoices (effectively infinite). */ - { "ALTER TABLE invoices ADD expiry_time INTEGER;", NULL }, - { "UPDATE invoices SET expiry_time=9223372036854775807;", NULL }, - /* Add pay_index field to paid invoices (initially, same order as id). */ - { "ALTER TABLE invoices ADD pay_index INTEGER;", NULL }, - { "CREATE UNIQUE INDEX invoices_pay_index" - " ON invoices(pay_index);", NULL }, - { "UPDATE invoices SET pay_index=id WHERE state=1;", NULL }, /* only paid invoice */ - /* Create next_pay_index variable (highest pay_index). */ - { "INSERT OR REPLACE INTO vars(name, val)" - " VALUES('next_pay_index', " - " COALESCE((SELECT MAX(pay_index) FROM invoices WHERE state=1), 0) + 1" - " );", NULL }, - /* Create first_block field; initialize from channel id if any. - * This fails for channels still awaiting lockin, but that only applies to - * pre-release software, so it's forgivable. */ - { "ALTER TABLE channels ADD first_blocknum INTEGER;", NULL }, - { "UPDATE channels SET first_blocknum=CAST(short_channel_id AS INTEGER) WHERE short_channel_id IS NOT NULL;", NULL }, - { "ALTER TABLE outputs ADD COLUMN channel_id INTEGER;", NULL }, - { "ALTER TABLE outputs ADD COLUMN peer_id BLOB;", NULL }, - { "ALTER TABLE outputs ADD COLUMN commitment_point BLOB;", NULL }, - { "ALTER TABLE invoices ADD COLUMN msatoshi_received INTEGER;", NULL }, - /* Normally impossible, so at least we'll know if databases are ancient. */ - { "UPDATE invoices SET msatoshi_received=0 WHERE state=1;", NULL }, - { "ALTER TABLE channels ADD COLUMN last_was_revoke INTEGER;", NULL }, - /* We no longer record incoming payments: invoices cover that. - * Without ALTER_TABLE DROP COLUMN support we need to do this by - * rename & copy, which works because there are no triggers etc. */ - { "ALTER TABLE payments RENAME TO temp_payments;", NULL }, - { "CREATE TABLE payments (" - " id INTEGER," - " timestamp INTEGER," - " status INTEGER," - " payment_hash BLOB," - " destination BLOB," - " msatoshi INTEGER," - " PRIMARY KEY (id)," - " UNIQUE (payment_hash)" - ");", NULL }, - { "INSERT INTO payments SELECT id, timestamp, status, payment_hash, destination, msatoshi FROM temp_payments WHERE direction=1;", NULL }, - { "DROP TABLE temp_payments;", NULL }, - /* We need to keep the preimage in case they ask to pay again. */ - { "ALTER TABLE payments ADD COLUMN payment_preimage BLOB;", NULL }, - /* We need to keep the shared secrets to decode error returns. */ - { "ALTER TABLE payments ADD COLUMN path_secrets BLOB;", NULL }, - /* Create time-of-payment of invoice, default already-paid - * invoices to current time. */ - { "ALTER TABLE invoices ADD paid_timestamp INTEGER;", NULL }, - { "UPDATE invoices" - " SET paid_timestamp = strftime('%s', 'now')" - " WHERE state = 1;", NULL }, - /* We need to keep the route node pubkeys and short channel ids to - * correctly mark routing failures. We separate short channel ids - * because we cannot safely save them as blobs due to byteorder - * concerns. */ - { "ALTER TABLE payments ADD COLUMN route_nodes BLOB;", NULL }, - { "ALTER TABLE payments ADD COLUMN route_channels TEXT;", NULL }, - { "CREATE TABLE htlc_sigs (channelid INTEGER REFERENCES channels(id) ON DELETE CASCADE, signature BLOB);", NULL }, - { "CREATE INDEX channel_idx ON htlc_sigs (channelid)", NULL }, - /* Get rid of OPENINGD entries; we don't put them in db any more */ - { "DELETE FROM channels WHERE state=1", NULL }, - /* Keep track of db upgrades, for debugging */ - { "CREATE TABLE db_upgrades (upgrade_from INTEGER, lightning_version TEXT);", NULL }, - /* We used not to clean up peers when their channels were gone. */ - { "DELETE FROM peers WHERE id NOT IN (SELECT peer_id FROM channels);", NULL }, - /* The ONCHAIND_CHEATED/THEIR_UNILATERAL/OUR_UNILATERAL/MUTUAL are now one */ - { "UPDATE channels SET STATE = 8 WHERE state > 8;", NULL }, - /* Add bolt11 to invoices table*/ - { "ALTER TABLE invoices ADD bolt11 TEXT;", NULL }, - /* What do we think the head of the blockchain looks like? Used - * primarily to track confirmations across restarts and making - * sure we handle reorgs correctly. */ - { "CREATE TABLE blocks (height INT, hash BLOB, prev_hash BLOB, UNIQUE(height));", NULL }, - /* ON DELETE CASCADE would have been nice for confirmation_height, - * so that we automatically delete outputs that fall off the - * blockchain and then we rediscover them if they are included - * again. However, we have the their_unilateral/to_us which we - * can't simply recognize from the chain without additional - * hints. So we just mark them as unconfirmed should the block - * die. */ - { "ALTER TABLE outputs ADD COLUMN confirmation_height INTEGER REFERENCES blocks(height) ON DELETE SET NULL;", NULL }, - { "ALTER TABLE outputs ADD COLUMN spend_height INTEGER REFERENCES blocks(height) ON DELETE SET NULL;", NULL }, - /* Create a covering index that covers both fields */ - { "CREATE INDEX output_height_idx ON outputs (confirmation_height, spend_height);", NULL }, - { "CREATE TABLE utxoset (" - " txid BLOB," - " outnum INT," - " blockheight INT REFERENCES blocks(height) ON DELETE CASCADE," - " spendheight INT REFERENCES blocks(height) ON DELETE SET NULL," - " txindex INT," - " scriptpubkey BLOB," - " satoshis BIGINT," - " PRIMARY KEY(txid, outnum));", NULL }, - { "CREATE INDEX short_channel_id ON utxoset (blockheight, txindex, outnum)", NULL }, - /* Necessary index for long rollbacks of the blockchain, otherwise we're - * doing table scans for every block removed. */ - { "CREATE INDEX utxoset_spend ON utxoset (spendheight)", NULL }, - /* Assign key 0 to unassigned shutdown_keyidx_local. */ - { "UPDATE channels SET shutdown_keyidx_local=0 WHERE shutdown_keyidx_local = -1;", NULL }, - /* FIXME: We should rename shutdown_keyidx_local to final_key_index */ - /* -- Payment routing failure information -- */ - /* BLOB if failure was due to unparseable onion, NULL otherwise */ - { "ALTER TABLE payments ADD failonionreply BLOB;", NULL }, - /* 0 if we could theoretically retry, 1 if PERM fail at payee */ - { "ALTER TABLE payments ADD faildestperm INTEGER;", NULL }, - /* Contents of routing_failure (only if not unparseable onion) */ - { "ALTER TABLE payments ADD failindex INTEGER;", NULL }, /* erring_index */ - { "ALTER TABLE payments ADD failcode INTEGER;", NULL }, /* failcode */ - { "ALTER TABLE payments ADD failnode BLOB;", NULL }, /* erring_node */ - { "ALTER TABLE payments ADD failchannel BLOB;", NULL }, /* erring_channel */ - { "ALTER TABLE payments ADD failupdate BLOB;", NULL }, /* channel_update - can be NULL*/ - /* -- Payment routing failure information ends -- */ - /* Delete route data for already succeeded or failed payments */ - { "UPDATE payments" - " SET path_secrets = NULL" - " , route_nodes = NULL" - " , route_channels = NULL" - " WHERE status <> 0;", NULL }, /* PAYMENT_PENDING */ - /* -- Routing statistics -- */ - { "ALTER TABLE channels ADD in_payments_offered INTEGER;", NULL }, - { "ALTER TABLE channels ADD in_payments_fulfilled INTEGER;", NULL }, - { "ALTER TABLE channels ADD in_msatoshi_offered INTEGER;", NULL }, - { "ALTER TABLE channels ADD in_msatoshi_fulfilled INTEGER;", NULL }, - { "ALTER TABLE channels ADD out_payments_offered INTEGER;", NULL }, - { "ALTER TABLE channels ADD out_payments_fulfilled INTEGER;", NULL }, - { "ALTER TABLE channels ADD out_msatoshi_offered INTEGER;", NULL }, - { "ALTER TABLE channels ADD out_msatoshi_fulfilled INTEGER;", NULL }, - { "UPDATE channels" - " SET in_payments_offered = 0, in_payments_fulfilled = 0" - " , in_msatoshi_offered = 0, in_msatoshi_fulfilled = 0" - " , out_payments_offered = 0, out_payments_fulfilled = 0" - " , out_msatoshi_offered = 0, out_msatoshi_fulfilled = 0" - " ;", NULL }, - /* -- Routing statistics ends --*/ - /* Record the msatoshi actually sent in a payment. */ - { "ALTER TABLE payments ADD msatoshi_sent INTEGER;", NULL }, - { "UPDATE payments SET msatoshi_sent = msatoshi;", NULL }, - /* Delete dangling utxoset entries due to Issue #1280 */ - { "DELETE FROM utxoset WHERE blockheight IN (" - " SELECT DISTINCT(blockheight)" - " FROM utxoset LEFT OUTER JOIN blocks on (blockheight == blocks.height) " - " WHERE blocks.hash IS NULL" - ");", NULL }, - /* Record feerate range, to optimize onchaind grinding actual fees. */ - { "ALTER TABLE channels ADD min_possible_feerate INTEGER;", NULL }, - { "ALTER TABLE channels ADD max_possible_feerate INTEGER;", NULL }, - /* https://bitcoinfees.github.io/#1d says Dec 17 peak was ~1M sat/kb - * which is 250,000 sat/Sipa */ - { "UPDATE channels SET min_possible_feerate=0, max_possible_feerate=250000;", NULL }, - /* -- Min and max msatoshi_to_us -- */ - { "ALTER TABLE channels ADD msatoshi_to_us_min INTEGER;", NULL }, - { "ALTER TABLE channels ADD msatoshi_to_us_max INTEGER;", NULL }, - { "UPDATE channels" - " SET msatoshi_to_us_min = msatoshi_local" - " , msatoshi_to_us_max = msatoshi_local" - " ;", NULL }, - /* -- Min and max msatoshi_to_us ends -- */ - /* Transactions we are interested in. Either we sent them ourselves or we - * are watching them. We don't cascade block height deletes so we don't - * forget any of them by accident.*/ - { "CREATE TABLE transactions (" - " id BLOB" - ", blockheight INTEGER REFERENCES blocks(height) ON DELETE SET NULL" - ", txindex INTEGER" - ", rawtx BLOB" - ", PRIMARY KEY (id)" - ");", NULL }, - /* -- Detailed payment failure -- */ - { "ALTER TABLE payments ADD faildetail TEXT;", NULL }, - { "UPDATE payments" - " SET faildetail = 'unspecified payment failure reason'" - " WHERE status = 2;", NULL }, /* PAYMENT_FAILED */ - /* -- Detailed payment faiure ends -- */ - { "CREATE TABLE channeltxs (" - /* The id serves as insertion order and short ID */ - " id INTEGER" - ", channel_id INTEGER REFERENCES channels(id) ON DELETE CASCADE" - ", type INTEGER" - ", transaction_id BLOB REFERENCES transactions(id) ON DELETE CASCADE" - /* The input_num is only used by the txo_watch, 0 if txwatch */ - ", input_num INTEGER" - /* The height at which we sent the depth notice */ - ", blockheight INTEGER REFERENCES blocks(height) ON DELETE CASCADE" - ", PRIMARY KEY(id)" - ");", NULL }, - /* -- Set the correct rescan height for PR #1398 -- */ - /* Delete blocks that are higher than our initial scan point, this is a - * no-op if we don't have a channel. */ - { "DELETE FROM blocks WHERE height > (SELECT MIN(first_blocknum) FROM channels);", NULL }, - /* Now make sure we have the lower bound block with the first_blocknum - * height. This may introduce a block with NULL height if we didn't have any - * blocks, remove that in the next. */ - { "INSERT OR IGNORE INTO blocks (height) VALUES ((SELECT MIN(first_blocknum) FROM channels));", NULL }, - { "DELETE FROM blocks WHERE height IS NULL;", NULL }, - /* -- End of PR #1398 -- */ - { "ALTER TABLE invoices ADD description TEXT;", NULL }, - /* FIXME: payments table 'description' is really a 'label' */ - { "ALTER TABLE payments ADD description TEXT;", NULL }, - /* future_per_commitment_point if other side proves we're out of date -- */ - { "ALTER TABLE channels ADD future_per_commitment_point BLOB;", NULL }, - /* last_sent_commit array fix */ - { "ALTER TABLE channels ADD last_sent_commit BLOB;", NULL }, - /* Stats table to track forwarded HTLCs. The values in the HTLCs - * and their states are replicated here and the entries are not - * deleted when the HTLC entries or the channel entries are - * deleted to avoid unexpected drops in statistics. */ - { "CREATE TABLE forwarded_payments (" - " in_htlc_id INTEGER REFERENCES channel_htlcs(id) ON DELETE SET NULL" - ", out_htlc_id INTEGER REFERENCES channel_htlcs(id) ON DELETE SET NULL" - ", in_channel_scid INTEGER" - ", out_channel_scid INTEGER" - ", in_msatoshi INTEGER" - ", out_msatoshi INTEGER" - ", state INTEGER" - ", UNIQUE(in_htlc_id, out_htlc_id)" - ");", NULL }, - /* Add a direction for failed payments. */ - { "ALTER TABLE payments ADD faildirection INTEGER;", NULL }, /* erring_direction */ - /* Fix dangling peers with no channels. */ - { "DELETE FROM peers WHERE id NOT IN (SELECT peer_id FROM channels);", NULL }, - { "ALTER TABLE outputs ADD scriptpubkey BLOB;", NULL }, - /* Keep bolt11 string for payments. */ - { "ALTER TABLE payments ADD bolt11 TEXT;", NULL }, - /* PR #2342 feerate per channel */ - { "ALTER TABLE channels ADD feerate_base INTEGER;", NULL }, - { "ALTER TABLE channels ADD feerate_ppm INTEGER;", NULL }, - { NULL, migrate_pr2342_feerate_per_channel }, - { "ALTER TABLE channel_htlcs ADD received_time INTEGER", NULL }, - { "ALTER TABLE forwarded_payments ADD received_time INTEGER", NULL }, - { "ALTER TABLE forwarded_payments ADD resolved_time INTEGER", NULL }, - { "ALTER TABLE channels ADD remote_upfront_shutdown_script BLOB;", NULL }, - /* PR #2524: Add failcode into forward_payment */ - { "ALTER TABLE forwarded_payments ADD failcode INTEGER;", NULL }, - /* remote signatures for channel announcement */ - { "ALTER TABLE channels ADD remote_ann_node_sig BLOB;", NULL }, - { "ALTER TABLE channels ADD remote_ann_bitcoin_sig BLOB;", NULL }, - /* Additional information for transaction tracking and listing */ - { "ALTER TABLE transactions ADD type INTEGER;", NULL }, - /* Not a foreign key on purpose since we still delete channels from - * the DB which would remove this. It is mainly used to group payments - * in the list view anyway, e.g., show all close and htlc transactions - * as a single bundle. */ - { "ALTER TABLE transactions ADD channel_id INTEGER;", NULL}, + {SQL("CREATE TABLE version (version INTEGER)"), NULL}, + {SQL("INSERT INTO version VALUES (1)"), NULL}, + {SQL("CREATE TABLE outputs (" + " prev_out_tx CHAR(64)" + ", prev_out_index INTEGER" + ", value INTEGER" + ", type INTEGER" + ", status INTEGER" + ", keyindex INTEGER" + ", PRIMARY KEY (prev_out_tx, prev_out_index));"), + NULL}, + {SQL("CREATE TABLE vars (" + " name VARCHAR(32)" + ", val VARCHAR(255)" + ", PRIMARY KEY (name)" + ");"), + NULL}, + {SQL("CREATE TABLE shachains (" + " id INTEGER" + ", min_index INTEGER" + ", num_valid INTEGER" + ", PRIMARY KEY (id)" + ");"), + NULL}, + {SQL("CREATE TABLE shachain_known (" + " shachain_id INTEGER REFERENCES shachains(id) ON DELETE CASCADE" + ", pos INTEGER" + ", idx INTEGER" + ", hash BLOB" + ", PRIMARY KEY (shachain_id, pos)" + ");"), + NULL}, + {SQL("CREATE TABLE channels (" + " id INTEGER," /* chan->id */ + " peer_id INTEGER REFERENCES peers(id) ON DELETE CASCADE," + " short_channel_id BLOB," + " channel_config_local INTEGER," + " channel_config_remote INTEGER," + " state INTEGER," + " funder INTEGER," + " channel_flags INTEGER," + " minimum_depth INTEGER," + " next_index_local INTEGER," + " next_index_remote INTEGER," + " next_htlc_id INTEGER, " + " funding_tx_id BLOB," + " funding_tx_outnum INTEGER," + " funding_satoshi INTEGER," + " funding_locked_remote INTEGER," + " push_msatoshi INTEGER," + " msatoshi_local INTEGER," /* our_msatoshi */ + /* START channel_info */ + " fundingkey_remote BLOB," + " revocation_basepoint_remote BLOB," + " payment_basepoint_remote BLOB," + " htlc_basepoint_remote BLOB," + " delayed_payment_basepoint_remote BLOB," + " per_commit_remote BLOB," + " old_per_commit_remote BLOB," + " local_feerate_per_kw INTEGER," + " remote_feerate_per_kw INTEGER," + /* END channel_info */ + " shachain_remote_id INTEGER," + " shutdown_scriptpubkey_remote BLOB," + " 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)" + ");"), + NULL}, + {SQL("CREATE TABLE peers (" + " id INTEGER" + ", node_id BLOB UNIQUE" /* pubkey */ + ", address TEXT" + ", PRIMARY KEY (id)" + ");"), + NULL}, + {SQL("CREATE TABLE channel_configs (" + " id INTEGER," + " dust_limit_satoshis INTEGER," + " max_htlc_value_in_flight_msat INTEGER," + " channel_reserve_satoshis INTEGER," + " htlc_minimum_msat INTEGER," + " to_self_delay INTEGER," + " max_accepted_htlcs INTEGER," + " PRIMARY KEY (id)" + ");"), + NULL}, + {SQL("CREATE TABLE channel_htlcs (" + " id INTEGER," + " channel_id INTEGER REFERENCES channels(id) ON DELETE CASCADE," + " channel_htlc_id INTEGER," + " direction INTEGER," + " origin_htlc INTEGER," + " msatoshi INTEGER," + " cltv_expiry INTEGER," + " payment_hash BLOB," + " payment_key BLOB," + " routing_onion BLOB," + " failuremsg BLOB," + " malformed_onion INTEGER," + " hstate INTEGER," + " shared_secret BLOB," + " PRIMARY KEY (id)," + " UNIQUE (channel_id, channel_htlc_id, direction)" + ");"), + NULL}, + {SQL("CREATE TABLE invoices (" + " id INTEGER," + " state INTEGER," + " msatoshi INTEGER," + " payment_hash BLOB," + " payment_key BLOB," + " label TEXT," + " PRIMARY KEY (id)," + " UNIQUE (label)," + " UNIQUE (payment_hash)" + ");"), + NULL}, + {SQL("CREATE TABLE payments (" + " id INTEGER," + " timestamp INTEGER," + " status INTEGER," + " payment_hash BLOB," + " direction INTEGER," + " destination BLOB," + " msatoshi INTEGER," + " PRIMARY KEY (id)," + " UNIQUE (payment_hash)" + ");"), + NULL}, + /* Add expiry field to invoices (effectively infinite). */ + {SQL("ALTER TABLE invoices ADD expiry_time INTEGER;"), NULL}, + {SQL("UPDATE invoices SET expiry_time=9223372036854775807;"), NULL}, + /* Add pay_index field to paid invoices (initially, same order as id). */ + {SQL("ALTER TABLE invoices ADD pay_index INTEGER;"), NULL}, + {SQL("CREATE UNIQUE INDEX invoices_pay_index ON invoices(pay_index);"), + NULL}, + {SQL("UPDATE invoices SET pay_index=id WHERE state=1;"), + NULL}, /* only paid invoice */ + /* Create next_pay_index variable (highest pay_index). */ + {SQL("INSERT OR REPLACE INTO vars(name, val)" + " VALUES('next_pay_index', " + " COALESCE((SELECT MAX(pay_index) FROM invoices WHERE state=1), 0) " + "+ 1" + " );"), + NULL}, + /* Create first_block field; initialize from channel id if any. + * This fails for channels still awaiting lockin, but that only applies to + * pre-release software, so it's forgivable. */ + {SQL("ALTER TABLE channels ADD first_blocknum INTEGER;"), NULL}, + {SQL("UPDATE channels SET first_blocknum=CAST(short_channel_id AS INTEGER) " + "WHERE short_channel_id IS NOT NULL;"), + NULL}, + {SQL("ALTER TABLE outputs ADD COLUMN channel_id INTEGER;"), NULL}, + {SQL("ALTER TABLE outputs ADD COLUMN peer_id BLOB;"), NULL}, + {SQL("ALTER TABLE outputs ADD COLUMN commitment_point BLOB;"), NULL}, + {SQL("ALTER TABLE invoices ADD COLUMN msatoshi_received INTEGER;"), NULL}, + /* Normally impossible, so at least we'll know if databases are ancient. */ + {SQL("UPDATE invoices SET msatoshi_received=0 WHERE state=1;"), NULL}, + {SQL("ALTER TABLE channels ADD COLUMN last_was_revoke INTEGER;"), NULL}, + /* We no longer record incoming payments: invoices cover that. + * Without ALTER_TABLE DROP COLUMN support we need to do this by + * rename & copy, which works because there are no triggers etc. */ + {SQL("ALTER TABLE payments RENAME TO temp_payments;"), NULL}, + {SQL("CREATE TABLE payments (" + " id INTEGER," + " timestamp INTEGER," + " status INTEGER," + " payment_hash BLOB," + " destination BLOB," + " msatoshi INTEGER," + " PRIMARY KEY (id)," + " UNIQUE (payment_hash)" + ");"), + NULL}, + {SQL("INSERT INTO payments SELECT id, timestamp, status, payment_hash, " + "destination, msatoshi FROM temp_payments WHERE direction=1;"), + NULL}, + {SQL("DROP TABLE temp_payments;"), NULL}, + /* We need to keep the preimage in case they ask to pay again. */ + {SQL("ALTER TABLE payments ADD COLUMN payment_preimage BLOB;"), NULL}, + /* We need to keep the shared secrets to decode error returns. */ + {SQL("ALTER TABLE payments ADD COLUMN path_secrets BLOB;"), NULL}, + /* Create time-of-payment of invoice, default already-paid + * invoices to current time. */ + {SQL("ALTER TABLE invoices ADD paid_timestamp INTEGER;"), NULL}, + {SQL("UPDATE invoices" + " SET paid_timestamp = strftime('%s', 'now')" + " WHERE state = 1;"), + NULL}, + /* We need to keep the route node pubkeys and short channel ids to + * correctly mark routing failures. We separate short channel ids + * because we cannot safely save them as blobs due to byteorder + * concerns. */ + {SQL("ALTER TABLE payments ADD COLUMN route_nodes BLOB;"), NULL}, + {SQL("ALTER TABLE payments ADD COLUMN route_channels TEXT;"), NULL}, + {SQL("CREATE TABLE htlc_sigs (channelid INTEGER REFERENCES channels(id) ON " + "DELETE CASCADE, signature BLOB);"), + NULL}, + {SQL("CREATE INDEX channel_idx ON htlc_sigs (channelid)"), NULL}, + /* Get rid of OPENINGD entries; we don't put them in db any more */ + {SQL("DELETE FROM channels WHERE state=1"), NULL}, + /* Keep track of db upgrades, for debugging */ + {SQL("CREATE TABLE db_upgrades (upgrade_from INTEGER, lightning_version " + "TEXT);"), + NULL}, + /* We used not to clean up peers when their channels were gone. */ + {SQL("DELETE FROM peers WHERE id NOT IN (SELECT peer_id FROM channels);"), + NULL}, + /* The ONCHAIND_CHEATED/THEIR_UNILATERAL/OUR_UNILATERAL/MUTUAL are now one + */ + {SQL("UPDATE channels SET STATE = 8 WHERE state > 8;"), NULL}, + /* Add bolt11 to invoices table*/ + {SQL("ALTER TABLE invoices ADD bolt11 TEXT;"), NULL}, + /* What do we think the head of the blockchain looks like? Used + * primarily to track confirmations across restarts and making + * sure we handle reorgs correctly. */ + {SQL("CREATE TABLE blocks (height INT, hash BLOB, prev_hash BLOB, " + "UNIQUE(height));"), + NULL}, + /* ON DELETE CASCADE would have been nice for confirmation_height, + * so that we automatically delete outputs that fall off the + * blockchain and then we rediscover them if they are included + * again. However, we have the their_unilateral/to_us which we + * can't simply recognize from the chain without additional + * hints. So we just mark them as unconfirmed should the block + * die. */ + {SQL("ALTER TABLE outputs ADD COLUMN confirmation_height INTEGER " + "REFERENCES blocks(height) ON DELETE SET NULL;"), + NULL}, + {SQL("ALTER TABLE outputs ADD COLUMN spend_height INTEGER REFERENCES " + "blocks(height) ON DELETE SET NULL;"), + NULL}, + /* Create a covering index that covers both fields */ + {SQL("CREATE INDEX output_height_idx ON outputs (confirmation_height, " + "spend_height);"), + NULL}, + {SQL("CREATE TABLE utxoset (" + " txid BLOB," + " outnum INT," + " blockheight INT REFERENCES blocks(height) ON DELETE CASCADE," + " spendheight INT REFERENCES blocks(height) ON DELETE SET NULL," + " txindex INT," + " scriptpubkey BLOB," + " satoshis BIGINT," + " PRIMARY KEY(txid, outnum));"), + NULL}, + {SQL("CREATE INDEX short_channel_id ON utxoset (blockheight, txindex, " + "outnum)"), + NULL}, + /* Necessary index for long rollbacks of the blockchain, otherwise we're + * doing table scans for every block removed. */ + {SQL("CREATE INDEX utxoset_spend ON utxoset (spendheight)"), NULL}, + /* Assign key 0 to unassigned shutdown_keyidx_local. */ + {SQL("UPDATE channels SET shutdown_keyidx_local=0 WHERE " + "shutdown_keyidx_local = -1;"), + NULL}, + /* FIXME: We should rename shutdown_keyidx_local to final_key_index */ + /* -- Payment routing failure information -- */ + /* BLOB if failure was due to unparseable onion, NULL otherwise */ + {SQL("ALTER TABLE payments ADD failonionreply BLOB;"), NULL}, + /* 0 if we could theoretically retry, 1 if PERM fail at payee */ + {SQL("ALTER TABLE payments ADD faildestperm INTEGER;"), NULL}, + /* Contents of routing_failure (only if not unparseable onion) */ + {SQL("ALTER TABLE payments ADD failindex INTEGER;"), + NULL}, /* erring_index */ + {SQL("ALTER TABLE payments ADD failcode INTEGER;"), NULL}, /* failcode */ + {SQL("ALTER TABLE payments ADD failnode BLOB;"), NULL}, /* erring_node */ + {SQL("ALTER TABLE payments ADD failchannel BLOB;"), + NULL}, /* erring_channel */ + {SQL("ALTER TABLE payments ADD failupdate BLOB;"), + NULL}, /* channel_update - can be NULL*/ + /* -- Payment routing failure information ends -- */ + /* Delete route data for already succeeded or failed payments */ + {SQL("UPDATE payments" + " SET path_secrets = NULL" + " , route_nodes = NULL" + " , route_channels = NULL" + " WHERE status <> 0;"), + NULL}, /* PAYMENT_PENDING */ + /* -- Routing statistics -- */ + {SQL("ALTER TABLE channels ADD in_payments_offered INTEGER;"), NULL}, + {SQL("ALTER TABLE channels ADD in_payments_fulfilled INTEGER;"), NULL}, + {SQL("ALTER TABLE channels ADD in_msatoshi_offered INTEGER;"), NULL}, + {SQL("ALTER TABLE channels ADD in_msatoshi_fulfilled INTEGER;"), NULL}, + {SQL("ALTER TABLE channels ADD out_payments_offered INTEGER;"), NULL}, + {SQL("ALTER TABLE channels ADD out_payments_fulfilled INTEGER;"), NULL}, + {SQL("ALTER TABLE channels ADD out_msatoshi_offered INTEGER;"), NULL}, + {SQL("ALTER TABLE channels ADD out_msatoshi_fulfilled INTEGER;"), NULL}, + {SQL("UPDATE channels" + " SET in_payments_offered = 0, in_payments_fulfilled = 0" + " , in_msatoshi_offered = 0, in_msatoshi_fulfilled = 0" + " , out_payments_offered = 0, out_payments_fulfilled = 0" + " , out_msatoshi_offered = 0, out_msatoshi_fulfilled = 0" + " ;"), + NULL}, + /* -- Routing statistics ends --*/ + /* Record the msatoshi actually sent in a payment. */ + {SQL("ALTER TABLE payments ADD msatoshi_sent INTEGER;"), NULL}, + {SQL("UPDATE payments SET msatoshi_sent = msatoshi;"), NULL}, + /* Delete dangling utxoset entries due to Issue #1280 */ + {SQL("DELETE FROM utxoset WHERE blockheight IN (" + " SELECT DISTINCT(blockheight)" + " FROM utxoset LEFT OUTER JOIN blocks on (blockheight == " + "blocks.height) " + " WHERE blocks.hash IS NULL" + ");"), + NULL}, + /* Record feerate range, to optimize onchaind grinding actual fees. */ + {SQL("ALTER TABLE channels ADD min_possible_feerate INTEGER;"), NULL}, + {SQL("ALTER TABLE channels ADD max_possible_feerate INTEGER;"), NULL}, + /* https://bitcoinfees.github.io/#1d says Dec 17 peak was ~1M sat/kb + * which is 250,000 sat/Sipa */ + {SQL("UPDATE channels SET min_possible_feerate=0, " + "max_possible_feerate=250000;"), + NULL}, + /* -- Min and max msatoshi_to_us -- */ + {SQL("ALTER TABLE channels ADD msatoshi_to_us_min INTEGER;"), NULL}, + {SQL("ALTER TABLE channels ADD msatoshi_to_us_max INTEGER;"), NULL}, + {SQL("UPDATE channels" + " SET msatoshi_to_us_min = msatoshi_local" + " , msatoshi_to_us_max = msatoshi_local" + " ;"), + NULL}, + /* -- Min and max msatoshi_to_us ends -- */ + /* Transactions we are interested in. Either we sent them ourselves or we + * are watching them. We don't cascade block height deletes so we don't + * forget any of them by accident.*/ + {SQL("CREATE TABLE transactions (" + " id BLOB" + ", blockheight INTEGER REFERENCES blocks(height) ON DELETE SET NULL" + ", txindex INTEGER" + ", rawtx BLOB" + ", PRIMARY KEY (id)" + ");"), + NULL}, + /* -- Detailed payment failure -- */ + {SQL("ALTER TABLE payments ADD faildetail TEXT;"), NULL}, + {SQL("UPDATE payments" + " SET faildetail = 'unspecified payment failure reason'" + " WHERE status = 2;"), + NULL}, /* PAYMENT_FAILED */ + /* -- Detailed payment faiure ends -- */ + {SQL("CREATE TABLE channeltxs (" + /* The id serves as insertion order and short ID */ + " id INTEGER" + ", channel_id INTEGER REFERENCES channels(id) ON DELETE CASCADE" + ", type INTEGER" + ", transaction_id BLOB REFERENCES transactions(id) ON DELETE CASCADE" + /* The input_num is only used by the txo_watch, 0 if txwatch */ + ", input_num INTEGER" + /* The height at which we sent the depth notice */ + ", blockheight INTEGER REFERENCES blocks(height) ON DELETE CASCADE" + ", PRIMARY KEY(id)" + ");"), + NULL}, + /* -- Set the correct rescan height for PR #1398 -- */ + /* Delete blocks that are higher than our initial scan point, this is a + * no-op if we don't have a channel. */ + {SQL("DELETE FROM blocks WHERE height > (SELECT MIN(first_blocknum) FROM " + "channels);"), + NULL}, + /* Now make sure we have the lower bound block with the first_blocknum + * height. This may introduce a block with NULL height if we didn't have any + * blocks, remove that in the next. */ + {SQL("INSERT OR IGNORE INTO blocks (height) VALUES ((SELECT " + "MIN(first_blocknum) FROM channels));"), + NULL}, + {SQL("DELETE FROM blocks WHERE height IS NULL;"), NULL}, + /* -- End of PR #1398 -- */ + {SQL("ALTER TABLE invoices ADD description TEXT;"), NULL}, + /* FIXME: payments table 'description' is really a 'label' */ + {SQL("ALTER TABLE payments ADD description TEXT;"), NULL}, + /* future_per_commitment_point if other side proves we're out of date -- */ + {SQL("ALTER TABLE channels ADD future_per_commitment_point BLOB;"), NULL}, + /* last_sent_commit array fix */ + {SQL("ALTER TABLE channels ADD last_sent_commit BLOB;"), NULL}, + /* Stats table to track forwarded HTLCs. The values in the HTLCs + * and their states are replicated here and the entries are not + * deleted when the HTLC entries or the channel entries are + * deleted to avoid unexpected drops in statistics. */ + {SQL("CREATE TABLE forwarded_payments (" + " in_htlc_id INTEGER REFERENCES channel_htlcs(id) ON DELETE SET NULL" + ", out_htlc_id INTEGER REFERENCES channel_htlcs(id) ON DELETE SET NULL" + ", in_channel_scid INTEGER" + ", out_channel_scid INTEGER" + ", in_msatoshi INTEGER" + ", out_msatoshi INTEGER" + ", state INTEGER" + ", UNIQUE(in_htlc_id, out_htlc_id)" + ");"), + NULL}, + /* Add a direction for failed payments. */ + {SQL("ALTER TABLE payments ADD faildirection INTEGER;"), + NULL}, /* erring_direction */ + /* Fix dangling peers with no channels. */ + {SQL("DELETE FROM peers WHERE id NOT IN (SELECT peer_id FROM channels);"), + NULL}, + {SQL("ALTER TABLE outputs ADD scriptpubkey BLOB;"), NULL}, + /* Keep bolt11 string for payments. */ + {SQL("ALTER TABLE payments ADD bolt11 TEXT;"), NULL}, + /* PR #2342 feerate per channel */ + {SQL("ALTER TABLE channels ADD feerate_base INTEGER;"), NULL}, + {SQL("ALTER TABLE channels ADD feerate_ppm INTEGER;"), NULL}, + {NULL, migrate_pr2342_feerate_per_channel}, + {SQL("ALTER TABLE channel_htlcs ADD received_time INTEGER"), NULL}, + {SQL("ALTER TABLE forwarded_payments ADD received_time INTEGER"), NULL}, + {SQL("ALTER TABLE forwarded_payments ADD resolved_time INTEGER"), NULL}, + {SQL("ALTER TABLE channels ADD remote_upfront_shutdown_script BLOB;"), + NULL}, + /* PR #2524: Add failcode into forward_payment */ + {SQL("ALTER TABLE forwarded_payments ADD failcode INTEGER;"), NULL}, + /* remote signatures for channel announcement */ + {SQL("ALTER TABLE channels ADD remote_ann_node_sig BLOB;"), NULL}, + {SQL("ALTER TABLE channels ADD remote_ann_bitcoin_sig BLOB;"), NULL}, + /* Additional information for transaction tracking and listing */ + {SQL("ALTER TABLE transactions ADD type INTEGER;"), NULL}, + /* Not a foreign key on purpose since we still delete channels from + * the DB which would remove this. It is mainly used to group payments + * in the list view anyway, e.g., show all close and htlc transactions + * as a single bundle. */ + {SQL("ALTER TABLE transactions ADD channel_id INTEGER;"), NULL}, }; /* Leak tracking. */ #if DEVELOPER -/* We need a global here, since caller has no context. Yuck! */ -static struct list_head db_statements = LIST_HEAD_INIT(db_statements); - -struct db_statement { - struct list_node list; - sqlite3_stmt *stmt; - const char *origin; -}; - -static struct db_statement *find_statement(sqlite3_stmt *stmt) +static void db_assert_no_outstanding_statements(struct db *db) { - struct db_statement *i; + struct db_stmt *stmt; - list_for_each(&db_statements, i, list) { - if (i->stmt == stmt) - return i; - } - return NULL; + stmt = list_top(&db->pending_statements, struct db_stmt, list); + if (stmt) + db_fatal("Unfinalized statement %s", stmt->location); } - -void db_assert_no_outstanding_statements(void) +#else +static void db_assert_no_outstanding_statements(struct db *db) { - struct db_statement *dbstat; - - dbstat = list_top(&db_statements, struct db_statement, list); - if (dbstat) - db_fatal("Unfinalized statement %s", dbstat->origin); } +#endif -static void dev_statement_start(sqlite3_stmt *stmt, const char *origin) +static void db_stmt_free(struct db_stmt *stmt) { - struct db_statement *dbstat = tal(NULL, struct db_statement); - dbstat->stmt = stmt; - dbstat->origin = origin; - list_add(&db_statements, &dbstat->list); + if (stmt->inner_stmt) + stmt->db->config->stmt_free_fn(stmt); + assert(stmt->inner_stmt == NULL); } -static void dev_statement_end(sqlite3_stmt *stmt) +struct db_stmt *db_prepare_v2_(const char *location, struct db *db, + const char *query_id) { - struct db_statement *dbstat = find_statement(stmt); - list_del_from(&db_statements, &dbstat->list); - tal_free(dbstat); -} -#else -static void dev_statement_start(sqlite3_stmt *stmt, const char *origin) -{ -} + struct db_stmt *stmt = tal(db, struct db_stmt); + size_t num_slots; + stmt->query = NULL; + + /* Normalize query_id paths, because unit tests are compiled with this + * prefix. */ + if (strncmp(query_id, "./", 2) == 0) + query_id += 2; + + if (!db->in_transaction) + db_fatal("Attempting to prepare a db_stmt outside of a " + "transaction: %s", location); + + /* Look up the query by its ID */ + for (size_t i = 0; i < db->config->num_queries; i++) { + if (streq(query_id, db->config->queries[i].query)) { + stmt->query = &db->config->queries[i]; + break; + } + } + if (stmt->query == NULL) + fatal("Could not resolve query %s", query_id); -static void dev_statement_end(sqlite3_stmt *stmt) -{ -} + num_slots = stmt->query->placeholders; + /* Allocate the slots for placeholders/bindings, zeroed next since + * that sets the type to DB_BINDING_UNINITIALIZED for later checks. */ + stmt->bindings = tal_arr(stmt, struct db_binding, num_slots); + for (size_t i=0; ibindings[i].type = DB_BINDING_UNINITIALIZED; -void db_assert_no_outstanding_statements(void) -{ -} -#endif + stmt->location = location; + stmt->error = NULL; + stmt->db = db; + stmt->executed = false; + stmt->inner_stmt = NULL; -#if !HAVE_SQLITE3_EXPANDED_SQL -/* Prior to sqlite3 v3.14, we have to use tracing to dump statements */ -static void trace_sqlite3(void *dbv, const char *stmt) -{ - struct db *db = dbv; + tal_add_destructor(stmt, db_stmt_free); - /* We get a "COMMIT;" after we've sent our changes. */ - if (!db->changes) { - assert(streq(stmt, "COMMIT;")); - return; - } + list_add(&db->pending_statements, &stmt->list); - tal_arr_expand(&db->changes, tal_strdup(db->changes, stmt)); + return stmt; } -#endif -void db_stmt_done(sqlite3_stmt *stmt) +#define db_prepare_v2(db,query) \ + db_prepare_v2_(__FILE__ ":" stringify(__LINE__), db, query) + +bool db_step(struct db_stmt *stmt) { - dev_statement_end(stmt); - sqlite3_finalize(stmt); + assert(stmt->executed); + return stmt->db->config->step_fn(stmt); } -sqlite3_stmt *db_select_prepare_(const char *location, struct db *db, const char *query) +u64 db_column_u64(struct db_stmt *stmt, int col) { - int err; - sqlite3_stmt *stmt; - const char *full_query = tal_fmt(db, "SELECT %s", query); - - assert(db->in_transaction); - - err = sqlite3_prepare_v2(db->sql, full_query, -1, &stmt, NULL); - - if (err != SQLITE_OK) - db_fatal("%s: %s: %s", location, full_query, sqlite3_errmsg(db->sql)); - - dev_statement_start(stmt, location); - tal_free(full_query); - return stmt; + return stmt->db->config->column_u64_fn(stmt, col); } -bool db_select_step_(const char *location, struct db *db, struct sqlite3_stmt *stmt) +int db_column_int(struct db_stmt *stmt, int col) { - int ret; - - ret = sqlite3_step(stmt); - if (ret == SQLITE_ROW) - return true; - if (ret != SQLITE_DONE) - db_fatal("%s: %s", location, sqlite3_errmsg(db->sql)); - db_stmt_done(stmt); - return false; + return stmt->db->config->column_int_fn(stmt, col); } -sqlite3_stmt *db_prepare_(const char *location, struct db *db, const char *query) -{ - int err; - sqlite3_stmt *stmt; - - assert(db->in_transaction); - - err = sqlite3_prepare_v2(db->sql, query, -1, &stmt, NULL); - if (err != SQLITE_OK) - db_fatal("%s: %s: %s", location, query, sqlite3_errmsg(db->sql)); - - dev_statement_start(stmt, location); - return stmt; +size_t db_column_bytes(struct db_stmt *stmt, int col) +{ + return stmt->db->config->column_bytes_fn(stmt, col); } -void db_exec_prepared_(const char *caller, struct db *db, sqlite3_stmt *stmt) +int db_column_is_null(struct db_stmt *stmt, int col) { - assert(db->in_transaction); - - if (sqlite3_step(stmt) != SQLITE_DONE) - db_fatal("%s: %s", caller, sqlite3_errmsg(db->sql)); - -#if HAVE_SQLITE3_EXPANDED_SQL - char *expanded_sql; - expanded_sql = sqlite3_expanded_sql(stmt); - tal_arr_expand(&db->changes, - tal_strdup(db->changes, expanded_sql)); - sqlite3_free(expanded_sql); -#endif - db_stmt_done(stmt); + return stmt->db->config->column_is_null_fn(stmt, col); } -/* This one doesn't check if we're in a transaction. */ -static void db_do_exec(const char *caller, struct db *db, const char *cmd) +const void *db_column_blob(struct db_stmt *stmt, int col) { - char *errmsg; - int err; - - err = sqlite3_exec(db->sql, cmd, NULL, NULL, &errmsg); - if (err != SQLITE_OK) { - db_fatal("%s:%s:%s:%s", caller, sqlite3_errstr(err), cmd, errmsg); - /* Only reached in testing */ - sqlite3_free(errmsg); - } -#if HAVE_SQLITE3_EXPANDED_SQL - tal_arr_expand(&db->changes, tal_strdup(db->changes, cmd)); -#endif + return stmt->db->config->column_blob_fn(stmt, col); } -static void PRINTF_FMT(3, 4) - db_exec(const char *caller, struct db *db, const char *fmt, ...) +const unsigned char *db_column_text(struct db_stmt *stmt, int col) { - va_list ap; - char *cmd; - - assert(db->in_transaction); - - va_start(ap, fmt); - cmd = tal_vfmt(db, fmt, ap); - va_end(ap); - - db_do_exec(caller, db, cmd); - tal_free(cmd); + return stmt->db->config->column_blob_fn(stmt, col); } -/* This one can fail: returns NULL if so */ -static sqlite3_stmt *db_query(const char *location, - struct db *db, const char *query) +size_t db_count_changes(struct db_stmt *stmt) { - sqlite3_stmt *stmt; - - assert(db->in_transaction); - - /* Sets stmt to NULL if not SQLITE_OK */ - sqlite3_prepare_v2(db->sql, query, -1, &stmt, NULL); - if (stmt) - dev_statement_start(stmt, location); - return stmt; + return stmt->db->config->count_changes_fn(stmt); } -sqlite3_stmt *PRINTF_FMT(3, 4) - db_select_(const char *location, struct db *db, const char *fmt, ...) +u64 db_last_insert_id_v2(struct db_stmt *stmt TAKES) { - va_list ap; - char *query = tal_strdup(db, "SELECT "); - sqlite3_stmt *stmt; - - assert(db->in_transaction); - - va_start(ap, fmt); - tal_append_vfmt(&query, fmt, ap); - va_end(ap); + u64 id; + id = stmt->db->config->last_insert_id_fn(stmt); - stmt = db_query(location, db, query); - if (!stmt) - db_fatal("%s:%s:%s", location, query, sqlite3_errmsg(db->sql)); - tal_free(query); + if (taken(stmt)) + tal_free(stmt); - return stmt; + return id; } static void destroy_db(struct db *db) { - db_assert_no_outstanding_statements(); - sqlite3_close(db->sql); + db_assert_no_outstanding_statements(db); + + if (db->config->teardown_fn) + db->config->teardown_fn(db); } /* We expect min changes (ie. BEGIN TRANSACTION): report if more. @@ -631,49 +603,60 @@ static void db_prepare_for_changes(struct db *db) db->changes = tal_arr(db, const char *, 0); } +bool db_in_transaction(struct db *db) +{ + return db->in_transaction; +} + void db_begin_transaction_(struct db *db, const char *location) { + bool ok; if (db->in_transaction) db_fatal("Already in transaction from %s", db->in_transaction); db_prepare_for_changes(db); - db_do_exec(location, db, "BEGIN TRANSACTION;"); + ok = db->config->begin_tx_fn(db); + if (!ok) + db_fatal("Failed to start DB transaction: %s", db->error); + db->in_transaction = location; } void db_commit_transaction(struct db *db) { - int err; - char *errmsg; - const char *cmd = "COMMIT;"; - + bool ok; assert(db->in_transaction); - db_assert_no_outstanding_statements(); - - /* We expect at least the BEGIN TRANSACTION */ - db_report_changes(db, cmd, 1); + db_assert_no_outstanding_statements(db); + ok = db->config->commit_tx_fn(db); - err = sqlite3_exec(db->sql, cmd, NULL, NULL, &errmsg); - if (err != SQLITE_OK) - db_fatal("%s:%s:%s:%s", __func__, sqlite3_errstr(err), cmd, errmsg); + if (!ok) + db_fatal("Failed to commit DB transaction: %s", db->error); + db_report_changes(db, NULL, 0); db->in_transaction = NULL; } static void setup_open_db(struct db *db) { -#if !HAVE_SQLITE3_EXPANDED_SQL - sqlite3_trace(db->sql, trace_sqlite3, db); -#endif - /* This must be outside a transaction, so catch it */ assert(!db->in_transaction); db_prepare_for_changes(db); - db_do_exec(__func__, db, "PRAGMA foreign_keys = ON;"); + if (db->config->setup_fn) + db->config->setup_fn(db); db_report_changes(db, NULL, 0); } +static struct db_config *db_config_find(const char *driver_name) +{ + size_t num_configs; + struct db_config **configs = autodata_get(db_backends, &num_configs); + for (size_t i=0; iname)) + return configs[i]; + return NULL; +} + /** * db_open - Open or create a sqlite3 database */ @@ -682,6 +665,7 @@ static struct db *db_open(const tal_t *ctx, char *filename) int err; struct db *db; sqlite3 *sql; + const char *driver_name = "sqlite3"; int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; err = sqlite3_open_v2(filename, &sql, flags, NULL); @@ -694,6 +678,17 @@ static struct db *db_open(const tal_t *ctx, char *filename) db = tal(ctx, struct db); db->filename = tal_strdup(db, filename); db->sql = sql; + db->config = NULL; + list_head_init(&db->pending_statements); + + db->config = db_config_find(driver_name); + if (!db->config) + db_fatal("Unable to find DB driver for %s", driver_name); + + // FIXME(cdecker) Once we parse DB connection strings this needs to be + // instantiated correctly. + db->conn = sql; + tal_add_destructor(db, destroy_db); db->in_transaction = NULL; db->changes = NULL; @@ -713,18 +708,17 @@ static struct db *db_open(const tal_t *ctx, char *filename) */ static int db_get_version(struct db *db) { - int res; - sqlite3_stmt *stmt = db_query(__func__, - db, "SELECT version FROM version LIMIT 1"); - - if (!stmt) - return -1; + int res = -1; + struct db_stmt *stmt = db_prepare_v2(db, SQL("SELECT version FROM version LIMIT 1")); + if (!db_query_prepared(stmt)) { + tal_free(stmt); + return res; + } - if (!db_select_step(db, stmt)) - return -1; + if (db_step(stmt)) + res = db_column_u64(stmt, 0); - res = sqlite3_column_int64(stmt, 0); - db_stmt_done(stmt); + tal_free(stmt); return res; } @@ -735,6 +729,7 @@ static void db_migrate(struct lightningd *ld, struct db *db, struct log *log) { /* Attempt to read the version from the database */ int current, orig, available; + struct db_stmt *stmt; db_begin_transaction(db); @@ -752,20 +747,31 @@ static void db_migrate(struct lightningd *ld, struct db *db, struct log *log) while (current < available) { current++; - if (dbmigrations[current].sql) - db_exec(__func__, db, "%s", dbmigrations[current].sql); + if (dbmigrations[current].sql) { + struct db_stmt *stmt = + db_prepare_v2(db, dbmigrations[current].sql); + db_exec_prepared_v2(stmt); + tal_free(stmt); + } if (dbmigrations[current].func) dbmigrations[current].func(ld, db); } /* Finally update the version number in the version table */ - db_exec(__func__, db, "UPDATE version SET version=%d;", available); + stmt = db_prepare_v2(db, SQL("UPDATE version SET version=?;")); + db_bind_u64(stmt, 0, available); + db_exec_prepared_v2(stmt); + tal_free(stmt); /* Annotate that we did upgrade, if any. */ - if (current != orig) - db_exec(__func__, db, - "INSERT INTO db_upgrades VALUES (%i, '%s');", - orig, version()); + if (current != orig) { + stmt = db_prepare_v2( + db, SQL("INSERT INTO db_upgrades VALUES (?, ?);")); + db_bind_u64(stmt, 0, orig); + db_bind_text(stmt, 1, version()); + db_exec_prepared_v2(stmt); + tal_free(stmt); + } db_commit_transaction(db); } @@ -780,272 +786,243 @@ struct db *db_setup(const tal_t *ctx, struct lightningd *ld, struct log *log) s64 db_get_intvar(struct db *db, char *varname, s64 defval) { - s64 res; - sqlite3_stmt *stmt; - const char *query; - - query = tal_fmt(db, "SELECT val FROM vars WHERE name='%s' LIMIT 1", varname); - stmt = db_query(__func__, db, query); - tal_free(query); - - if (!stmt) - return defval; - - if (db_select_step(db, stmt)) { - const unsigned char *stringvar = sqlite3_column_text(stmt, 0); - res = atol((const char *)stringvar); - db_stmt_done(stmt); - } else - res = defval; - + s64 res = defval; + struct db_stmt *stmt = db_prepare_v2( + db, SQL("SELECT val FROM vars WHERE name= ? LIMIT 1")); + db_bind_text(stmt, 0, varname); + if (!db_query_prepared(stmt)) + goto done; + + if (db_step(stmt)) + res = atol((const char*)db_column_text(stmt, 0)); + +done: + tal_free(stmt); return res; } void db_set_intvar(struct db *db, char *varname, s64 val) { - /* Attempt to update */ - db_exec(__func__, db, - "UPDATE vars SET val='%" PRId64 "' WHERE name='%s';", val, - varname); - if (sqlite3_changes(db->sql) == 0) - db_exec( - __func__, db, - "INSERT INTO vars (name, val) VALUES ('%s', '%" PRId64 - "');", - varname, val); + char *v = tal_fmt(NULL, "%"PRIi64, val); + size_t changes; + struct db_stmt *stmt = db_prepare_v2(db, SQL("UPDATE vars SET val=? WHERE name=?;")); + db_bind_text(stmt, 0, v); + db_bind_text(stmt, 1, varname); + if (!db_exec_prepared_v2(stmt)) + db_fatal("Error executing update: %s", stmt->error); + changes = db_count_changes(stmt); + tal_free(stmt); + + if (changes == 0) { + stmt = db_prepare_v2(db, SQL("INSERT INTO vars (name, val) VALUES (?, ?);")); + db_bind_text(stmt, 0, varname); + db_bind_text(stmt, 1, v); + if (!db_exec_prepared_v2(stmt)) + db_fatal("Error executing insert: %s", stmt->error); + tal_free(stmt); + } + tal_free(v); } -void *sqlite3_column_arr_(const tal_t *ctx, sqlite3_stmt *stmt, int col, - size_t bytes, const char *label, const char *caller) +/* Will apply the current config fee settings to all channels */ +void migrate_pr2342_feerate_per_channel(struct lightningd *ld, struct db *db) { - size_t sourcelen = sqlite3_column_bytes(stmt, col); - void *p; + struct db_stmt *stmt = db_prepare_v2( + db, SQL("UPDATE channels SET feerate_base = ?, feerate_ppm = ?;")); - if (sqlite3_column_type(stmt, col) == SQLITE_NULL) - return NULL; + db_bind_int(stmt, 0, ld->config.fee_base); + db_bind_int(stmt, 1, ld->config.fee_per_satoshi); - if (sourcelen % bytes != 0) - db_fatal("%s: column size %zu not a multiple of %s (%zu)", - caller, sourcelen, label, bytes); - - p = tal_arr_label(ctx, char, sourcelen, label); - memcpy(p, sqlite3_column_blob(stmt, col), sourcelen); - return p; + db_exec_prepared_v2(stmt); + tal_free(stmt); } -bool sqlite3_bind_short_channel_id(sqlite3_stmt *stmt, int col, - const struct short_channel_id *id) +void db_bind_null(struct db_stmt *stmt, int pos) { - char *ser = short_channel_id_to_str(id, id); - int err = sqlite3_bind_blob(stmt, col, ser, strlen(ser), SQLITE_TRANSIENT); - tal_free(ser); - return err == SQLITE_OK; + assert(pos < tal_count(stmt->bindings)); + stmt->bindings[pos].type = DB_BINDING_NULL; } -bool sqlite3_column_short_channel_id(sqlite3_stmt *stmt, int col, - struct short_channel_id *dest) +void db_bind_int(struct db_stmt *stmt, int pos, int val) { - const char *source = sqlite3_column_blob(stmt, col); - size_t sourcelen = sqlite3_column_bytes(stmt, col); - return short_channel_id_from_str(source, sourcelen, dest, true); + assert(pos < tal_count(stmt->bindings)); + stmt->bindings[pos].type = DB_BINDING_INT; + stmt->bindings[pos].v.i = val; } -bool sqlite3_bind_short_channel_id_array(sqlite3_stmt *stmt, int col, - const struct short_channel_id *id) -{ - u8 *ser; - size_t num; - size_t i; - - /* Handle nulls early. */ - if (!id) { - int err = sqlite3_bind_null(stmt, col); - return err == SQLITE_OK; - } - - ser = tal_arr(NULL, u8, 0); - num = tal_count(id); - - for (i = 0; i < num; ++i) - towire_short_channel_id(&ser, &id[i]); - int err = sqlite3_bind_blob(stmt, col, ser, tal_count(ser), SQLITE_TRANSIENT); - - tal_free(ser); - return err == SQLITE_OK; -} -struct short_channel_id * -sqlite3_column_short_channel_id_array(const tal_t *ctx, - sqlite3_stmt *stmt, int col) +void db_bind_u64(struct db_stmt *stmt, int pos, u64 val) { - const u8 *ser; - size_t len; - struct short_channel_id *ret; - - /* Handle nulls early. */ - if (sqlite3_column_type(stmt, col) == SQLITE_NULL) - return NULL; - - ser = sqlite3_column_blob(stmt, col); - len = sqlite3_column_bytes(stmt, col); - ret = tal_arr(ctx, struct short_channel_id, 0); - - while (len != 0) { - struct short_channel_id scid; - fromwire_short_channel_id(&ser, &len, &scid); - tal_arr_expand(&ret, scid); - } + assert(pos < tal_count(stmt->bindings)); + stmt->bindings[pos].type = DB_BINDING_UINT64; + stmt->bindings[pos].v.u64 = val; +} - return ret; +void db_bind_blob(struct db_stmt *stmt, int pos, const u8 *val, size_t len) +{ + assert(pos < tal_count(stmt->bindings)); + stmt->bindings[pos].type = DB_BINDING_BLOB; + stmt->bindings[pos].v.blob = val; + stmt->bindings[pos].len = len; } -bool sqlite3_bind_tx(sqlite3_stmt *stmt, int col, const struct bitcoin_tx *tx) +void db_bind_text(struct db_stmt *stmt, int pos, const char *val) { - u8 *ser = linearize_tx(NULL, tx); - int err = sqlite3_bind_blob(stmt, col, ser, tal_count(ser), SQLITE_TRANSIENT); - tal_free(ser); - return err == SQLITE_OK; + assert(pos < tal_count(stmt->bindings)); + stmt->bindings[pos].type = DB_BINDING_TEXT; + stmt->bindings[pos].v.text = val; + stmt->bindings[pos].len = strlen(val); } -struct bitcoin_tx *sqlite3_column_tx(const tal_t *ctx, sqlite3_stmt *stmt, - int col) +void db_bind_preimage(struct db_stmt *stmt, int pos, const struct preimage *p) { - const u8 *src = sqlite3_column_blob(stmt, col); - size_t len = sqlite3_column_bytes(stmt, col); - return pull_bitcoin_tx(ctx, &src, &len); + db_bind_blob(stmt, pos, p->r, sizeof(struct preimage)); } -bool sqlite3_bind_signature(sqlite3_stmt *stmt, int col, - const secp256k1_ecdsa_signature *sig) + +void db_bind_sha256(struct db_stmt *stmt, int pos, const struct sha256 *s) { - bool ok; - u8 buf[64]; - ok = secp256k1_ecdsa_signature_serialize_compact(secp256k1_ctx, buf, - sig) == 1; - int err = sqlite3_bind_blob(stmt, col, buf, sizeof(buf), SQLITE_TRANSIENT); - return ok && err == SQLITE_OK; + db_bind_blob(stmt, pos, s->u.u8, sizeof(struct sha256)); } -bool sqlite3_column_signature(sqlite3_stmt *stmt, int col, - secp256k1_ecdsa_signature *sig) +void db_bind_sha256d(struct db_stmt *stmt, int pos, const struct sha256_double *s) { - assert(sqlite3_column_bytes(stmt, col) == 64); - return secp256k1_ecdsa_signature_parse_compact( - secp256k1_ctx, sig, sqlite3_column_blob(stmt, col)) == 1; + db_bind_sha256(stmt, pos, &s->sha); } -bool sqlite3_column_pubkey(sqlite3_stmt *stmt, int col, struct pubkey *dest) +void db_bind_secret(struct db_stmt *stmt, int pos, const struct secret *s) { - assert(sqlite3_column_bytes(stmt, col) == PUBKEY_CMPR_LEN); - return pubkey_from_der(sqlite3_column_blob(stmt, col), PUBKEY_CMPR_LEN, dest); + assert(sizeof(s->data) == 32); + db_bind_blob(stmt, pos, s->data, sizeof(s->data)); } -bool sqlite3_bind_pubkey(sqlite3_stmt *stmt, int col, const struct pubkey *pk) +void db_bind_secret_arr(struct db_stmt *stmt, int col, const struct secret *s) { - u8 der[PUBKEY_CMPR_LEN]; - pubkey_to_der(der, pk); - int err = sqlite3_bind_blob(stmt, col, der, sizeof(der), SQLITE_TRANSIENT); - return err == SQLITE_OK; + size_t num = tal_count(s), elsize = sizeof(s->data); + u8 *ser = tal_arr(stmt, u8, num * elsize); + + for (size_t i = 0; i < num; ++i) + memcpy(ser + i * elsize, &s[i], elsize); + + db_bind_blob(stmt, col, ser, tal_count(ser)); } -bool sqlite3_column_node_id(sqlite3_stmt *stmt, int col, struct node_id *dest) +void db_bind_txid(struct db_stmt *stmt, int pos, const struct bitcoin_txid *t) { - assert(sqlite3_column_bytes(stmt, col) == sizeof(dest->k)); - memcpy(dest->k, sqlite3_column_blob(stmt, col), sizeof(dest->k)); - return node_id_valid(dest); + db_bind_sha256d(stmt, pos, &t->shad); } -bool sqlite3_bind_node_id(sqlite3_stmt *stmt, int col, const struct node_id *id) +void db_bind_node_id(struct db_stmt *stmt, int pos, const struct node_id *id) { - assert(node_id_valid(id)); - int err = sqlite3_bind_blob(stmt, col, id->k, sizeof(id->k), SQLITE_TRANSIENT); - return err == SQLITE_OK; + db_bind_blob(stmt, pos, id->k, sizeof(id->k)); } -bool sqlite3_bind_pubkey_array(sqlite3_stmt *stmt, int col, - const struct pubkey *pks) +void db_bind_node_id_arr(struct db_stmt *stmt, int col, + const struct node_id *ids) { - size_t n; - size_t i; - u8 *ders; + /* Copy into contiguous array: ARM will add padding to struct node_id! */ + size_t n = tal_count(ids); + u8 *arr = tal_arr(stmt, u8, n * sizeof(ids[0].k)); - if (!pks) { - int err = sqlite3_bind_null(stmt, col); - return err == SQLITE_OK; + for (size_t i = 0; i < n; ++i) { + assert(node_id_valid(&ids[i])); + memcpy(arr + sizeof(ids[i].k) * i, + ids[i].k, + sizeof(ids[i].k)); } + db_bind_blob(stmt, col, arr, tal_count(arr)); +} - n = tal_count(pks); - ders = tal_arr(NULL, u8, n * PUBKEY_CMPR_LEN); - - for (i = 0; i < n; ++i) - pubkey_to_der(&ders[i * PUBKEY_CMPR_LEN], &pks[i]); - int err = sqlite3_bind_blob(stmt, col, ders, tal_count(ders), SQLITE_TRANSIENT); +void db_bind_pubkey(struct db_stmt *stmt, int pos, const struct pubkey *pk) +{ + u8 *der = tal_arr(stmt, u8, PUBKEY_CMPR_LEN); + pubkey_to_der(der, pk); + db_bind_blob(stmt, pos, der, PUBKEY_CMPR_LEN); +} - tal_free(ders); - return err == SQLITE_OK; +void db_bind_short_channel_id(struct db_stmt *stmt, int col, + const struct short_channel_id *id) +{ + char *ser = short_channel_id_to_str(stmt, id); + db_bind_text(stmt, col, ser); } -struct pubkey *sqlite3_column_pubkey_array(const tal_t *ctx, - sqlite3_stmt *stmt, int col) + +void db_bind_short_channel_id_arr(struct db_stmt *stmt, int col, + const struct short_channel_id *id) { - size_t i; - size_t n; - struct pubkey *ret; - const u8 *ders; + u8 *ser = tal_arr(stmt, u8, 0); + size_t num = tal_count(id); - if (sqlite3_column_type(stmt, col) == SQLITE_NULL) - return NULL; + for (size_t i = 0; i < num; ++i) + towire_short_channel_id(&ser, &id[i]); - n = sqlite3_column_bytes(stmt, col) / PUBKEY_CMPR_LEN; - assert(n * PUBKEY_CMPR_LEN == (size_t)sqlite3_column_bytes(stmt, col)); - ret = tal_arr(ctx, struct pubkey, n); - ders = sqlite3_column_blob(stmt, col); + db_bind_blob(stmt, col, ser, tal_count(ser)); +} - for (i = 0; i < n; ++i) { - if (!pubkey_from_der(&ders[i * PUBKEY_CMPR_LEN], PUBKEY_CMPR_LEN, &ret[i])) - return tal_free(ret); - } +void db_bind_signature(struct db_stmt *stmt, int col, + const secp256k1_ecdsa_signature *sig) +{ + u8 *buf = tal_arr(stmt, u8, 64); + int ret = secp256k1_ecdsa_signature_serialize_compact(secp256k1_ctx, + buf, sig); + assert(ret == 1); + db_bind_blob(stmt, col, buf, 64); +} - return ret; +void db_bind_timeabs(struct db_stmt *stmt, int col, struct timeabs t) +{ + u64 timestamp = t.ts.tv_nsec + (((u64) t.ts.tv_sec) * ((u64) NSEC_IN_SEC)); + db_bind_u64(stmt, col, timestamp); } -bool sqlite3_bind_node_id_array(sqlite3_stmt *stmt, int col, - const struct node_id *ids) +void db_bind_tx(struct db_stmt *stmt, int col, const struct bitcoin_tx *tx) { - size_t n; - u8 *arr; + u8 *ser = linearize_tx(stmt, tx); + assert(ser); + db_bind_blob(stmt, col, ser, tal_count(ser)); +} - if (!ids) { - int err = sqlite3_bind_null(stmt, col); - return err == SQLITE_OK; - } +void db_bind_amount_msat(struct db_stmt *stmt, int pos, + const struct amount_msat *msat) +{ + db_bind_u64(stmt, pos, msat->millisatoshis); /* Raw: low level function */ +} - /* Copy into contiguous array: ARM will add padding to struct node_id! */ - n = tal_count(ids); - arr = tal_arr(NULL, u8, n * sizeof(ids[0].k)); - for (size_t i = 0; i < n; ++i) { - assert(node_id_valid(&ids[i])); - memcpy(arr + sizeof(ids[i].k) * i, - ids[i].k, - sizeof(ids[i].k)); - } - int err = sqlite3_bind_blob(stmt, col, arr, tal_count(arr), SQLITE_TRANSIENT); +void db_bind_amount_sat(struct db_stmt *stmt, int pos, + const struct amount_sat *sat) +{ + db_bind_u64(stmt, pos, sat->satoshis); /* Raw: low level function */ +} - tal_free(arr); - return err == SQLITE_OK; +void db_bind_json_escape(struct db_stmt *stmt, int pos, + const struct json_escape *esc) +{ + db_bind_text(stmt, pos, esc->s); } -struct node_id *sqlite3_column_node_id_array(const tal_t *ctx, - sqlite3_stmt *stmt, int col) +void db_column_preimage(struct db_stmt *stmt, int col, + struct preimage *preimage) { - size_t n; - struct node_id *ret; - const u8 *arr; + const u8 *raw; + size_t size = sizeof(struct preimage); + assert(db_column_bytes(stmt, col) == size); + raw = db_column_blob(stmt, col); + memcpy(preimage, raw, size); +} - if (sqlite3_column_type(stmt, col) == SQLITE_NULL) - return NULL; +void db_column_node_id(struct db_stmt *stmt, int col, struct node_id *dest) +{ + assert(db_column_bytes(stmt, col) == sizeof(dest->k)); + memcpy(dest->k, db_column_blob(stmt, col), sizeof(dest->k)); + assert(node_id_valid(dest)); +} - n = sqlite3_column_bytes(stmt, col) / sizeof(ret->k); - assert(n * sizeof(ret->k) == (size_t)sqlite3_column_bytes(stmt, col)); +struct node_id *db_column_node_id_arr(const tal_t *ctx, struct db_stmt *stmt, + int col) +{ + struct node_id *ret; + size_t n = db_column_bytes(stmt, col) / sizeof(ret->k); + const u8 *arr = db_column_blob(stmt, col); + assert(n * sizeof(ret->k) == (size_t)db_column_bytes(stmt, col)); ret = tal_arr(ctx, struct node_id, n); - arr = sqlite3_column_blob(stmt, col); for (size_t i = 0; i < n; i++) { memcpy(ret[i].k, arr + i * sizeof(ret[i].k), sizeof(ret[i].k)); @@ -1056,112 +1033,185 @@ struct node_id *sqlite3_column_node_id_array(const tal_t *ctx, return ret; } -bool sqlite3_column_preimage(sqlite3_stmt *stmt, int col, struct preimage *dest) +void db_column_pubkey(struct db_stmt *stmt, int pos, struct pubkey *dest) { - assert(sqlite3_column_bytes(stmt, col) == sizeof(struct preimage)); - return memcpy(dest, sqlite3_column_blob(stmt, col), sizeof(struct preimage)); + bool ok; + assert(db_column_bytes(stmt, pos) == PUBKEY_CMPR_LEN); + ok = pubkey_from_der(db_column_blob(stmt, pos), PUBKEY_CMPR_LEN, dest); + assert(ok); } -bool sqlite3_bind_preimage(sqlite3_stmt *stmt, int col, const struct preimage *p) +bool db_column_short_channel_id(struct db_stmt *stmt, int col, + struct short_channel_id *dest) { - int err = sqlite3_bind_blob(stmt, col, p, sizeof(struct preimage), SQLITE_TRANSIENT); - return err == SQLITE_OK; + const char *source = db_column_blob(stmt, col); + size_t sourcelen = db_column_bytes(stmt, col); + return short_channel_id_from_str(source, sourcelen, dest, true); } -bool sqlite3_column_sha256(sqlite3_stmt *stmt, int col, struct sha256 *dest) +struct short_channel_id * +db_column_short_channel_id_arr(const tal_t *ctx, struct db_stmt *stmt, int col) { - assert(sqlite3_column_bytes(stmt, col) == sizeof(struct sha256)); - return memcpy(dest, sqlite3_column_blob(stmt, col), sizeof(struct sha256)); + const u8 *ser; + size_t len; + struct short_channel_id *ret; + + ser = db_column_blob(stmt, col); + len = db_column_bytes(stmt, col); + ret = tal_arr(ctx, struct short_channel_id, 0); + + while (len != 0) { + struct short_channel_id scid; + fromwire_short_channel_id(&ser, &len, &scid); + tal_arr_expand(&ret, scid); + } + + return ret; } -bool sqlite3_bind_sha256(sqlite3_stmt *stmt, int col, const struct sha256 *p) +bool db_column_signature(struct db_stmt *stmt, int col, + secp256k1_ecdsa_signature *sig) { - int err = sqlite3_bind_blob(stmt, col, p, sizeof(struct sha256), SQLITE_TRANSIENT); - return err == SQLITE_OK; + assert(db_column_bytes(stmt, col) == 64); + return secp256k1_ecdsa_signature_parse_compact( + secp256k1_ctx, sig, db_column_blob(stmt, col)) == 1; } -bool sqlite3_column_sha256_double(sqlite3_stmt *stmt, int col, struct sha256_double *dest) +struct timeabs db_column_timeabs(struct db_stmt *stmt, int col) { - assert(sqlite3_column_bytes(stmt, col) == sizeof(struct sha256_double)); - return memcpy(dest, sqlite3_column_blob(stmt, col), sizeof(struct sha256_double)); + struct timeabs t; + u64 timestamp = db_column_u64(stmt, col); + t.ts.tv_sec = timestamp / NSEC_IN_SEC; + t.ts.tv_nsec = timestamp % NSEC_IN_SEC; + return t; + } -struct secret *sqlite3_column_secrets(const tal_t *ctx, - sqlite3_stmt *stmt, int col) +struct bitcoin_tx *db_column_tx(const tal_t *ctx, struct db_stmt *stmt, int col) { - return sqlite3_column_arr(ctx, stmt, col, struct secret); + const u8 *src = db_column_blob(stmt, col); + size_t len = db_column_bytes(stmt, col); + return pull_bitcoin_tx(ctx, &src, &len); } -bool sqlite3_bind_sha256_double(sqlite3_stmt *stmt, int col, const struct sha256_double *p) +void *db_column_arr_(const tal_t *ctx, struct db_stmt *stmt, int col, + size_t bytes, const char *label, const char *caller) { - int err = sqlite3_bind_blob(stmt, col, p, sizeof(struct sha256_double), SQLITE_TRANSIENT); - return err == SQLITE_OK; + size_t sourcelen = db_column_bytes(stmt, col); + void *p; + + if (db_column_is_null(stmt, col)) + return NULL; + + if (sourcelen % bytes != 0) + db_fatal("%s: column size %zu not a multiple of %s (%zu)", + caller, sourcelen, label, bytes); + + p = tal_arr_label(ctx, char, sourcelen, label); + memcpy(p, db_column_blob(stmt, col), sourcelen); + return p; } -struct json_escape *sqlite3_column_json_escape(const tal_t *ctx, - sqlite3_stmt *stmt, int col) +void db_column_amount_msat(struct db_stmt *stmt, int col, + struct amount_msat *msat) { - return json_escape_string_(ctx, - sqlite3_column_blob(stmt, col), - sqlite3_column_bytes(stmt, col)); + msat->millisatoshis = db_column_u64(stmt, col); /* Raw: low level function */ } -bool sqlite3_bind_json_escape(sqlite3_stmt *stmt, int col, - const struct json_escape *esc) +void db_column_amount_sat(struct db_stmt *stmt, int col, struct amount_sat *sat) { - int err = sqlite3_bind_text(stmt, col, esc->s, strlen(esc->s), SQLITE_TRANSIENT); - return err == SQLITE_OK; + sat->satoshis = db_column_u64(stmt, col); /* Raw: low level function */ } -struct amount_msat sqlite3_column_amount_msat(sqlite3_stmt *stmt, int col) +struct json_escape *db_column_json_escape(const tal_t *ctx, + struct db_stmt *stmt, int col) { - struct amount_msat msat; + return json_escape_string_(ctx, db_column_blob(stmt, col), + db_column_bytes(stmt, col)); +} - msat.millisatoshis = sqlite3_column_int64(stmt, col); /* Raw: low level function */ - return msat; +void db_column_sha256(struct db_stmt *stmt, int col, struct sha256 *sha) +{ + const u8 *raw; + size_t size = sizeof(struct sha256); + assert(db_column_bytes(stmt, col) == size); + raw = db_column_blob(stmt, col); + memcpy(sha, raw, size); } -struct amount_sat sqlite3_column_amount_sat(sqlite3_stmt *stmt, int col) +void db_column_sha256d(struct db_stmt *stmt, int col, + struct sha256_double *shad) { - struct amount_sat sat; + const u8 *raw; + size_t size = sizeof(struct sha256_double); + assert(db_column_bytes(stmt, col) == size); + raw = db_column_blob(stmt, col); + memcpy(shad, raw, size); +} - sat.satoshis = sqlite3_column_int64(stmt, col); /* Raw: low level function */ - return sat; +void db_column_secret(struct db_stmt *stmt, int col, struct secret *s) +{ + const u8 *raw; + assert(db_column_bytes(stmt, col) == sizeof(struct secret)); + raw = db_column_blob(stmt, col); + memcpy(s, raw, sizeof(struct secret)); } -void sqlite3_bind_amount_msat(sqlite3_stmt *stmt, int col, - struct amount_msat msat) +struct secret *db_column_secret_arr(const tal_t *ctx, struct db_stmt *stmt, + int col) { - sqlite3_bind_int64(stmt, col, msat.millisatoshis); /* Raw: low level function */ + return db_column_arr(ctx, stmt, col, struct secret); } -void sqlite3_bind_amount_sat(sqlite3_stmt *stmt, int col, - struct amount_sat sat) +void db_column_txid(struct db_stmt *stmt, int pos, struct bitcoin_txid *t) { - sqlite3_bind_int64(stmt, col, sat.satoshis); /* Raw: low level function */ + db_column_sha256d(stmt, pos, &t->shad); } -/* Will apply the current config fee settings to all channels */ -void migrate_pr2342_feerate_per_channel(struct lightningd *ld, struct db *db) +bool db_exec_prepared_v2(struct db_stmt *stmt TAKES) { - db_exec(__func__, db, - "UPDATE channels SET feerate_base = %u, feerate_ppm = %u;", - ld->config.fee_base, - ld->config.fee_per_satoshi); + bool ret = stmt->db->config->exec_fn(stmt); + stmt->executed = true; + list_del_from(&stmt->db->pending_statements, &stmt->list); + + /* The driver itself doesn't call `fatal` since we want to override it + * for testing. Instead we check here that the error message is set if + * we report an error. */ + if (!ret) { + assert(stmt->error); + db_fatal("Error executing statement: %s", stmt->error); + } + + if (taken(stmt)) + tal_free(stmt); + + return ret; } -void sqlite3_bind_timeabs(sqlite3_stmt *stmt, int col, struct timeabs t) +bool db_query_prepared(struct db_stmt *stmt) { - u64 timestamp = t.ts.tv_nsec + (((u64) t.ts.tv_sec) * ((u64) NSEC_IN_SEC)); - sqlite3_bind_int64(stmt, col, timestamp); + /* Make sure we don't accidentally execute a modifying query using a + * read-only path. */ + bool ret; + assert(stmt->query->readonly); + ret = stmt->db->config->query_fn(stmt); + stmt->executed = true; + list_del_from(&stmt->db->pending_statements, &stmt->list); + return ret; } -struct timeabs sqlite3_column_timeabs(sqlite3_stmt *stmt, int col) +void db_changes_add(struct db_stmt *stmt, const char * expanded) { - struct timeabs t; - u64 timestamp = sqlite3_column_int64(stmt, col); - t.ts.tv_sec = timestamp / NSEC_IN_SEC; - t.ts.tv_nsec = timestamp % NSEC_IN_SEC; - return t; + struct db *db = stmt->db; + + if (stmt->query->readonly) { + return; + } + /* We get a "COMMIT;" after we've sent our changes. */ + if (!db->changes) { + assert(streq(expanded, "COMMIT;")); + return; + } + tal_arr_expand(&db->changes, tal_strdup(db->changes, expanded)); } diff --git a/wallet/db.h b/wallet/db.h index 52260c5be1b7..2444c17723d4 100644 --- a/wallet/db.h +++ b/wallet/db.h @@ -6,24 +6,42 @@ #include #include #include +#include +#include #include #include #include #include #include -#include #include struct lightningd; struct log; struct node_id; +struct db_stmt; +struct db; + +/** + * Macro to annotate a named SQL query. + * + * This macro is used to annotate SQL queries that might need rewriting for + * different SQL dialects. It is used both as a marker for the query + * extraction logic in devtools/sql-rewrite.py to identify queries, as well as + * a way to swap out the query text with it's name so that the query execution + * engine can then look up the rewritten query using its name. + * + */ +#define NAMED_SQL(name,x) x + +/** + * Simple annotation macro that auto-generates names for NAMED_SQL + * + * If this macro is changed it is likely that the extraction logic in + * devtools/sql-rewrite.py needs to change as well, since they need to + * generate identical names to work correctly. + */ +#define SQL(x) NAMED_SQL( __FILE__ ":" stringify(__LINE__) ":" stringify(__COUNTER__), x) -struct db { - char *filename; - const char *in_transaction; - sqlite3 *sql; - const char **changes; -}; /** * db_setup - Open a the lightningd database and update the schema @@ -39,16 +57,6 @@ struct db { */ struct db *db_setup(const tal_t *ctx, struct lightningd *ld, struct log *log); -/** - * db_select - Prepare and execute a SELECT, and return the result - * - * A simpler version of db_select_prepare. - */ -sqlite3_stmt *PRINTF_FMT(3, 4) - db_select_(const char *location, struct db *db, const char *fmt, ...); -#define db_select(db, ...) \ - db_select_(__FILE__ ":" stringify(__LINE__), db, __VA_ARGS__) - /** * db_begin_transaction - Begin a transaction * @@ -58,6 +66,8 @@ sqlite3_stmt *PRINTF_FMT(3, 4) db_begin_transaction_((db), __FILE__ ":" stringify(__LINE__)) void db_begin_transaction_(struct db *db, const char *location); +bool db_in_transaction(struct db *db); + /** * db_commit_transaction - Commit a running transaction * @@ -82,136 +92,130 @@ void db_set_intvar(struct db *db, char *varname, s64 val); */ s64 db_get_intvar(struct db *db, char *varname, s64 defval); +void db_bind_null(struct db_stmt *stmt, int pos); +void db_bind_int(struct db_stmt *stmt, int pos, int val); +void db_bind_u64(struct db_stmt *stmt, int pos, u64 val); +void db_bind_blob(struct db_stmt *stmt, int pos, const u8 *val, size_t len); +void db_bind_text(struct db_stmt *stmt, int pos, const char *val); +void db_bind_preimage(struct db_stmt *stmt, int pos, const struct preimage *p); +void db_bind_sha256(struct db_stmt *stmt, int pos, const struct sha256 *s); +void db_bind_sha256d(struct db_stmt *stmt, int pos, const struct sha256_double *s); +void db_bind_secret(struct db_stmt *stmt, int pos, const struct secret *s); +void db_bind_secret_arr(struct db_stmt *stmt, int col, const struct secret *s); +void db_bind_txid(struct db_stmt *stmt, int pos, const struct bitcoin_txid *t); +void db_bind_node_id(struct db_stmt *stmt, int pos, const struct node_id *ni); +void db_bind_node_id_arr(struct db_stmt *stmt, int col, + const struct node_id *ids); +void db_bind_pubkey(struct db_stmt *stmt, int pos, const struct pubkey *p); +void db_bind_short_channel_id(struct db_stmt *stmt, int col, + const struct short_channel_id *id); +void db_bind_short_channel_id_arr(struct db_stmt *stmt, int col, + const struct short_channel_id *id); +void db_bind_signature(struct db_stmt *stmt, int col, + const secp256k1_ecdsa_signature *sig); +void db_bind_timeabs(struct db_stmt *stmt, int col, struct timeabs t); +void db_bind_tx(struct db_stmt *stmt, int col, const struct bitcoin_tx *tx); +void db_bind_amount_msat(struct db_stmt *stmt, int pos, + const struct amount_msat *msat); +void db_bind_amount_sat(struct db_stmt *stmt, int pos, + const struct amount_sat *sat); +void db_bind_json_escape(struct db_stmt *stmt, int pos, + const struct json_escape *esc); + +bool db_step(struct db_stmt *stmt); +u64 db_column_u64(struct db_stmt *stmt, int col); +int db_column_int(struct db_stmt *stmt, int col); +size_t db_column_bytes(struct db_stmt *stmt, int col); +int db_column_is_null(struct db_stmt *stmt, int col); +const void* db_column_blob(struct db_stmt *stmt, int col); +const unsigned char *db_column_text(struct db_stmt *stmt, int col); +void db_column_preimage(struct db_stmt *stmt, int col, struct preimage *preimage); +void db_column_amount_msat(struct db_stmt *stmt, int col, struct amount_msat *msat); +void db_column_amount_sat(struct db_stmt *stmt, int col, struct amount_sat *sat); +struct json_escape *db_column_json_escape(const tal_t *ctx, struct db_stmt *stmt, int col); +void db_column_sha256(struct db_stmt *stmt, int col, struct sha256 *sha); +void db_column_sha256d(struct db_stmt *stmt, int col, struct sha256_double *shad); +void db_column_secret(struct db_stmt *stmt, int col, struct secret *s); +struct secret *db_column_secret_arr(const tal_t *ctx, struct db_stmt *stmt, + int col); +void db_column_txid(struct db_stmt *stmt, int pos, struct bitcoin_txid *t); +void db_column_node_id(struct db_stmt *stmt, int pos, struct node_id *ni); +struct node_id *db_column_node_id_arr(const tal_t *ctx, struct db_stmt *stmt, + int col); +void db_column_pubkey(struct db_stmt *stmt, int pos, struct pubkey *p); +bool db_column_short_channel_id(struct db_stmt *stmt, int col, + struct short_channel_id *dest); +struct short_channel_id * +db_column_short_channel_id_arr(const tal_t *ctx, struct db_stmt *stmt, int col); +bool db_column_signature(struct db_stmt *stmt, int col, + secp256k1_ecdsa_signature *sig); +struct timeabs db_column_timeabs(struct db_stmt *stmt, int col); +struct bitcoin_tx *db_column_tx(const tal_t *ctx, struct db_stmt *stmt, int col); + +#define db_column_arr(ctx, stmt, col, type) \ + ((type *)db_column_arr_((ctx), (stmt), (col), \ + sizeof(type), TAL_LABEL(type, "[]"), \ + __func__)) +void *db_column_arr_(const tal_t *ctx, struct db_stmt *stmt, int col, + size_t bytes, const char *label, const char *caller); + /** - * db_select_prepare -- Prepare a DB select statement (read-only!) + * db_exec_prepared -- Execute a prepared statement * - * Tiny wrapper around `sqlite3_prepare_v2` that checks and sets - * errors like `db_query` and `db_exec` do. It calls fatal if - * the stmt is not valid. + * After preparing a statement using `db_prepare`, and after binding all + * non-null variables using the `db_bind_*` functions, it can be executed with + * this function. It is a small, transaction-aware, wrapper around `db_step`, + * that calls fatal() if the execution fails. This may take ownership of + * `stmt` if annotated with `take()`and will free it before returning. * - * Call db_select_step() until it returns false (which will also consume - * the stmt). + * If you'd like to issue a query and access the rows returned by the query + * please use `db_query_prepared` instead, since this function will not expose + * returned results, and the `stmt` can only be used for calls to + * `db_count_changes` and `db_last_insert_id` after executing. * - * @db: Database to query/exec - * @query: The SELECT SQL statement to compile + * @stmt: The prepared statement to execute */ -#define db_select_prepare(db, query) \ - db_select_prepare_(__FILE__ ":" stringify(__LINE__), db, query) -sqlite3_stmt *db_select_prepare_(const char *location, - struct db *db, const char *query); +bool db_exec_prepared_v2(struct db_stmt *stmt TAKES); /** - * db_select_step -- iterate through db results. + * db_query_prepared -- Execute a prepared query + * + * After preparing a query using `db_prepare`, and after binding all non-null + * variables using the `db_bind_*` functions, it can be executed with this + * function. This function must be called before calling `db_step` or any of + * the `db_column_*` column access functions. * - * Returns false and frees stmt if we've reached end, otherwise - * it means sqlite3_step has returned SQLITE_ROW. + * If you are not executing a read-only statement, please use + * `db_exec_prepared` instead. + * + * @stmt: The prepared statement to execute */ -#define db_select_step(db, stmt) \ - db_select_step_(__FILE__ ":" stringify(__LINE__), db, stmt) -bool db_select_step_(const char *location, - struct db *db, struct sqlite3_stmt *stmt); +bool db_query_prepared(struct db_stmt *stmt); +size_t db_count_changes(struct db_stmt *stmt); +u64 db_last_insert_id_v2(struct db_stmt *stmt); /** * db_prepare -- Prepare a DB query/command * - * Tiny wrapper around `sqlite3_prepare_v2` that checks and sets - * errors like `db_query` and `db_exec` do. It returns a statement - * `stmt` if the given query/command was successfully compiled into a - * statement, `NULL` otherwise. On failure `db->err` will be set with - * the human readable error. + * Create an instance of `struct db_stmt` that encapsulates a SQL query or command. + * + * @query MUST be wrapped in a `SQL()` macro call, since that allows the + * extraction and translation of the query into the target SQL dialect. + * + * It does not execute the query and does not check its validity, but + * allocates the placeholders detected in the query. The placeholders in the + * `stmt` can then be bound using the `db_bind_*` functions, and executed + * using `db_exec_prepared` for write-only statements and `db_query_prepared` + * for read-only statements. * * @db: Database to query/exec * @query: The SQL statement to compile */ -#define db_prepare(db,query) \ - db_prepare_(__FILE__ ":" stringify(__LINE__), db, query) -sqlite3_stmt *db_prepare_(const char *location, struct db *db, const char *query); +struct db_stmt *db_prepare_v2_(const char *location, struct db *db, + const char *query_id); -/** - * db_exec_prepared -- Execute a prepared statement - * - * After preparing a statement using `db_prepare`, and after binding - * all non-null variables using the `sqlite3_bind_*` functions, it can - * be executed with this function. It is a small, transaction-aware, - * wrapper around `sqlite3_step`, that calls fatal() if the execution - * fails. This will take ownership of `stmt` and will free - * it before returning. - * - * @db: The database to execute on - * @stmt: The prepared statement to execute - */ -#define db_exec_prepared(db,stmt) db_exec_prepared_(__func__,db,stmt) -void db_exec_prepared_(const char *caller, struct db *db, sqlite3_stmt *stmt); - -/* Wrapper around sqlite3_finalize(), for tracking statements. */ -void db_stmt_done(sqlite3_stmt *stmt); - -/* Call when you know there should be no outstanding db statements. */ -void db_assert_no_outstanding_statements(void); - -#define sqlite3_column_arr(ctx, stmt, col, type) \ - ((type *)sqlite3_column_arr_((ctx), (stmt), (col), \ - sizeof(type), TAL_LABEL(type, "[]"), \ - __func__)) -void *sqlite3_column_arr_(const tal_t *ctx, sqlite3_stmt *stmt, int col, - size_t bytes, const char *label, const char *caller); - -bool sqlite3_bind_short_channel_id(sqlite3_stmt *stmt, int col, - const struct short_channel_id *id); -WARN_UNUSED_RESULT bool sqlite3_column_short_channel_id(sqlite3_stmt *stmt, int col, - struct short_channel_id *dest); -bool sqlite3_bind_short_channel_id_array(sqlite3_stmt *stmt, int col, - const struct short_channel_id *id); -struct short_channel_id * -sqlite3_column_short_channel_id_array(const tal_t *ctx, - sqlite3_stmt *stmt, int col); -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); -bool sqlite3_bind_signature(sqlite3_stmt *stmt, int col, const secp256k1_ecdsa_signature *sig); -bool sqlite3_column_signature(sqlite3_stmt *stmt, int col, secp256k1_ecdsa_signature *sig); - -bool sqlite3_column_pubkey(sqlite3_stmt *stmt, int col, struct pubkey *dest); -bool sqlite3_bind_pubkey(sqlite3_stmt *stmt, int col, const struct pubkey *pk); - -bool sqlite3_column_node_id(sqlite3_stmt *stmt, int col, struct node_id *dest); -bool sqlite3_bind_node_id(sqlite3_stmt *stmt, int col, const struct node_id *id); - -bool sqlite3_bind_pubkey_array(sqlite3_stmt *stmt, int col, - const struct pubkey *pks); -struct pubkey *sqlite3_column_pubkey_array(const tal_t *ctx, - sqlite3_stmt *stmt, int col); - -bool sqlite3_bind_node_id_array(sqlite3_stmt *stmt, int col, - const struct node_id *ids); -struct node_id *sqlite3_column_node_id_array(const tal_t *ctx, - sqlite3_stmt *stmt, int col); - -bool sqlite3_column_preimage(sqlite3_stmt *stmt, int col, struct preimage *dest); -bool sqlite3_bind_preimage(sqlite3_stmt *stmt, int col, const struct preimage *p); - -bool sqlite3_column_sha256(sqlite3_stmt *stmt, int col, struct sha256 *dest); -bool sqlite3_bind_sha256(sqlite3_stmt *stmt, int col, const struct sha256 *p); - -bool sqlite3_column_sha256_double(sqlite3_stmt *stmt, int col, struct sha256_double *dest); -bool sqlite3_bind_sha256_double(sqlite3_stmt *stmt, int col, const struct sha256_double *p); -struct secret *sqlite3_column_secrets(const tal_t *ctx, - sqlite3_stmt *stmt, int col); - -struct json_escape *sqlite3_column_json_escape(const tal_t *ctx, - sqlite3_stmt *stmt, int col); -bool sqlite3_bind_json_escape(sqlite3_stmt *stmt, int col, - const struct json_escape *esc); - -struct amount_msat sqlite3_column_amount_msat(sqlite3_stmt *stmt, int col); -struct amount_sat sqlite3_column_amount_sat(sqlite3_stmt *stmt, int col); -void sqlite3_bind_amount_msat(sqlite3_stmt *stmt, int col, - struct amount_msat msat); -void sqlite3_bind_amount_sat(sqlite3_stmt *stmt, int col, - struct amount_sat sat); - -/* Helpers to read and write absolute times from and to the database. */ -void sqlite3_bind_timeabs(sqlite3_stmt *stmt, int col, struct timeabs t); -struct timeabs sqlite3_column_timeabs(sqlite3_stmt *stmt, int col); +/* TODO(cdecker) Remove the v2 suffix after finishing the migration */ +#define db_prepare_v2(db,query) \ + db_prepare_v2_(__FILE__ ":" stringify(__LINE__), db, query) #endif /* LIGHTNING_WALLET_DB_H */ diff --git a/wallet/db_common.h b/wallet/db_common.h new file mode 100644 index 000000000000..74b21b3ed834 --- /dev/null +++ b/wallet/db_common.h @@ -0,0 +1,148 @@ +#ifndef LIGHTNING_WALLET_DB_COMMON_H +#define LIGHTNING_WALLET_DB_COMMON_H +#include "config.h" +#include +#include +#include +#include +#include + +/* For testing, we want to catch fatal messages. */ +#ifndef db_fatal +#define db_fatal fatal +#endif + +struct db { + char *filename; + const char *in_transaction; + sqlite3 *sql; + + /* DB-specific context */ + void *conn; + + /* The configuration, including translated queries for the current + * instance. */ + const struct db_config *config; + + const char **changes; + + /* List of statements that have been created but not executed yet. */ + struct list_head pending_statements; + char *error; +}; + +struct db_query { + const char *name; + const char *query; + + /* How many placeholders are in the query (and how many will we have + to allocate when instantiating this query)? */ + size_t placeholders; + + /* Is this a read-only query? If it is there's no need to tell plugins + * about it. */ + bool readonly; +}; + +enum db_binding_type { + DB_BINDING_UNINITIALIZED = 0, + DB_BINDING_NULL, + DB_BINDING_BLOB, + DB_BINDING_TEXT, + DB_BINDING_UINT64, + DB_BINDING_INT, +}; + +struct db_binding { + enum db_binding_type type; + union { + int i; + u64 u64; + const char* text; + const u8 *blob; + } v; + size_t len; +}; + +struct db_stmt { + /* Our entry in the list of pending statements. */ + struct list_node list; + + /* Database we are querying */ + struct db *db; + + /* Which SQL statement are we trying to execute? */ + struct db_query *query; + + /* Which parameters are we binding to the statement? */ + struct db_binding *bindings; + + /* Where are we calling this statement from? */ + const char *location; + + const char *error; + + /* Pointer to DB-specific statement. */ + void *inner_stmt; + + bool executed; +}; + +struct db_config { + const char *name; + struct db_query *queries; + size_t num_queries; + + /* Function used to execute a statement that doesn't result in a + * response. */ + bool (*exec_fn)(struct db_stmt *stmt); + + /* Function to execute a query that will result in a response. */ + bool (*query_fn)(struct db_stmt *stmt); + + /* Function used to step forwards through query results. Returns + * `false` if there are no more rows to return. */ + bool (*step_fn)(struct db_stmt *stmt); + + bool (*begin_tx_fn)(struct db *db); + bool (*commit_tx_fn)(struct db *db); + + /* The free function must make sure that any associated state stored + * in `stmt->inner_stmt` is freed correctly, setting the pointer to + * NULL after cleaning up. It will ultmately be called by the + * destructor of `struct db_stmt`, before clearing the db_stmt + * itself. */ + void (*stmt_free_fn)(struct db_stmt *db_stmt); + + /* Column access in a row. Only covers the primitives, others need to + * use these internally to translate (hence the non-allocating + * column_{text,blob}_fn since most other types want in place + * assignment. */ + bool (*column_is_null_fn)(struct db_stmt *stmt, int col); + u64 (*column_u64_fn)(struct db_stmt *stmt, int col); + size_t (*column_bytes_fn)(struct db_stmt *stmt, int col); + const void *(*column_blob_fn)(struct db_stmt *stmt, int col); + const unsigned char *(*column_text_fn)(struct db_stmt *stmt, int col); + s64 (*column_int_fn)(struct db_stmt *stmt, int col); + + u64 (*last_insert_id_fn)(struct db_stmt *stmt); + size_t (*count_changes_fn)(struct db_stmt *stmt); + + bool (*setup_fn)(struct db *db); + void (*teardown_fn)(struct db *db); +}; + +/* Provide a way for DB backends to register themselves */ +AUTODATA_TYPE(db_backends, struct db_config); + +/** + * Report a statement that changes the wallet + * + * Allows the DB driver to report an expanded statement during + * execution. Changes are queued up and reported to the `db_write` plugin hook + * upon committing. + */ +void db_changes_add(struct db_stmt *db_stmt, const char * expanded); + + +#endif /* LIGHTNING_WALLET_DB_COMMON_H */ diff --git a/wallet/db_sqlite3.c b/wallet/db_sqlite3.c new file mode 100644 index 000000000000..5d568d4563a8 --- /dev/null +++ b/wallet/db_sqlite3.c @@ -0,0 +1,235 @@ +#include +#include "gen_db_sqlite3.c" +#include +#include +#include +#include + +#if HAVE_SQLITE3 + +#if !HAVE_SQLITE3_EXPANDED_SQL +/* Prior to sqlite3 v3.14, we have to use tracing to dump statements */ +static void trace_sqlite3(void *stmtv, const char *stmt) +{ + struct db_stmt = (struct db_stmt*)stmtv; + db_changes_add(stmt, stmt); +} +#endif + +static const char *db_sqlite3_fmt_error(struct db_stmt *stmt) +{ + return tal_fmt(stmt, "%s: %s: %s", stmt->location, stmt->query->query, + sqlite3_errmsg(stmt->db->conn)); +} + +static bool db_sqlite3_setup(struct db *db) +{ + sqlite3_stmt *stmt; + int err; + sqlite3_prepare_v2(db->conn, "PRAGMA foreign_keys = ON;", -1, &stmt, NULL); + err = sqlite3_step(stmt); + sqlite3_finalize(stmt); + return err == SQLITE_DONE; +} + +static bool db_sqlite3_query(struct db_stmt *stmt) +{ + sqlite3_stmt *s; + sqlite3 *conn = (sqlite3*)stmt->db->conn; + int err; + + err = sqlite3_prepare_v2(conn, stmt->query->query, -1, &s, NULL); + + for (size_t i=0; iquery->placeholders; i++) { + struct db_binding *b = &stmt->bindings[i]; + + /* sqlite3 uses printf-like offsets, we don't... */ + int pos = i+1; + switch (b->type) { + case DB_BINDING_UNINITIALIZED: + db_fatal("DB binding not initialized: position=%zu, " + "query=\"%s\n", + i, stmt->query->query); + case DB_BINDING_UINT64: + sqlite3_bind_int64(s, pos, b->v.u64); + break; + case DB_BINDING_INT: + sqlite3_bind_int(s, pos, b->v.i); + break; + case DB_BINDING_BLOB: + sqlite3_bind_blob(s, pos, b->v.blob, b->len, + SQLITE_TRANSIENT); + break; + case DB_BINDING_TEXT: + sqlite3_bind_text(s, pos, b->v.text, b->len, + SQLITE_TRANSIENT); + break; + case DB_BINDING_NULL: + sqlite3_bind_null(s, pos); + break; + } + } + + if (err != SQLITE_OK) { + tal_free(stmt->error); + stmt->error = db_sqlite3_fmt_error(stmt); + return false; + } + + stmt->inner_stmt = s; + return true; +} + +static bool db_sqlite3_exec(struct db_stmt *stmt) +{ + int err; +#if !HAVE_SQLITE3_EXPANDED_SQL + /* Register the tracing function if we don't have an explicit way of + * expanding the statement. */ + sqlite3_trace(db->sql, trace_sqlite3, stmt); +#endif + + if (!db_sqlite3_query(stmt)) { + /* If the prepare step caused an error we hand it up. */ + return false; + } + + err = sqlite3_step(stmt->inner_stmt); + if (err != SQLITE_DONE) { + tal_free(stmt->error); + stmt->error = db_sqlite3_fmt_error(stmt); + return false; + } + +#if HAVE_SQLITE3_EXPANDED_SQL + /* Manually expand and call the callback */ + char *expanded_sql; + expanded_sql = sqlite3_expanded_sql(stmt->inner_stmt); + db_changes_add(stmt, expanded_sql); + sqlite3_free(expanded_sql); +#else + /* Unregister the trace callback to avoid it accessing the potentially + * stale pointer to stmt */ + sqlite3_trace(db->sql, NULL, NULL); +#endif + + return true; +} + +static bool db_sqlite3_step(struct db_stmt *stmt) +{ + sqlite3_stmt *s = (sqlite3_stmt*)stmt->inner_stmt; + return sqlite3_step(s) == SQLITE_ROW; +} + +static bool db_sqlite3_begin_tx(struct db *db) +{ + int err; + char *errmsg; + err = sqlite3_exec(db->conn, "BEGIN TRANSACTION;", NULL, NULL, &errmsg); + if (err != SQLITE_OK) { + db->error = tal_fmt(db, "Failed to begin a transaction: %s", errmsg); + return false; + } + return true; +} + +static bool db_sqlite3_commit_tx(struct db *db) +{ + int err; + char *errmsg; + err = sqlite3_exec(db->conn, "COMMIT;", NULL, NULL, &errmsg); + if (err != SQLITE_OK) { + db->error = tal_fmt(db, "Failed to begin a transaction: %s", errmsg); + return false; + } + return true; +} + +static bool db_sqlite3_column_is_null(struct db_stmt *stmt, int col) +{ + sqlite3_stmt *s = (sqlite3_stmt*)stmt->inner_stmt; + return sqlite3_column_type(s, col) == SQLITE_NULL; +} + +static u64 db_sqlite3_column_u64(struct db_stmt *stmt, int col) +{ + sqlite3_stmt *s = (sqlite3_stmt*)stmt->inner_stmt; + return sqlite3_column_int64(s, col); +} + +static s64 db_sqlite3_column_int(struct db_stmt *stmt, int col) +{ + sqlite3_stmt *s = (sqlite3_stmt*)stmt->inner_stmt; + return sqlite3_column_int(s, col); +} + +static size_t db_sqlite3_column_bytes(struct db_stmt *stmt, int col) +{ + sqlite3_stmt *s = (sqlite3_stmt*)stmt->inner_stmt; + return sqlite3_column_bytes(s, col); +} + +static const void *db_sqlite3_column_blob(struct db_stmt *stmt, int col) +{ + sqlite3_stmt *s = (sqlite3_stmt*)stmt->inner_stmt; + return sqlite3_column_blob(s, col); +} + +static const unsigned char *db_sqlite3_column_text(struct db_stmt *stmt, int col) +{ + sqlite3_stmt *s = (sqlite3_stmt*)stmt->inner_stmt; + return sqlite3_column_text(s, col); +} + +static void db_sqlite3_stmt_free(struct db_stmt *stmt) +{ + if (stmt->inner_stmt) + sqlite3_finalize(stmt->inner_stmt); + stmt->inner_stmt = NULL; +} + +static size_t db_sqlite3_count_changes(struct db_stmt *stmt) +{ + sqlite3 *s = stmt->db->conn; + return sqlite3_changes(s); +} + +static void db_sqlite3_close(struct db *db) +{ + sqlite3_close(db->sql); +} + +static u64 db_sqlite3_last_insert_id(struct db_stmt *stmt) +{ + sqlite3 *s = stmt->db->conn; + return sqlite3_last_insert_rowid(s); +} + +struct db_config db_sqlite3_config = { + .name = "sqlite3", + .queries = db_sqlite3_queries, + .num_queries = DB_SQLITE3_QUERY_COUNT, + .exec_fn = &db_sqlite3_exec, + .query_fn = &db_sqlite3_query, + .step_fn = &db_sqlite3_step, + .begin_tx_fn = &db_sqlite3_begin_tx, + .commit_tx_fn = &db_sqlite3_commit_tx, + .stmt_free_fn = &db_sqlite3_stmt_free, + + .column_is_null_fn = &db_sqlite3_column_is_null, + .column_u64_fn = &db_sqlite3_column_u64, + .column_int_fn = &db_sqlite3_column_int, + .column_bytes_fn = &db_sqlite3_column_bytes, + .column_blob_fn = &db_sqlite3_column_blob, + .column_text_fn = &db_sqlite3_column_text, + + .last_insert_id_fn = &db_sqlite3_last_insert_id, + .count_changes_fn = &db_sqlite3_count_changes, + .setup_fn = &db_sqlite3_setup, + .teardown_fn = &db_sqlite3_close, +}; + +AUTODATA(db_backends, &db_sqlite3_config); + +#endif diff --git a/wallet/invoices.c b/wallet/invoices.c index 3c61f61012a2..371c8ceed618 100644 --- a/wallet/invoices.c +++ b/wallet/invoices.c @@ -12,11 +12,8 @@ #include #include #include -#include #include -#define INVOICE_TBL_FIELDS "state, payment_key, payment_hash, label, msatoshi, expiry_time, pay_index, msatoshi_received, paid_timestamp, bolt11, description" - struct invoice_waiter { /* Is this waiter already triggered? */ bool triggered; @@ -87,38 +84,38 @@ trigger_invoice_waiter_expire_or_delete(struct invoices *invoices, } static struct invoice_details *wallet_stmt2invoice_details(const tal_t *ctx, - sqlite3_stmt *stmt) + struct db_stmt *stmt) { struct invoice_details *dtl = tal(ctx, struct invoice_details); - dtl->state = sqlite3_column_int(stmt, 0); + dtl->state = db_column_int(stmt, 0); - sqlite3_column_preimage(stmt, 1, &dtl->r); + db_column_preimage(stmt, 1, &dtl->r); - sqlite3_column_sha256(stmt, 2, &dtl->rhash); + db_column_sha256(stmt, 2, &dtl->rhash); - dtl->label = sqlite3_column_json_escape(dtl, stmt, 3); + dtl->label = db_column_json_escape(dtl, stmt, 3); - if (sqlite3_column_type(stmt, 4) != SQLITE_NULL) { + if (!db_column_is_null(stmt, 4)) { dtl->msat = tal(dtl, struct amount_msat); - *dtl->msat = sqlite3_column_amount_msat(stmt, 4); + db_column_amount_msat(stmt, 4, dtl->msat); } else { dtl->msat = NULL; } - dtl->expiry_time = sqlite3_column_int64(stmt, 5); + dtl->expiry_time = db_column_u64(stmt, 5); if (dtl->state == PAID) { - dtl->pay_index = sqlite3_column_int64(stmt, 6); - dtl->received = sqlite3_column_amount_msat(stmt, 7); - dtl->paid_timestamp = sqlite3_column_int64(stmt, 8); + dtl->pay_index = db_column_u64(stmt, 6); + db_column_amount_msat(stmt, 7, &dtl->received); + dtl->paid_timestamp = db_column_u64(stmt, 8); } - dtl->bolt11 = tal_strndup(dtl, sqlite3_column_blob(stmt, 9), - sqlite3_column_bytes(stmt, 9)); + dtl->bolt11 = tal_strndup(dtl, db_column_blob(stmt, 9), + db_column_bytes(stmt, 9)); - if (sqlite3_column_type(stmt, 10) != SQLITE_NULL) + if (!db_column_is_null(stmt, 10)) dtl->description = tal_strdup( - dtl, (const char *)sqlite3_column_text(stmt, 10)); + dtl, (const char *)db_column_text(stmt, 10)); else dtl->description = NULL; @@ -128,16 +125,15 @@ static struct invoice_details *wallet_stmt2invoice_details(const tal_t *ctx, /* Update expirations. */ static void update_db_expirations(struct invoices *invoices, u64 now) { - sqlite3_stmt *stmt; - stmt = db_prepare(invoices->db, - "UPDATE invoices" - " SET state = ?" - " WHERE state = ?" - " AND expiry_time <= ?;"); - sqlite3_bind_int(stmt, 1, EXPIRED); - sqlite3_bind_int(stmt, 2, UNPAID); - sqlite3_bind_int64(stmt, 3, now); - db_exec_prepared(invoices->db, stmt); + struct db_stmt *stmt; + stmt = db_prepare_v2(invoices->db, SQL("UPDATE invoices" + " SET state = ?" + " WHERE state = ?" + " AND expiry_time <= ?;")); + db_bind_int(stmt, 0, EXPIRED); + db_bind_int(stmt, 1, UNPAID); + db_bind_u64(stmt, 2, now); + db_exec_prepared_v2(take(stmt)); } static void install_expiration_timer(struct invoices *invoices); @@ -172,7 +168,7 @@ static void trigger_expiration(struct invoices *invoices) struct list_head idlist; struct invoice_id_node *idn; u64 now = time_now().ts.tv_sec; - sqlite3_stmt *stmt; + struct db_stmt *stmt; struct invoice i; /* Free current expiration timer */ @@ -180,18 +176,20 @@ static void trigger_expiration(struct invoices *invoices) /* Acquire all expired invoices and save them in a list */ list_head_init(&idlist); - stmt = db_select_prepare(invoices->db, - "id" - " FROM invoices" - " WHERE state = ?" - " AND expiry_time <= ?;"); - sqlite3_bind_int(stmt, 1, UNPAID); - sqlite3_bind_int64(stmt, 2, now); - while (db_select_step(invoices->db, stmt)) { + stmt = db_prepare_v2(invoices->db, SQL("SELECT id" + " FROM invoices" + " WHERE state = ?" + " AND expiry_time <= ?")); + db_bind_int(stmt, 0, UNPAID); + db_bind_u64(stmt, 1, now); + db_query_prepared(stmt); + + while (db_step(stmt)) { idn = tal(tmpctx, struct invoice_id_node); list_add_tail(&idlist, &idn->list); - idn->id = sqlite3_column_int64(stmt, 0); + idn->id = db_column_u64(stmt, 0); } + tal_free(stmt); /* Expire all those invoices */ update_db_expirations(invoices, now); @@ -200,9 +198,7 @@ static void trigger_expiration(struct invoices *invoices) list_for_each(&idlist, idn, list) { /* Trigger expiration */ i.id = idn->id; - trigger_invoice_waiter_expire_or_delete(invoices, - idn->id, - &i); + trigger_invoice_waiter_expire_or_delete(invoices, idn->id, &i); } install_expiration_timer(invoices); @@ -211,7 +207,7 @@ static void trigger_expiration(struct invoices *invoices) static void install_expiration_timer(struct invoices *invoices) { bool res; - sqlite3_stmt *stmt; + struct db_stmt *stmt; struct timerel rel; struct timeabs expiry; struct timeabs now = time_now(); @@ -219,20 +215,21 @@ static void install_expiration_timer(struct invoices *invoices) assert(!invoices->expiration_timer); /* Find unpaid invoice with nearest expiry time */ - stmt = db_select_prepare(invoices->db, - "MIN(expiry_time)" - " FROM invoices" - " WHERE state = ?;"); - sqlite3_bind_int(stmt, 1, UNPAID); - res = db_select_step(invoices->db, stmt); + stmt = db_prepare_v2(invoices->db, SQL("SELECT MIN(expiry_time)" + " FROM invoices" + " WHERE state = ?;")); + db_bind_int(stmt, 0, UNPAID); + + db_query_prepared(stmt); + + res = db_step(stmt); assert(res); - if (sqlite3_column_type(stmt, 0) == SQLITE_NULL) { + + if (db_column_is_null(stmt, 0)) /* Nothing to install */ - db_stmt_done(stmt); - return; - } else - invoices->min_expiry_time = sqlite3_column_int64(stmt, 0); - db_stmt_done(stmt); + goto done; + + invoices->min_expiry_time = db_column_u64(stmt, 0); memset(&expiry, 0, sizeof(expiry)); expiry.ts.tv_sec = invoices->min_expiry_time; @@ -250,6 +247,8 @@ static void install_expiration_timer(struct invoices *invoices) rel, &trigger_expiration, invoices); +done: + tal_free(stmt); } bool invoices_create(struct invoices *invoices, @@ -262,7 +261,7 @@ bool invoices_create(struct invoices *invoices, const struct preimage *r, const struct sha256 *rhash) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; struct invoice dummy; u64 expiry_time; u64 now = time_now().ts.tv_sec; @@ -279,35 +278,33 @@ bool invoices_create(struct invoices *invoices, expiry_time = now + expiry; /* Save to database. */ - /* Need to use the lower level API of sqlite3 to bind - * label. Otherwise we'd need to implement sanitization of - * that string for sql injections... */ - stmt = db_prepare(invoices->db, - "INSERT INTO invoices" - " ( payment_hash, payment_key, state" - " , msatoshi, label, expiry_time" - " , pay_index, msatoshi_received" - " , paid_timestamp, bolt11, description)" - " VALUES ( ?, ?, ?" - " , ?, ?, ?" - " , NULL, NULL" - " , NULL, ?, ?);"); - - sqlite3_bind_blob(stmt, 1, rhash, sizeof(struct sha256), SQLITE_TRANSIENT); - sqlite3_bind_blob(stmt, 2, r, sizeof(struct preimage), SQLITE_TRANSIENT); - sqlite3_bind_int(stmt, 3, UNPAID); + stmt = db_prepare_v2( + invoices->db, + SQL("INSERT INTO invoices" + " ( payment_hash, payment_key, state" + " , msatoshi, label, expiry_time" + " , pay_index, msatoshi_received" + " , paid_timestamp, bolt11, description)" + " VALUES ( ?, ?, ?" + " , ?, ?, ?" + " , NULL, NULL" + " , NULL, ?, ?);")); + + db_bind_sha256(stmt, 0, rhash); + db_bind_preimage(stmt, 1, r); + db_bind_int(stmt, 2, UNPAID); if (msat) - sqlite3_bind_amount_msat(stmt, 4, *msat); + db_bind_amount_msat(stmt, 3, msat); else - sqlite3_bind_null(stmt, 4); - sqlite3_bind_json_escape(stmt, 5, label); - sqlite3_bind_int64(stmt, 6, expiry_time); - sqlite3_bind_text(stmt, 7, b11enc, strlen(b11enc), SQLITE_TRANSIENT); - sqlite3_bind_text(stmt, 8, description, strlen(description), SQLITE_TRANSIENT); + db_bind_null(stmt, 3); + db_bind_json_escape(stmt, 4, label); + db_bind_u64(stmt, 5, expiry_time); + db_bind_text(stmt, 6, b11enc); + db_bind_text(stmt, 7, description); - db_exec_prepared(invoices->db, stmt); + db_exec_prepared_v2(stmt); - pinvoice->id = sqlite3_last_insert_rowid(invoices->db->sql); + pinvoice->id = db_last_insert_id_v2(take(stmt)); /* Install expiration trigger. */ if (!invoices->expiration_timer || @@ -329,18 +326,20 @@ bool invoices_find_by_label(struct invoices *invoices, struct invoice *pinvoice, const struct json_escape *label) { - sqlite3_stmt *stmt; - - stmt = db_select_prepare(invoices->db, - "id" - " FROM invoices" - " WHERE label = ?;"); - sqlite3_bind_json_escape(stmt, 1, label); - if (!db_select_step(invoices->db, stmt)) + struct db_stmt *stmt; + stmt = db_prepare_v2(invoices->db, SQL("SELECT id" + " FROM invoices" + " WHERE label = ?;")); + db_bind_json_escape(stmt, 0, label); + db_query_prepared(stmt); + + if (!db_step(stmt)) { + tal_free(stmt); return false; + } - pinvoice->id = sqlite3_column_int64(stmt, 0); - db_stmt_done(stmt); + pinvoice->id = db_column_u64(stmt, 0); + tal_free(stmt); return true; } @@ -348,90 +347,112 @@ bool invoices_find_by_rhash(struct invoices *invoices, struct invoice *pinvoice, const struct sha256 *rhash) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; - stmt = db_select_prepare(invoices->db, - "id" - " FROM invoices" - " WHERE payment_hash = ?;"); - sqlite3_bind_blob(stmt, 1, rhash, sizeof(*rhash), SQLITE_TRANSIENT); - if (!db_select_step(invoices->db, stmt)) - return false; + stmt = db_prepare_v2(invoices->db, SQL("SELECT id" + " FROM invoices" + " WHERE payment_hash = ?;")); + db_bind_sha256(stmt, 0, rhash); + db_query_prepared(stmt); - pinvoice->id = sqlite3_column_int64(stmt, 0); - db_stmt_done(stmt); - return true; + if (!db_step(stmt)) { + tal_free(stmt); + return false; + } else { + pinvoice->id = db_column_u64(stmt, 0); + tal_free(stmt); + return true; + } } bool invoices_find_unpaid(struct invoices *invoices, struct invoice *pinvoice, const struct sha256 *rhash) { - sqlite3_stmt *stmt; - - stmt = db_select_prepare(invoices->db, - " id" - " FROM invoices" - " WHERE payment_hash = ?" - " AND state = ?;"); - sqlite3_bind_blob(stmt, 1, rhash, sizeof(*rhash), SQLITE_TRANSIENT); - sqlite3_bind_int(stmt, 2, UNPAID); - if (!db_select_step(invoices->db, stmt)) + struct db_stmt *stmt; + stmt = db_prepare_v2(invoices->db, SQL("SELECT id" + " FROM invoices" + " WHERE payment_hash = ?" + " AND state = ?;")); + db_bind_sha256(stmt, 0, rhash); + db_bind_int(stmt, 1, UNPAID); + db_query_prepared(stmt); + + if (!db_step(stmt)) { + tal_free(stmt); return false; - - pinvoice->id = sqlite3_column_int64(stmt, 0); - db_stmt_done(stmt); - return true; + } else { + pinvoice->id = db_column_u64(stmt, 0); + tal_free(stmt); + return true; + } } -bool invoices_delete(struct invoices *invoices, - struct invoice invoice) +bool invoices_delete(struct invoices *invoices, struct invoice invoice) { - sqlite3_stmt *stmt; - + struct db_stmt *stmt; + int changes; /* Delete from database. */ - stmt = db_prepare(invoices->db, "DELETE FROM invoices WHERE id=?;"); - sqlite3_bind_int64(stmt, 1, invoice.id); - db_exec_prepared(invoices->db, stmt); + stmt = db_prepare_v2(invoices->db, + SQL("DELETE FROM invoices WHERE id=?;")); + db_bind_u64(stmt, 0, invoice.id); + db_exec_prepared_v2(stmt); - if (sqlite3_changes(invoices->db->sql) != 1) - return false; + changes = db_count_changes(stmt); + tal_free(stmt); + if (changes != 1) { + return false; + } /* Tell all the waiters about the fact that it was deleted. */ - trigger_invoice_waiter_expire_or_delete(invoices, - invoice.id, NULL); + trigger_invoice_waiter_expire_or_delete(invoices, invoice.id, NULL); return true; } void invoices_delete_expired(struct invoices *invoices, u64 max_expiry_time) { - sqlite3_stmt *stmt; - stmt = db_prepare(invoices->db, + struct db_stmt *stmt; + stmt = db_prepare_v2(invoices->db, SQL( "DELETE FROM invoices" " WHERE state = ?" - " AND expiry_time <= ?;"); - sqlite3_bind_int(stmt, 1, EXPIRED); - sqlite3_bind_int64(stmt, 2, max_expiry_time); - db_exec_prepared(invoices->db, stmt); + " AND expiry_time <= ?;")); + db_bind_int(stmt, 0, EXPIRED); + db_bind_u64(stmt, 1, max_expiry_time); + db_exec_prepared_v2(take(stmt)); } bool invoices_iterate(struct invoices *invoices, struct invoice_iterator *it) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; if (!it->p) { - stmt = db_select_prepare(invoices->db, - INVOICE_TBL_FIELDS - " FROM invoices;"); + stmt = db_prepare_v2(invoices->db, SQL("SELECT" + " state" + ", payment_key" + ", payment_hash" + ", label" + ", msatoshi" + ", expiry_time" + ", pay_index" + ", msatoshi_received" + ", paid_timestamp" + ", bolt11" + ", description" + " FROM invoices;")); + db_query_prepared(stmt); it->p = stmt; } else stmt = it->p; - if (db_select_step(invoices->db, stmt)) + + if (db_step(stmt)) + /* stmt doesn't need to be freed since we expect to be called + * again, and stmt will be freed on the last iteration. */ return true; + tal_free(stmt); it->p = NULL; return false; } @@ -441,7 +462,7 @@ invoices_iterator_deref(const tal_t *ctx, struct invoices *invoices UNUSED, const struct invoice_iterator *it) { assert(it->p); - return wallet_stmt2invoice_details(ctx, (sqlite3_stmt*) it->p); + return wallet_stmt2invoice_details(ctx, (struct db_stmt*) it->p); } static s64 get_next_pay_index(struct db *db) @@ -457,17 +478,19 @@ static s64 get_next_pay_index(struct db *db) static enum invoice_status invoice_get_status(struct invoices *invoices, struct invoice invoice) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; enum invoice_status state; bool res; - stmt = db_select_prepare(invoices->db, - "state FROM invoices WHERE id = ?;"); - sqlite3_bind_int64(stmt, 1, invoice.id); - res = db_select_step(invoices->db, stmt); + stmt = db_prepare_v2( + invoices->db, SQL("SELECT state FROM invoices WHERE id = ?;")); + db_bind_u64(stmt, 0, invoice.id); + db_query_prepared(stmt); + + res = db_step(stmt); assert(res); - state = sqlite3_column_int(stmt, 0); - db_stmt_done(stmt); + state = db_column_int(stmt, 0); + tal_free(stmt); return state; } @@ -475,7 +498,7 @@ void invoices_resolve(struct invoices *invoices, struct invoice invoice, struct amount_msat received) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; s64 pay_index; u64 paid_timestamp; enum invoice_status state = invoice_get_status(invoices, invoice); @@ -487,19 +510,18 @@ void invoices_resolve(struct invoices *invoices, paid_timestamp = time_now().ts.tv_sec; /* Update database. */ - stmt = db_prepare(invoices->db, - "UPDATE invoices" - " SET state=?" - " , pay_index=?" - " , msatoshi_received=?" - " , paid_timestamp=?" - " WHERE id=?;"); - sqlite3_bind_int(stmt, 1, PAID); - sqlite3_bind_int64(stmt, 2, pay_index); - sqlite3_bind_amount_msat(stmt, 3, received); - sqlite3_bind_int64(stmt, 4, paid_timestamp); - sqlite3_bind_int64(stmt, 5, invoice.id); - db_exec_prepared(invoices->db, stmt); + stmt = db_prepare_v2(invoices->db, SQL("UPDATE invoices" + " SET state=?" + " , pay_index=?" + " , msatoshi_received=?" + " , paid_timestamp=?" + " WHERE id=?;")); + db_bind_int(stmt, 0, PAID); + db_bind_u64(stmt, 1, pay_index); + db_bind_amount_msat(stmt, 2, &received); + db_bind_u64(stmt, 3, paid_timestamp); + db_bind_u64(stmt, 4, invoice.id); + db_exec_prepared_v2(take(stmt)); /* Tell all the waiters about the paid invoice. */ trigger_invoice_waiter_resolve(invoices, invoice.id, &invoice); @@ -539,29 +561,29 @@ void invoices_waitany(const tal_t *ctx, void (*cb)(const struct invoice *, void*), void *cbarg) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; struct invoice invoice; /* Look for an already-paid invoice. */ - stmt = db_select_prepare(invoices->db, - "id" + stmt = db_prepare_v2(invoices->db, + SQL("SELECT id" " FROM invoices" " WHERE pay_index NOT NULL" " AND pay_index > ?" - " ORDER BY pay_index ASC LIMIT 1;"); - sqlite3_bind_int64(stmt, 1, lastpay_index); + " ORDER BY pay_index ASC LIMIT 1;")); + db_bind_u64(stmt, 0, lastpay_index); + db_query_prepared(stmt); - if (db_select_step(invoices->db, stmt)) { - invoice.id = sqlite3_column_int64(stmt, 0); - db_stmt_done(stmt); + if (db_step(stmt)) { + invoice.id = db_column_u64(stmt, 0); cb(&invoice, cbarg); - return; - } - - /* None found. */ - add_invoice_waiter(ctx, &invoices->waiters, + } else { + /* None found. */ + add_invoice_waiter(ctx, &invoices->waiters, true, 0, cb, cbarg); + } + tal_free(stmt); } @@ -589,19 +611,30 @@ const struct invoice_details *invoices_get_details(const tal_t *ctx, struct invoices *invoices, struct invoice invoice) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; bool res; struct invoice_details *details; - stmt = db_select_prepare(invoices->db, - INVOICE_TBL_FIELDS - " FROM invoices" - " WHERE id = ?;"); - sqlite3_bind_int64(stmt, 1, invoice.id); - res = db_select_step(invoices->db, stmt); + stmt = db_prepare_v2(invoices->db, SQL("SELECT" + " state" + ", payment_key" + ", payment_hash" + ", label" + ", msatoshi" + ", expiry_time" + ", pay_index" + ", msatoshi_received" + ", paid_timestamp" + ", bolt11" + ", description" + " FROM invoices" + " WHERE id = ?;")); + db_bind_u64(stmt, 0, invoice.id); + db_query_prepared(stmt); + res = db_step(stmt); assert(res); details = wallet_stmt2invoice_details(ctx, stmt); - db_stmt_done(stmt); + tal_free(stmt); return details; } diff --git a/wallet/test/Makefile b/wallet/test/Makefile index ddeb9ddfdf0b..c0ed67fa402d 100644 --- a/wallet/test/Makefile +++ b/wallet/test/Makefile @@ -17,6 +17,7 @@ WALLET_TEST_COMMON_OBJS := \ common/utils.o \ common/wireaddr.o \ common/version.o \ + wallet/db_sqlite3.o \ wire/towire.o \ wire/fromwire.o diff --git a/wallet/test/run-db.c b/wallet/test/run-db.c index 355c6458bbee..bd8642250948 100644 --- a/wallet/test/run-db.c +++ b/wallet/test/run-db.c @@ -24,6 +24,9 @@ size_t bigsize_get(const u8 *p UNNEEDED, size_t max UNNEEDED, bigsize_t *val UNN /* Generated stub for bigsize_put */ size_t bigsize_put(u8 buf[BIGSIZE_MAX_LEN] UNNEEDED, bigsize_t v UNNEEDED) { fprintf(stderr, "bigsize_put called!\n"); abort(); } +/* Generated stub for fatal */ +void fatal(const char *fmt UNNEEDED, ...) +{ fprintf(stderr, "fatal called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ static char *db_err; @@ -75,7 +78,9 @@ static bool test_empty_db_migrate(struct lightningd *ld) static bool test_primitives(void) { + struct db_stmt *stmt; struct db *db = create_test_db(); + db_err = NULL; db_begin_transaction(db); CHECK(db->in_transaction); db_commit_transaction(db); @@ -84,11 +89,15 @@ static bool test_primitives(void) db_commit_transaction(db); db_begin_transaction(db); - db_exec(__func__, db, "SELECT name FROM sqlite_master WHERE type='table';"); + stmt = db_prepare_v2(db, SQL("SELECT name FROM sqlite_master WHERE type='table';")); + CHECK_MSG(db_exec_prepared_v2(stmt), "db_exec_prepared must succeed"); CHECK_MSG(!db_err, "Simple correct SQL command"); + tal_free(stmt); - db_exec(__func__, db, "not a valid SQL statement"); + stmt = db_prepare_v2(db, SQL("not a valid SQL statement")); + CHECK_MSG(!db_exec_prepared_v2(stmt), "db_exec_prepared must fail"); CHECK_MSG(db_err, "Failing SQL command"); + tal_free(stmt); db_err = tal_free(db_err); db_commit_transaction(db); CHECK(!db->in_transaction); diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index a1a9b2a39478..93e4ecb8b317 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -1126,6 +1126,7 @@ static bool test_channel_config_crud(struct lightningd *ld, const tal_t *ctx) static bool test_htlc_crud(struct lightningd *ld, const tal_t *ctx) { + struct db_stmt *stmt; struct htlc_in in, *hin; struct htlc_out out, *hout; struct preimage payment_key; @@ -1136,8 +1137,13 @@ static bool test_htlc_crud(struct lightningd *ld, const tal_t *ctx) struct htlc_out_map *htlcs_out = tal(ctx, struct htlc_out_map); /* Make sure we have our references correct */ - CHECK(transaction_wrap(w->db, - db_exec(__func__, w->db, "INSERT INTO channels (id) VALUES (1);"))); + db_begin_transaction(w->db); + char *query = SQL("INSERT INTO channels (id) VALUES (1);"); + stmt = db_prepare_v2(w->db, query); + db_exec_prepared_v2(stmt); + tal_free(stmt); + db_commit_transaction(w->db); + chan->dbid = 1; chan->peer = peer; diff --git a/wallet/wallet.c b/wallet/wallet.c index 9e86688ce391..cc391f029630 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -14,6 +14,7 @@ #include #include #include +#include #define SQLITE_MAX_UINT 0x7FFFFFFFFFFFFFFF #define DIRECTION_INCOMING 0 @@ -24,7 +25,7 @@ static void outpointfilters_init(struct wallet *w) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; struct utxo **utxos = wallet_get_utxos(NULL, w, output_state_any); struct bitcoin_txid txid; u32 outnum; @@ -36,13 +37,17 @@ static void outpointfilters_init(struct wallet *w) tal_free(utxos); w->utxoset_outpoints = outpointfilter_new(w); - stmt = db_select_prepare(w->db, "txid, outnum FROM utxoset WHERE spendheight is NULL"); - - while (db_select_step(w->db, stmt)) { - sqlite3_column_sha256_double(stmt, 0, &txid.shad); - outnum = sqlite3_column_int(stmt, 1); + stmt = db_prepare_v2( + w->db, + SQL("SELECT txid, outnum FROM utxoset WHERE spendheight is NULL")); + db_query_prepared(stmt); + + while (db_step(stmt)) { + db_column_sha256d(stmt, 0, &txid.shad); + outnum = db_column_int(stmt, 1); outpointfilter_add(w->utxoset_outpoints, &txid, outnum); } + tal_free(stmt); } struct wallet *wallet_new(struct lightningd *ld, @@ -63,86 +68,94 @@ struct wallet *wallet_new(struct lightningd *ld, return wallet; } -#define UTXO_FIELDS \ - "prev_out_tx, prev_out_index, value, type, status, keyindex, " \ - "channel_id, peer_id, commitment_point, confirmation_height, " \ - "spend_height, scriptpubkey" - /* This can fail if we've already seen UTXO. */ bool wallet_add_utxo(struct wallet *w, struct utxo *utxo, enum wallet_output_type type) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; - stmt = db_select_prepare(w->db, - "* from outputs WHERE prev_out_tx=? AND prev_out_index=?"); - sqlite3_bind_blob(stmt, 1, &utxo->txid, sizeof(utxo->txid), SQLITE_TRANSIENT); - sqlite3_bind_int(stmt, 2, utxo->outnum); + stmt = db_prepare_v2(w->db, SQL("SELECT * from outputs WHERE " + "prev_out_tx=? AND prev_out_index=?")); + db_bind_txid(stmt, 0, &utxo->txid); + db_bind_int(stmt, 1, utxo->outnum); + db_query_prepared(stmt); /* If we get a result, that means a clash. */ - if (db_select_step(w->db, stmt)) { - db_stmt_done(stmt); + if (db_step(stmt)) { + tal_free(stmt); return false; } - - stmt = db_prepare(w->db, "INSERT INTO outputs (" - UTXO_FIELDS - ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"); - sqlite3_bind_blob(stmt, 1, &utxo->txid, sizeof(utxo->txid), SQLITE_TRANSIENT); - sqlite3_bind_int(stmt, 2, utxo->outnum); - sqlite3_bind_amount_sat(stmt, 3, utxo->amount); - sqlite3_bind_int(stmt, 4, wallet_output_type_in_db(type)); - sqlite3_bind_int(stmt, 5, output_state_available); - sqlite3_bind_int(stmt, 6, utxo->keyindex); + tal_free(stmt); + + stmt = db_prepare_v2( + w->db, SQL("INSERT INTO outputs (" + " prev_out_tx" + ", prev_out_index" + ", value" + ", type" + ", status" + ", keyindex" + ", channel_id" + ", peer_id" + ", commitment_point" + ", confirmation_height" + ", spend_height" + ", scriptpubkey" + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); + db_bind_txid(stmt, 0, &utxo->txid); + db_bind_int(stmt, 1, utxo->outnum); + db_bind_amount_sat(stmt, 2, &utxo->amount); + db_bind_int(stmt, 3, wallet_output_type_in_db(type)); + db_bind_int(stmt, 4, output_state_available); + db_bind_int(stmt, 5, utxo->keyindex); if (utxo->close_info) { - sqlite3_bind_int64(stmt, 7, utxo->close_info->channel_id); - sqlite3_bind_node_id(stmt, 8, &utxo->close_info->peer_id); - sqlite3_bind_pubkey(stmt, 9, &utxo->close_info->commitment_point); + db_bind_u64(stmt, 6, utxo->close_info->channel_id); + db_bind_node_id(stmt, 7, &utxo->close_info->peer_id); + db_bind_pubkey(stmt, 8, &utxo->close_info->commitment_point); } else { - sqlite3_bind_null(stmt, 7); - sqlite3_bind_null(stmt, 8); - sqlite3_bind_null(stmt, 9); + db_bind_null(stmt, 6); + db_bind_null(stmt, 7); + db_bind_null(stmt, 8); } if (utxo->blockheight) { - sqlite3_bind_int(stmt, 10, *utxo->blockheight); + db_bind_int(stmt, 9, *utxo->blockheight); } else - sqlite3_bind_null(stmt, 10); + db_bind_null(stmt, 9); if (utxo->spendheight) - sqlite3_bind_int(stmt, 11, *utxo->spendheight); + db_bind_int(stmt, 10, *utxo->spendheight); else - sqlite3_bind_null(stmt, 11); + db_bind_null(stmt, 10); if (utxo->scriptPubkey) - sqlite3_bind_blob(stmt, 12, utxo->scriptPubkey, - tal_bytelen(utxo->scriptPubkey), - SQLITE_TRANSIENT); + db_bind_blob(stmt, 11, utxo->scriptPubkey, + tal_bytelen(utxo->scriptPubkey)); else - sqlite3_bind_null(stmt, 12); + db_bind_null(stmt, 11); - db_exec_prepared(w->db, stmt); + db_exec_prepared_v2(take(stmt)); return true; } /** * wallet_stmt2output - Extract data from stmt and fill an UTXO */ -static struct utxo *wallet_stmt2output(const tal_t *ctx, sqlite3_stmt *stmt) +static struct utxo *wallet_stmt2output(const tal_t *ctx, struct db_stmt *stmt) { struct utxo *utxo = tal(ctx, struct utxo); u32 *blockheight, *spendheight; - sqlite3_column_sha256_double(stmt, 0, &utxo->txid.shad); - utxo->outnum = sqlite3_column_int(stmt, 1); - utxo->amount = sqlite3_column_amount_sat(stmt, 2); - utxo->is_p2sh = sqlite3_column_int(stmt, 3) == p2sh_wpkh; - utxo->status = sqlite3_column_int(stmt, 4); - utxo->keyindex = sqlite3_column_int(stmt, 5); - if (sqlite3_column_type(stmt, 6) != SQLITE_NULL) { + db_column_txid(stmt, 0, &utxo->txid); + utxo->outnum = db_column_int(stmt, 1); + db_column_amount_sat(stmt, 2, &utxo->amount); + utxo->is_p2sh = db_column_int(stmt, 3) == p2sh_wpkh; + utxo->status = db_column_int(stmt, 4); + utxo->keyindex = db_column_int(stmt, 5); + if (!db_column_is_null(stmt, 6)) { utxo->close_info = tal(utxo, struct unilateral_close_info); - utxo->close_info->channel_id = sqlite3_column_int64(stmt, 6); - sqlite3_column_node_id(stmt, 7, &utxo->close_info->peer_id); - sqlite3_column_pubkey(stmt, 8, &utxo->close_info->commitment_point); + utxo->close_info->channel_id = db_column_u64(stmt, 6); + db_column_node_id(stmt, 7, &utxo->close_info->peer_id); + db_column_pubkey(stmt, 8, &utxo->close_info->commitment_point); } else { utxo->close_info = NULL; } @@ -151,22 +164,22 @@ static struct utxo *wallet_stmt2output(const tal_t *ctx, sqlite3_stmt *stmt) utxo->spendheight = NULL; utxo->scriptPubkey = NULL; - if (sqlite3_column_type(stmt, 9) != SQLITE_NULL) { + if (!db_column_is_null(stmt, 9)) { blockheight = tal(utxo, u32); - *blockheight = sqlite3_column_int(stmt, 9); + *blockheight = db_column_int(stmt, 9); utxo->blockheight = blockheight; } - if (sqlite3_column_type(stmt, 10) != SQLITE_NULL) { + if (!db_column_is_null(stmt, 10)) { spendheight = tal(utxo, u32); - *spendheight = sqlite3_column_int(stmt, 10); + *spendheight = db_column_int(stmt, 10); utxo->spendheight = spendheight; } - if (sqlite3_column_type(stmt, 11) != SQLITE_NULL) { + if (!db_column_is_null(stmt, 11)) { utxo->scriptPubkey = - tal_dup_arr(utxo, u8, sqlite3_column_blob(stmt, 11), - sqlite3_column_bytes(stmt, 11), 0); + tal_dup_arr(utxo, u8, db_column_blob(stmt, 11), + db_column_bytes(stmt, 11), 0); } return utxo; @@ -177,62 +190,112 @@ bool wallet_update_output_status(struct wallet *w, const u32 outnum, enum output_status oldstatus, enum output_status newstatus) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; + size_t changes; if (oldstatus != output_state_any) { - stmt = db_prepare( - w->db, "UPDATE outputs SET status=? WHERE status=? AND prev_out_tx=? AND prev_out_index=?"); - sqlite3_bind_int(stmt, 1, output_status_in_db(newstatus)); - sqlite3_bind_int(stmt, 2, output_status_in_db(oldstatus)); - sqlite3_bind_blob(stmt, 3, txid, sizeof(*txid), SQLITE_TRANSIENT); - sqlite3_bind_int(stmt, 4, outnum); + stmt = db_prepare_v2( + w->db, SQL("UPDATE outputs SET status=? WHERE status=? AND " + "prev_out_tx=? AND prev_out_index=?")); + db_bind_int(stmt, 0, output_status_in_db(newstatus)); + db_bind_int(stmt, 1, output_status_in_db(oldstatus)); + db_bind_txid(stmt, 2, txid); + db_bind_int(stmt, 3, outnum); } else { - stmt = db_prepare( - w->db, "UPDATE outputs SET status=? WHERE prev_out_tx=? AND prev_out_index=?"); - sqlite3_bind_int(stmt, 1, output_status_in_db(newstatus)); - sqlite3_bind_blob(stmt, 2, txid, sizeof(*txid), SQLITE_TRANSIENT); - sqlite3_bind_int(stmt, 3, outnum); + stmt = db_prepare_v2(w->db, + SQL("UPDATE outputs SET status=? WHERE " + "prev_out_tx=? AND prev_out_index=?")); + db_bind_int(stmt, 0, output_status_in_db(newstatus)); + db_bind_txid(stmt, 1, txid); + db_bind_int(stmt, 2, outnum); } - db_exec_prepared(w->db, stmt); - return sqlite3_changes(w->db->sql) > 0; + db_exec_prepared_v2(stmt); + changes = db_count_changes(stmt); + tal_free(stmt); + return changes > 0; } struct utxo **wallet_get_utxos(const tal_t *ctx, struct wallet *w, const enum output_status state) { struct utxo **results; int i; - sqlite3_stmt *stmt; - - if (state == output_state_any) - stmt = db_select_prepare(w->db, UTXO_FIELDS " FROM outputs"); - else { - stmt = db_select_prepare(w->db, UTXO_FIELDS - " FROM outputs WHERE status=?1"); - sqlite3_bind_int(stmt, 1, output_status_in_db(state)); - } + struct db_stmt *stmt; + + if (state == output_state_any) { + stmt = db_prepare_v2(w->db, SQL("SELECT" + " prev_out_tx" + ", prev_out_index" + ", value" + ", type" + ", status" + ", keyindex" + ", channel_id" + ", peer_id" + ", commitment_point" + ", confirmation_height" + ", spend_height" + ", scriptpubkey " + "FROM outputs")); + } else { + stmt = db_prepare_v2(w->db, SQL("SELECT" + " prev_out_tx" + ", prev_out_index" + ", value" + ", type" + ", status" + ", keyindex" + ", channel_id" + ", peer_id" + ", commitment_point" + ", confirmation_height" + ", spend_height" + ", scriptpubkey " + "FROM outputs " + "WHERE status= ? ")); + db_bind_int(stmt, 0, output_status_in_db(state)); + } + db_query_prepared(stmt); results = tal_arr(ctx, struct utxo*, 0); - for (i=0; db_select_step(w->db, stmt); i++) { + for (i=0; db_step(stmt); i++) { struct utxo *u = wallet_stmt2output(results, stmt); tal_arr_expand(&results, u); } + tal_free(stmt); return results; } -struct utxo **wallet_get_unconfirmed_closeinfo_utxos(const tal_t *ctx, struct wallet *w) +struct utxo **wallet_get_unconfirmed_closeinfo_utxos(const tal_t *ctx, + struct wallet *w) { + struct db_stmt *stmt; struct utxo **results; int i; - sqlite3_stmt *stmt = db_select_prepare( - w->db, UTXO_FIELDS - " FROM outputs WHERE channel_id IS NOT NULL and confirmation_height IS NULL"); - - results = tal_arr(ctx, struct utxo*, 0); - for (i=0; db_select_step(w->db, stmt); i++) { + stmt = db_prepare_v2(w->db, SQL("SELECT" + " prev_out_tx" + ", prev_out_index" + ", value" + ", type" + ", status" + ", keyindex" + ", channel_id" + ", peer_id" + ", commitment_point" + ", confirmation_height" + ", spend_height" + ", scriptpubkey" + " FROM outputs" + " WHERE channel_id IS NOT NULL AND " + "confirmation_height IS NULL")); + db_query_prepared(stmt); + + results = tal_arr(ctx, struct utxo *, 0); + for (i = 0; db_step(stmt); i++) { struct utxo *u = wallet_stmt2output(results, stmt); tal_arr_expand(&results, u); } + tal_free(stmt); return results; } @@ -488,17 +551,20 @@ s64 wallet_get_newindex(struct lightningd *ld) static void wallet_shachain_init(struct wallet *wallet, struct wallet_shachain *chain) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; assert(chain->id == 0); /* Create shachain */ shachain_init(&chain->chain); - stmt = db_prepare(wallet->db, "INSERT INTO shachains (min_index, num_valid) VALUES (?, 0);"); - sqlite3_bind_int64(stmt, 1, chain->chain.min_index); - db_exec_prepared(wallet->db, stmt); + stmt = db_prepare_v2( + wallet->db, + SQL("INSERT INTO shachains (min_index, num_valid) VALUES (?, 0);")); + db_bind_u64(stmt, 0, chain->chain.min_index); + db_exec_prepared_v2(stmt); - chain->id = sqlite3_last_insert_rowid(wallet->db->sql); + chain->id = db_last_insert_id_v2(stmt); + tal_free(stmt); } /* TODO(cdecker) Stolen from shachain, move to some appropriate location */ @@ -522,7 +588,7 @@ bool wallet_shachain_add_hash(struct wallet *wallet, uint64_t index, const struct secret *hash) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; u32 pos = count_trailing_zeroes(index); struct sha256 s; @@ -534,20 +600,22 @@ bool wallet_shachain_add_hash(struct wallet *wallet, return false; } - stmt = db_prepare(wallet->db, "UPDATE shachains SET num_valid=?, min_index=? WHERE id=?"); - sqlite3_bind_int(stmt, 1, chain->chain.num_valid); - sqlite3_bind_int64(stmt, 2, index); - sqlite3_bind_int64(stmt, 3, chain->id); - db_exec_prepared(wallet->db, stmt); - - stmt = db_prepare( - wallet->db, - "REPLACE INTO shachain_known (shachain_id, pos, idx, hash) VALUES (?, ?, ?, ?);"); - sqlite3_bind_int64(stmt, 1, chain->id); - sqlite3_bind_int(stmt, 2, pos); - sqlite3_bind_int64(stmt, 3, index); - sqlite3_bind_blob(stmt, 4, hash, sizeof(*hash), SQLITE_TRANSIENT); - db_exec_prepared(wallet->db, stmt); + stmt = db_prepare_v2( + wallet->db, + SQL("UPDATE shachains SET num_valid=?, min_index=? WHERE id=?")); + db_bind_int(stmt, 0, chain->chain.num_valid); + db_bind_u64(stmt, 1, index); + db_bind_u64(stmt, 2, chain->id); + db_exec_prepared_v2(take(stmt)); + + stmt = db_prepare_v2(wallet->db, + SQL("REPLACE INTO shachain_known (shachain_id, " + "pos, idx, hash) VALUES (?, ?, ?, ?);")); + db_bind_u64(stmt, 0, chain->id); + db_bind_int(stmt, 1, pos); + db_bind_u64(stmt, 2, index); + db_bind_secret(stmt, 3, hash); + db_exec_prepared_v2(take(stmt)); return true; } @@ -555,76 +623,91 @@ bool wallet_shachain_add_hash(struct wallet *wallet, bool wallet_shachain_load(struct wallet *wallet, u64 id, struct wallet_shachain *chain) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; chain->id = id; shachain_init(&chain->chain); /* Load shachain metadata */ - stmt = db_select_prepare(wallet->db, "min_index, num_valid FROM shachains WHERE id=?"); - sqlite3_bind_int64(stmt, 1, id); + stmt = db_prepare_v2( + wallet->db, + SQL("SELECT min_index, num_valid FROM shachains WHERE id=?")); + db_bind_u64(stmt, 0, id); + db_query_prepared(stmt); - if (!db_select_step(wallet->db, stmt)) + if (!db_step(stmt)) { + tal_free(stmt); return false; + } - chain->chain.min_index = sqlite3_column_int64(stmt, 0); - chain->chain.num_valid = sqlite3_column_int64(stmt, 1); - db_stmt_done(stmt); + chain->chain.min_index = db_column_u64(stmt, 0); + chain->chain.num_valid = db_column_u64(stmt, 1); + tal_free(stmt); /* Load shachain known entries */ - stmt = db_select_prepare(wallet->db, "idx, hash, pos FROM shachain_known WHERE shachain_id=?"); - sqlite3_bind_int64(stmt, 1, id); - - while (db_select_step(wallet->db, stmt)) { - int pos = sqlite3_column_int(stmt, 2); - chain->chain.known[pos].index = sqlite3_column_int64(stmt, 0); - memcpy(&chain->chain.known[pos].hash, sqlite3_column_blob(stmt, 1), sqlite3_column_bytes(stmt, 1)); - } + stmt = db_prepare_v2(wallet->db, + SQL("SELECT idx, hash, pos FROM shachain_known " + "WHERE shachain_id=?")); + db_bind_u64(stmt, 0, id); + db_query_prepared(stmt); + + while (db_step(stmt)) { + int pos = db_column_int(stmt, 2); + chain->chain.known[pos].index = db_column_u64(stmt, 0); + db_column_sha256(stmt, 1, &chain->chain.known[pos].hash); + } + tal_free(stmt); return true; } static struct peer *wallet_peer_load(struct wallet *w, const u64 dbid) { const unsigned char *addrstr; - struct peer *peer; + struct peer *peer = NULL; struct node_id id; struct wireaddr_internal addr; + struct db_stmt *stmt; - sqlite3_stmt *stmt = - db_select(w->db, - "id, node_id, address FROM peers WHERE id=%"PRIu64";", dbid); + stmt = db_prepare_v2( + w->db, SQL("SELECT id, node_id, address FROM peers WHERE id=?;")); + db_bind_u64(stmt, 0, dbid); + db_query_prepared(stmt); - if (!db_select_step(w->db, stmt)) - return NULL; + if (!db_step(stmt)) + goto done; - if (!sqlite3_column_node_id(stmt, 1, &id)) { - db_stmt_done(stmt); - return NULL; - } - addrstr = sqlite3_column_text(stmt, 2); - if (!parse_wireaddr_internal((const char*)addrstr, &addr, DEFAULT_PORT, false, false, true, NULL)) { - db_stmt_done(stmt); - return NULL; - } + if (db_column_is_null(stmt, 1)) + goto done; + + db_column_node_id(stmt, 1, &id); - peer = new_peer(w->ld, sqlite3_column_int64(stmt, 0), - &id, &addr); - db_stmt_done(stmt); + addrstr = db_column_text(stmt, 2); + if (!parse_wireaddr_internal((const char*)addrstr, &addr, DEFAULT_PORT, false, false, true, NULL)) + goto done; + peer = new_peer(w->ld, db_column_u64(stmt, 0), &id, &addr); + +done: + tal_free(stmt); return peer; } static secp256k1_ecdsa_signature * wallet_htlc_sigs_load(const tal_t *ctx, struct wallet *w, u64 channelid) { - sqlite3_stmt *stmt = db_select_prepare(w->db, "signature FROM htlc_sigs WHERE channelid = ?"); + struct db_stmt *stmt; + + stmt = db_prepare_v2( + w->db, SQL("SELECT signature FROM htlc_sigs WHERE channelid = ?")); secp256k1_ecdsa_signature *htlc_sigs = tal_arr(ctx, secp256k1_ecdsa_signature, 0); - sqlite3_bind_int64(stmt, 1, channelid); + db_bind_u64(stmt, 0, channelid); + db_query_prepared(stmt); - while (db_select_step(w->db, stmt)) { + while (db_step(stmt)) { secp256k1_ecdsa_signature sig; - sqlite3_column_signature(stmt, 0, &sig); + db_column_signature(stmt, 0, &sig); tal_arr_expand(&htlc_sigs, sig); } + tal_free(stmt); log_debug(w->log, "Loaded %zu HTLC signatures from DB", tal_count(htlc_sigs)); @@ -635,23 +718,23 @@ bool wallet_remote_ann_sigs_load(const tal_t *ctx, struct wallet *w, u64 id, secp256k1_ecdsa_signature **remote_ann_node_sig, secp256k1_ecdsa_signature **remote_ann_bitcoin_sig) { - sqlite3_stmt *stmt; - int res; - stmt = db_select_prepare(w->db, - "remote_ann_node_sig, remote_ann_bitcoin_sig" - " FROM channels WHERE id = ?"); - sqlite3_bind_int64(stmt, 1, id); + struct db_stmt *stmt; + bool res; + stmt = db_prepare_v2( + w->db, SQL("SELECT remote_ann_node_sig, remote_ann_bitcoin_sig" + " FROM channels WHERE id = ?")); + db_bind_u64(stmt, 0, id); + db_query_prepared(stmt); - res = sqlite3_step(stmt); + res = db_step(stmt); /* This must succeed, since we know the channel exists */ - assert(res == SQLITE_ROW); + assert(res); /* if only one sig exists, forget the sig and hope peer send new ones*/ - if(sqlite3_column_type(stmt, 0) == SQLITE_NULL || - sqlite3_column_type(stmt, 1) == SQLITE_NULL) { + if (db_column_is_null(stmt, 0) || db_column_is_null(stmt, 1)) { *remote_ann_node_sig = *remote_ann_bitcoin_sig = NULL; - db_stmt_done(stmt); + tal_free(stmt); return true; } @@ -659,26 +742,26 @@ bool wallet_remote_ann_sigs_load(const tal_t *ctx, struct wallet *w, u64 id, *remote_ann_node_sig = tal(ctx, secp256k1_ecdsa_signature); *remote_ann_bitcoin_sig = tal(ctx, secp256k1_ecdsa_signature); - if (!sqlite3_column_signature(stmt, 0, *remote_ann_node_sig)) + if (!db_column_signature(stmt, 0, *remote_ann_node_sig)) goto fail; - if (!sqlite3_column_signature(stmt, 1, *remote_ann_bitcoin_sig)) + if (!db_column_signature(stmt, 1, *remote_ann_bitcoin_sig)) goto fail; - db_stmt_done(stmt); + tal_free(stmt); return true; fail: *remote_ann_node_sig = tal_free(*remote_ann_node_sig); *remote_ann_bitcoin_sig = tal_free(*remote_ann_bitcoin_sig); - db_stmt_done(stmt); + tal_free(stmt); return false; } /** - * wallet_stmt2channel - Helper to populate a wallet_channel from a sqlite3_stmt + * wallet_stmt2channel - Helper to populate a wallet_channel from a `db_stmt` */ -static struct channel *wallet_stmt2channel(struct wallet *w, sqlite3_stmt *stmt) +static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stmt) { bool ok = true; struct channel_info channel_info; @@ -692,12 +775,14 @@ static struct channel *wallet_stmt2channel(struct wallet *w, sqlite3_stmt *stmt) struct bitcoin_signature last_sig; u8 *remote_shutdown_scriptpubkey; struct changed_htlc *last_sent_commit; - s64 final_key_idx; + s64 final_key_idx, channel_config_id; struct basepoints local_basepoints; struct pubkey local_funding_pubkey; struct pubkey *future_per_commitment_point; + struct amount_sat funding_sat; + struct amount_msat push_msat, our_msat, msat_to_us_min, msat_to_us_max; - peer_dbid = sqlite3_column_int64(stmt, 1); + peer_dbid = db_column_u64(stmt, 1); peer = find_peer_by_dbid(w->ld, peer_dbid); if (!peer) { peer = wallet_peer_load(w, peer_dbid); @@ -706,23 +791,22 @@ static struct channel *wallet_stmt2channel(struct wallet *w, sqlite3_stmt *stmt) } } - if (sqlite3_column_type(stmt, 2) != SQLITE_NULL) { + if (!db_column_is_null(stmt, 2)) { scid = tal(tmpctx, struct short_channel_id); - if (!sqlite3_column_short_channel_id(stmt, 2, scid)) + if (!db_column_short_channel_id(stmt, 2, scid)) return NULL; } else { scid = NULL; } - ok &= wallet_shachain_load(w, sqlite3_column_int64(stmt, 27), - &wshachain); + ok &= wallet_shachain_load(w, db_column_u64(stmt, 27), &wshachain); - remote_shutdown_scriptpubkey = sqlite3_column_arr(tmpctx, stmt, 28, u8); + remote_shutdown_scriptpubkey = db_column_arr(tmpctx, stmt, 28, u8); /* Do we have a last_sent_commit, if yes, populate */ - if (sqlite3_column_type(stmt, 41) != SQLITE_NULL) { - const u8 *cursor = sqlite3_column_blob(stmt, 41); - size_t len = sqlite3_column_bytes(stmt, 41); + if (!db_column_is_null(stmt, 41)) { + const u8 *cursor = db_column_blob(stmt, 41); + size_t len = db_column_bytes(stmt, 41); size_t n = 0; last_sent_commit = tal_arr(tmpctx, struct changed_htlc, n); while (len) { @@ -734,154 +818,182 @@ static struct channel *wallet_stmt2channel(struct wallet *w, sqlite3_stmt *stmt) last_sent_commit = NULL; #ifdef COMPAT_V060 - if (!last_sent_commit && sqlite3_column_type(stmt, 30) != SQLITE_NULL) { + if (!last_sent_commit && !db_column_is_null(stmt, 30)) { last_sent_commit = tal(tmpctx, struct changed_htlc); - last_sent_commit->newstate = sqlite3_column_int64(stmt, 30); - last_sent_commit->id = sqlite3_column_int64(stmt, 31); + last_sent_commit->newstate = db_column_u64(stmt, 30); + last_sent_commit->id = db_column_u64(stmt, 31); } #endif - if (sqlite3_column_type(stmt, 40) != SQLITE_NULL) { + if (!db_column_is_null(stmt, 40)) { future_per_commitment_point = tal(tmpctx, struct pubkey); - ok &= sqlite3_column_pubkey(stmt, 40, - future_per_commitment_point); + db_column_pubkey(stmt, 40, future_per_commitment_point); } else future_per_commitment_point = NULL; - ok &= wallet_channel_config_load(w, sqlite3_column_int64(stmt, 3), - &our_config); - ok &= sqlite3_column_sha256_double(stmt, 12, &funding_txid.shad); - - ok &= sqlite3_column_signature(stmt, 33, &last_sig.s); + channel_config_id = db_column_u64(stmt, 3); + ok &= wallet_channel_config_load(w, channel_config_id, &our_config); + db_column_sha256d(stmt, 12, &funding_txid.shad); + ok &= db_column_signature(stmt, 33, &last_sig.s); last_sig.sighash_type = SIGHASH_ALL; /* Populate channel_info */ - ok &= sqlite3_column_pubkey(stmt, 18, &channel_info.remote_fundingkey); - ok &= sqlite3_column_pubkey(stmt, 19, &channel_info.theirbase.revocation); - ok &= sqlite3_column_pubkey(stmt, 20, &channel_info.theirbase.payment); - ok &= sqlite3_column_pubkey(stmt, 21, &channel_info.theirbase.htlc); - ok &= sqlite3_column_pubkey(stmt, 22, &channel_info.theirbase.delayed_payment); - ok &= sqlite3_column_pubkey(stmt, 23, &channel_info.remote_per_commit); - ok &= sqlite3_column_pubkey(stmt, 24, &channel_info.old_remote_per_commit); - channel_info.feerate_per_kw[LOCAL] = sqlite3_column_int(stmt, 25); - channel_info.feerate_per_kw[REMOTE] = sqlite3_column_int(stmt, 26); - - wallet_channel_config_load(w, sqlite3_column_int64(stmt, 4), + db_column_pubkey(stmt, 18, &channel_info.remote_fundingkey); + db_column_pubkey(stmt, 19, &channel_info.theirbase.revocation); + db_column_pubkey(stmt, 20, &channel_info.theirbase.payment); + db_column_pubkey(stmt, 21, &channel_info.theirbase.htlc); + db_column_pubkey(stmt, 22, &channel_info.theirbase.delayed_payment); + db_column_pubkey(stmt, 23, &channel_info.remote_per_commit); + db_column_pubkey(stmt, 24, &channel_info.old_remote_per_commit); + channel_info.feerate_per_kw[LOCAL] = db_column_int(stmt, 25); + channel_info.feerate_per_kw[REMOTE] = db_column_int(stmt, 26); + + wallet_channel_config_load(w, db_column_u64(stmt, 4), &channel_info.their_config); if (!ok) { return NULL; } - final_key_idx = sqlite3_column_int64(stmt, 29); + final_key_idx = db_column_u64(stmt, 29); if (final_key_idx < 0) { log_broken(w->log, "%s: Final key < 0", __func__); return NULL; } - get_channel_basepoints(w->ld, &peer->id, sqlite3_column_int64(stmt, 0), + get_channel_basepoints(w->ld, &peer->id, db_column_u64(stmt, 0), &local_basepoints, &local_funding_pubkey); - chan = new_channel(peer, sqlite3_column_int64(stmt, 0), + + db_column_amount_sat(stmt, 14, &funding_sat); + db_column_amount_msat(stmt, 16, &push_msat); + db_column_amount_msat(stmt, 17, &our_msat); + db_column_amount_msat(stmt, 38, &msat_to_us_min); + db_column_amount_msat(stmt, 39, &msat_to_us_max); + + chan = new_channel(peer, db_column_u64(stmt, 0), &wshachain, - sqlite3_column_int(stmt, 5), - sqlite3_column_int(stmt, 6), + db_column_int(stmt, 5), + db_column_int(stmt, 6), NULL, /* Set up fresh log */ "Loaded from database", - sqlite3_column_int(stmt, 7), + db_column_int(stmt, 7), &our_config, - sqlite3_column_int(stmt, 8), - sqlite3_column_int64(stmt, 9), - sqlite3_column_int64(stmt, 10), - sqlite3_column_int64(stmt, 11), + db_column_int(stmt, 8), + db_column_u64(stmt, 9), + db_column_u64(stmt, 10), + db_column_u64(stmt, 11), &funding_txid, - sqlite3_column_int(stmt, 13), - sqlite3_column_amount_sat(stmt, 14), - sqlite3_column_amount_msat(stmt, 16), - sqlite3_column_int(stmt, 15) != 0, + db_column_int(stmt, 13), + funding_sat, + push_msat, + db_column_int(stmt, 15) != 0, scid, - sqlite3_column_amount_msat(stmt, 17), - sqlite3_column_amount_msat(stmt, 38), /* msatoshi_to_us_min */ - sqlite3_column_amount_msat(stmt, 39), /* msatoshi_to_us_max */ - sqlite3_column_tx(tmpctx, stmt, 32), + our_msat, + msat_to_us_min, /* msatoshi_to_us_min */ + msat_to_us_max, /* msatoshi_to_us_max */ + db_column_tx(tmpctx, stmt, 32), &last_sig, wallet_htlc_sigs_load(tmpctx, w, - sqlite3_column_int64(stmt, 0)), + db_column_u64(stmt, 0)), &channel_info, remote_shutdown_scriptpubkey, final_key_idx, - sqlite3_column_int(stmt, 34) != 0, + db_column_int(stmt, 34) != 0, last_sent_commit, - sqlite3_column_int64(stmt, 35), - sqlite3_column_int(stmt, 36), - sqlite3_column_int(stmt, 37), + db_column_u64(stmt, 35), + db_column_int(stmt, 36), + db_column_int(stmt, 37), /* Not connected */ false, &local_basepoints, &local_funding_pubkey, future_per_commitment_point, - sqlite3_column_int(stmt, 42), - sqlite3_column_int(stmt, 43), - sqlite3_column_arr(tmpctx, stmt, 44, u8)); + db_column_int(stmt, 42), + db_column_int(stmt, 43), + db_column_arr(tmpctx, stmt, 44, u8)); return chan; } -/* List of fields to retrieve from the channels DB table, in the order - * that wallet_stmt2channel understands and will parse correctly */ -/* Numbers below are sqlite3_column indices for the first field - * of that line. */ -static const char *channel_fields = - /*0*/ "id, peer_id, short_channel_id, channel_config_local, " - /*4*/ "channel_config_remote, state, funder, channel_flags, " - /*8*/ "minimum_depth, " - /*9*/ "next_index_local, next_index_remote, " - /*11*/ "next_htlc_id, funding_tx_id, funding_tx_outnum, funding_satoshi, " - /*15*/ "funding_locked_remote, push_msatoshi, msatoshi_local, " - /*18*/ "fundingkey_remote, revocation_basepoint_remote, " - /*20*/ "payment_basepoint_remote, htlc_basepoint_remote, " - /*22*/ "delayed_payment_basepoint_remote, per_commit_remote, " - /*24*/ "old_per_commit_remote, local_feerate_per_kw, remote_feerate_per_kw, shachain_remote_id, " - /*28*/ "shutdown_scriptpubkey_remote, shutdown_keyidx_local, " - /*30*/ "last_sent_commit_state, last_sent_commit_id, " - /*32*/ "last_tx, last_sig, last_was_revoke, first_blocknum, " - /*36*/ "min_possible_feerate, max_possible_feerate, " - /*38*/ "msatoshi_to_us_min, msatoshi_to_us_max, future_per_commitment_point, " - /*41*/ "last_sent_commit, " - /*42*/ "feerate_base, feerate_ppm, remote_upfront_shutdown_script"; - static void set_max_channel_dbid(struct wallet *w) { - sqlite3_stmt *stmt; - int result; + struct db_stmt *stmt; - stmt = db_select(w->db, "id FROM channels ORDER BY id DESC LIMIT 1;"); + stmt = db_prepare_v2(w->db, SQL("SELECT id FROM channels ORDER BY id DESC LIMIT 1;")); + db_query_prepared(stmt); w->max_channel_dbid = 0; - result = sqlite3_step(stmt); - if (result == SQLITE_ROW) - w->max_channel_dbid = sqlite3_column_int64(stmt, 0); + if (db_step(stmt)) + w->max_channel_dbid = db_column_u64(stmt, 0); - db_stmt_done(stmt); + tal_free(stmt); } static bool wallet_channels_load_active(struct wallet *w) { bool ok = true; - sqlite3_stmt *stmt; - - /* We load all non-closed channels */ - stmt = db_select(w->db, "%s FROM channels WHERE state < %d;", channel_fields, CLOSED); - + struct db_stmt *stmt; int count = 0; - while (db_select_step(w->db, stmt)) { + + /* We load all channels */ + stmt = db_prepare_v2(w->db, SQL("SELECT" + " id" + ", peer_id" + ", short_channel_id" + ", channel_config_local" + ", channel_config_remote" + ", 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" + ", fundingkey_remote" + ", revocation_basepoint_remote" + ", payment_basepoint_remote" + ", htlc_basepoint_remote" + ", delayed_payment_basepoint_remote" + ", per_commit_remote" + ", old_per_commit_remote" + ", local_feerate_per_kw" + ", 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" + ", last_was_revoke" + ", first_blocknum" + ", min_possible_feerate" + ", max_possible_feerate" + ", msatoshi_to_us_min" + ", msatoshi_to_us_max" + ", future_per_commitment_point" + ", last_sent_commit" + ", feerate_base" + ", feerate_ppm" + ", remote_upfront_shutdown_script" + " FROM channels WHERE state < ?;")); + db_bind_int(stmt, 0, CLOSED); + db_query_prepared(stmt); + + while (db_step(stmt)) { struct channel *c = wallet_stmt2channel(w, stmt); if (!c) { ok = false; - db_stmt_done(stmt); break; } count++; } log_debug(w->log, "Loaded %d channels from DB", count); - + tal_free(stmt); return ok; } @@ -900,20 +1012,39 @@ void wallet_channel_stats_incr_x(struct wallet *w, u64 cdbid, struct amount_msat msat) { - char const *payments_stat = tal_fmt(tmpctx, "%s_payments_%s", - dir, typ); - char const *msatoshi_stat = tal_fmt(tmpctx, "%s_msatoshi_%s", - dir, typ); - char const *qry = tal_fmt(tmpctx, - "UPDATE channels" - " SET %s = COALESCE(%s, 0) + 1" - " , %s = COALESCE(%s, 0) + %"PRIu64"" - " WHERE id = %"PRIu64";", - payments_stat, payments_stat, - msatoshi_stat, msatoshi_stat, msat.millisatoshis, /* Raw: db access */ - cdbid); - sqlite3_stmt *stmt = db_prepare(w->db, qry); - db_exec_prepared(w->db, stmt); + struct db_stmt *stmt; + const char *query; + /* TODO These would be much better as a switch statement, leaving + * these here for now in order to keep the commit clean. */ + if (streq(dir, "in") && streq(typ, "offered")) { + query = SQL("UPDATE channels" + " SET in_payments_offered = COALESCE(in_payments_offered, 0) + 1" + " , in_msatoshi_offered = COALESCE(in_msatoshi_offered, 0) + ?" + " WHERE id = ?;"); + } else if (streq(dir, "in") && streq(typ, "fulfilled")) { + query = SQL("UPDATE channels" + " SET in_payments_fulfilled = COALESCE(in_payments_fulfilled, 0) + 1" + " , in_msatoshi_fulfilled = COALESCE(in_msatoshi_fulfilled, 0) + ?" + " WHERE id = ?;"); + } else if (streq(dir, "out") && streq(typ, "offered")) { + query = SQL("UPDATE channels" + " SET out_payments_offered = COALESCE(out_payments_offered, 0) + 1" + " , out_msatoshi_offered = COALESCE(out_msatoshi_offered, 0) + ?" + " WHERE id = ?;"); + } else if (streq(dir, "out") && streq(typ, "fulfilled")) { + query = SQL("UPDATE channels" + " SET out_payments_fulfilled = COALESCE(out_payments_fulfilled, 0) + 1" + " , out_msatoshi_fulfilled = COALESCE(out_msatoshi_fulfilled, 0) + ?" + " WHERE id = ?;"); + } else { + fatal("Unknown stats key %s %s", dir, typ); + } + + stmt = db_prepare_v2(w->db, query); + db_bind_amount_msat(stmt, 0, &msat); + db_bind_u64(stmt, 1, cdbid); + + db_exec_prepared_v2(take(stmt)); } void wallet_channel_stats_incr_in_offered(struct wallet *w, u64 id, struct amount_msat m) @@ -940,85 +1071,88 @@ void wallet_channel_stats_load(struct wallet *w, u64 id, struct channel_stats *stats) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; int res; - stmt = db_select_prepare(w->db, - " in_payments_offered, in_payments_fulfilled" - ", in_msatoshi_offered, in_msatoshi_fulfilled" - ", out_payments_offered, out_payments_fulfilled" - ", out_msatoshi_offered, out_msatoshi_fulfilled" - " FROM channels" - " WHERE id = ?"); - sqlite3_bind_int64(stmt, 1, id); - - res = sqlite3_step(stmt); + stmt = db_prepare_v2(w->db, SQL( + "SELECT" + " in_payments_offered, in_payments_fulfilled" + ", in_msatoshi_offered, in_msatoshi_fulfilled" + ", out_payments_offered, out_payments_fulfilled" + ", out_msatoshi_offered, out_msatoshi_fulfilled" + " FROM channels" + " WHERE id = ?")); + db_bind_u64(stmt, 0, id); + db_query_prepared(stmt); + + res = db_step(stmt); /* This must succeed, since we know the channel exists */ - assert(res == SQLITE_ROW); + assert(res); - stats->in_payments_offered = sqlite3_column_int64(stmt, 0); - stats->in_payments_fulfilled = sqlite3_column_int64(stmt, 1); - stats->in_msatoshi_offered = sqlite3_column_amount_msat(stmt, 2); - stats->in_msatoshi_fulfilled = sqlite3_column_amount_msat(stmt, 3); - stats->out_payments_offered = sqlite3_column_int64(stmt, 4); - stats->out_payments_fulfilled = sqlite3_column_int64(stmt, 5); - stats->out_msatoshi_offered = sqlite3_column_amount_msat(stmt, 6); - stats->out_msatoshi_fulfilled = sqlite3_column_amount_msat(stmt, 7); - db_stmt_done(stmt); + stats->in_payments_offered = db_column_u64(stmt, 0); + stats->in_payments_fulfilled = db_column_u64(stmt, 1); + db_column_amount_msat(stmt, 2, &stats->in_msatoshi_offered); + db_column_amount_msat(stmt, 3, &stats->in_msatoshi_fulfilled); + stats->out_payments_offered = db_column_u64(stmt, 4); + stats->out_payments_fulfilled = db_column_u64(stmt, 5); + db_column_amount_msat(stmt, 6, &stats->out_msatoshi_offered); + db_column_amount_msat(stmt, 7, &stats->out_msatoshi_fulfilled); + tal_free(stmt); } void wallet_blocks_heights(struct wallet *w, u32 def, u32 *min, u32 *max) { assert(min != NULL && max != NULL); - sqlite3_stmt *stmt = db_select_prepare(w->db, "MIN(height), MAX(height) FROM blocks;"); - + struct db_stmt *stmt = db_prepare_v2(w->db, SQL("SELECT MIN(height), MAX(height) FROM blocks;")); + db_query_prepared(stmt); *min = def; *max = def; /* If we ever processed a block we'll get the latest block in the chain */ - if (db_select_step(w->db, stmt)) { - if (sqlite3_column_type(stmt, 0) != SQLITE_NULL) { - *min = sqlite3_column_int(stmt, 0); - *max = sqlite3_column_int(stmt, 1); + if (db_step(stmt)) { + if (!db_column_is_null(stmt, 0)) { + *min = db_column_int(stmt, 0); + *max = db_column_int(stmt, 1); } - db_stmt_done(stmt); } + tal_free(stmt); } static void wallet_channel_config_insert(struct wallet *w, struct channel_config *cc) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; assert(cc->id == 0); - stmt = db_prepare(w->db, "INSERT INTO channel_configs DEFAULT VALUES;"); - db_exec_prepared(w->db, stmt); - cc->id = sqlite3_last_insert_rowid(w->db->sql); + stmt = db_prepare_v2(w->db, SQL("INSERT INTO channel_configs DEFAULT VALUES;")); + db_exec_prepared_v2(stmt); + cc->id = db_last_insert_id_v2(stmt); + tal_free(stmt); } static void wallet_channel_config_save(struct wallet *w, const struct channel_config *cc) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; assert(cc->id != 0); - 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_amount_sat(stmt, 1, cc->dust_limit); - sqlite3_bind_amount_msat(stmt, 2, cc->max_htlc_value_in_flight); - sqlite3_bind_amount_sat(stmt, 3, cc->channel_reserve); - sqlite3_bind_amount_msat(stmt, 4, cc->htlc_minimum); - 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); - db_exec_prepared(w->db, stmt); + stmt = db_prepare_v2(w->db, SQL("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=?;")); + db_bind_amount_sat(stmt, 0, &cc->dust_limit); + db_bind_amount_msat(stmt, 1, &cc->max_htlc_value_in_flight); + db_bind_amount_sat(stmt, 2, &cc->channel_reserve); + db_bind_amount_msat(stmt, 3, &cc->htlc_minimum); + db_bind_int(stmt, 4, cc->to_self_delay); + db_bind_int(stmt, 5, cc->max_accepted_htlcs); + db_bind_u64(stmt, 6, cc->id); + db_exec_prepared_v2(take(stmt)); } bool wallet_channel_config_load(struct wallet *w, const u64 id, @@ -1026,23 +1160,26 @@ bool wallet_channel_config_load(struct wallet *w, const u64 id, { bool ok = true; int col = 1; - const char *query = - "id, dust_limit_satoshis, max_htlc_value_in_flight_msat, " + const char *query = SQL( + "SELECT id, dust_limit_satoshis, max_htlc_value_in_flight_msat, " "channel_reserve_satoshis, htlc_minimum_msat, to_self_delay, " - "max_accepted_htlcs FROM channel_configs WHERE id=%" PRIu64 ";"; - sqlite3_stmt *stmt = db_select(w->db, query, id); - if (!db_select_step(w->db, stmt)) + "max_accepted_htlcs FROM channel_configs WHERE id= ? ;"); + struct db_stmt *stmt = db_prepare_v2(w->db, query); + db_bind_u64(stmt, 0, id); + db_query_prepared(stmt); + + if (!db_step(stmt)) return false; cc->id = id; - cc->dust_limit = sqlite3_column_amount_sat(stmt, col++); - cc->max_htlc_value_in_flight = sqlite3_column_amount_msat(stmt, col++); - cc->channel_reserve = sqlite3_column_amount_sat(stmt, col++); - cc->htlc_minimum = sqlite3_column_amount_msat(stmt, col++); - cc->to_self_delay = sqlite3_column_int(stmt, col++); - cc->max_accepted_htlcs = sqlite3_column_int(stmt, col++); + db_column_amount_sat(stmt, col++, &cc->dust_limit); + db_column_amount_msat(stmt, col++, &cc->max_htlc_value_in_flight); + db_column_amount_sat(stmt, col++, &cc->channel_reserve); + db_column_amount_msat(stmt, col++, &cc->htlc_minimum); + cc->to_self_delay = db_column_int(stmt, col++); + cc->max_accepted_htlcs = db_column_int(stmt, col++); assert(col == 7); - db_stmt_done(stmt); + tal_free(stmt); return ok; } @@ -1056,135 +1193,134 @@ void wallet_announcement_save(struct wallet *w, u64 id, secp256k1_ecdsa_signature *remote_ann_node_sig, secp256k1_ecdsa_signature *remote_ann_bitcoin_sig) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; - stmt = db_prepare(w->db, "UPDATE channels SET" - " remote_ann_node_sig=?," - " remote_ann_bitcoin_sig=?" - " WHERE id=?"); + stmt = db_prepare_v2(w->db, SQL("UPDATE channels SET" + " remote_ann_node_sig=?," + " remote_ann_bitcoin_sig=?" + " WHERE id=?")); - sqlite3_bind_signature(stmt, 1, remote_ann_node_sig); - sqlite3_bind_signature(stmt, 2, remote_ann_bitcoin_sig); - sqlite3_bind_int64(stmt, 3, id); - db_exec_prepared(w->db, stmt); + db_bind_signature(stmt, 0, remote_ann_node_sig); + db_bind_signature(stmt, 1, remote_ann_bitcoin_sig); + db_bind_u64(stmt, 2, id); + db_exec_prepared_v2(take(stmt)); } void wallet_channel_save(struct wallet *w, struct channel *chan) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; u8 *last_sent_commit; assert(chan->first_blocknum); wallet_channel_config_save(w, &chan->our_config); - stmt = db_prepare(w->db, "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=?," - " last_was_revoke=?," - " min_possible_feerate=?," - " max_possible_feerate=?," - " msatoshi_to_us_min=?," - " msatoshi_to_us_max=?," - " feerate_base=?," - " feerate_ppm=?," - " remote_upfront_shutdown_script=?" - " WHERE id=?"); - sqlite3_bind_int64(stmt, 1, chan->their_shachain.id); + stmt = db_prepare_v2(w->db, SQL("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=?," + " last_was_revoke=?," + " min_possible_feerate=?," + " max_possible_feerate=?," + " msatoshi_to_us_min=?," + " msatoshi_to_us_max=?," + " feerate_base=?," + " feerate_ppm=?," + " remote_upfront_shutdown_script=?" + " WHERE id=?")); + db_bind_u64(stmt, 0, chan->their_shachain.id); if (chan->scid) - sqlite3_bind_short_channel_id(stmt, 2, chan->scid); + db_bind_short_channel_id(stmt, 1, chan->scid); else - sqlite3_bind_null(stmt, 2); - sqlite3_bind_int(stmt, 3, chan->state); - sqlite3_bind_int(stmt, 4, chan->funder); - sqlite3_bind_int(stmt, 5, chan->channel_flags); - sqlite3_bind_int(stmt, 6, chan->minimum_depth); + db_bind_null(stmt, 1); + db_bind_int(stmt, 2, chan->state); + db_bind_int(stmt, 3, chan->funder); + db_bind_int(stmt, 4, chan->channel_flags); + db_bind_int(stmt, 5, chan->minimum_depth); - sqlite3_bind_int64(stmt, 7, chan->next_index[LOCAL]); - sqlite3_bind_int64(stmt, 8, chan->next_index[REMOTE]); - sqlite3_bind_int64(stmt, 9, chan->next_htlc_id); + db_bind_u64(stmt, 6, chan->next_index[LOCAL]); + db_bind_u64(stmt, 7, chan->next_index[REMOTE]); + db_bind_u64(stmt, 8, chan->next_htlc_id); - sqlite3_bind_sha256_double(stmt, 10, &chan->funding_txid.shad); + db_bind_sha256d(stmt, 9, &chan->funding_txid.shad); - sqlite3_bind_int(stmt, 11, chan->funding_outnum); - sqlite3_bind_amount_sat(stmt, 12, chan->funding); - sqlite3_bind_int(stmt, 13, chan->remote_funding_locked); - sqlite3_bind_amount_msat(stmt, 14, chan->push); - sqlite3_bind_amount_msat(stmt, 15, chan->our_msat); + db_bind_int(stmt, 10, chan->funding_outnum); + db_bind_amount_sat(stmt, 11, &chan->funding); + db_bind_int(stmt, 12, chan->remote_funding_locked); + db_bind_amount_msat(stmt, 13, &chan->push); + db_bind_amount_msat(stmt, 14, &chan->our_msat); if (chan->remote_shutdown_scriptpubkey) - sqlite3_bind_blob(stmt, 16, chan->remote_shutdown_scriptpubkey, - tal_count(chan->remote_shutdown_scriptpubkey), - SQLITE_TRANSIENT); + db_bind_blob(stmt, 15, chan->remote_shutdown_scriptpubkey, + tal_count(chan->remote_shutdown_scriptpubkey)); else - sqlite3_bind_null(stmt, 16); - - sqlite3_bind_int64(stmt, 17, chan->final_key_idx); - sqlite3_bind_int64(stmt, 18, chan->our_config.id); - sqlite3_bind_tx(stmt, 19, chan->last_tx); - sqlite3_bind_signature(stmt, 20, &chan->last_sig.s); - sqlite3_bind_int(stmt, 21, chan->last_was_revoke); - sqlite3_bind_int(stmt, 22, chan->min_possible_feerate); - sqlite3_bind_int(stmt, 23, chan->max_possible_feerate); - sqlite3_bind_amount_msat(stmt, 24, chan->msat_to_us_min); - sqlite3_bind_amount_msat(stmt, 25, chan->msat_to_us_max); - sqlite3_bind_int(stmt, 26, chan->feerate_base); - sqlite3_bind_int(stmt, 27, chan->feerate_ppm); + db_bind_null(stmt, 15); + + db_bind_u64(stmt, 16, chan->final_key_idx); + db_bind_u64(stmt, 17, chan->our_config.id); + db_bind_tx(stmt, 18, chan->last_tx); + db_bind_signature(stmt, 19, &chan->last_sig.s); + db_bind_int(stmt, 20, chan->last_was_revoke); + db_bind_int(stmt, 21, chan->min_possible_feerate); + db_bind_int(stmt, 22, chan->max_possible_feerate); + db_bind_amount_msat(stmt, 23, &chan->msat_to_us_min); + db_bind_amount_msat(stmt, 24, &chan->msat_to_us_max); + db_bind_int(stmt, 25, chan->feerate_base); + db_bind_int(stmt, 26, chan->feerate_ppm); if (chan->remote_upfront_shutdown_script) - sqlite3_bind_blob(stmt, 28, chan->remote_upfront_shutdown_script, - tal_count(chan->remote_upfront_shutdown_script), - SQLITE_TRANSIENT); + db_bind_blob( + stmt, 27, chan->remote_upfront_shutdown_script, + tal_count(chan->remote_upfront_shutdown_script)); else - sqlite3_bind_null(stmt, 28); - sqlite3_bind_int64(stmt, 29, chan->dbid); - db_exec_prepared(w->db, stmt); + db_bind_null(stmt, 27); + db_bind_u64(stmt, 28, chan->dbid); + db_exec_prepared_v2(take(stmt)); wallet_channel_config_save(w, &chan->channel_info.their_config); - stmt = db_prepare(w->db, "UPDATE channels SET" - " fundingkey_remote=?," - " revocation_basepoint_remote=?," - " payment_basepoint_remote=?," - " htlc_basepoint_remote=?," - " delayed_payment_basepoint_remote=?," - " per_commit_remote=?," - " old_per_commit_remote=?," - " local_feerate_per_kw=?," - " remote_feerate_per_kw=?," - " channel_config_remote=?," - " future_per_commitment_point=?" - " WHERE id=?"); - sqlite3_bind_pubkey(stmt, 1, &chan->channel_info.remote_fundingkey); - sqlite3_bind_pubkey(stmt, 2, &chan->channel_info.theirbase.revocation); - sqlite3_bind_pubkey(stmt, 3, &chan->channel_info.theirbase.payment); - sqlite3_bind_pubkey(stmt, 4, &chan->channel_info.theirbase.htlc); - sqlite3_bind_pubkey(stmt, 5, &chan->channel_info.theirbase.delayed_payment); - sqlite3_bind_pubkey(stmt, 6, &chan->channel_info.remote_per_commit); - sqlite3_bind_pubkey(stmt, 7, &chan->channel_info.old_remote_per_commit); - sqlite3_bind_int(stmt, 8, chan->channel_info.feerate_per_kw[LOCAL]); - sqlite3_bind_int(stmt, 9, chan->channel_info.feerate_per_kw[REMOTE]); - sqlite3_bind_int64(stmt, 10, chan->channel_info.their_config.id); + stmt = db_prepare_v2(w->db, SQL("UPDATE channels SET" + " fundingkey_remote=?," + " revocation_basepoint_remote=?," + " payment_basepoint_remote=?," + " htlc_basepoint_remote=?," + " delayed_payment_basepoint_remote=?," + " per_commit_remote=?," + " old_per_commit_remote=?," + " local_feerate_per_kw=?," + " remote_feerate_per_kw=?," + " channel_config_remote=?," + " future_per_commitment_point=?" + " WHERE id=?")); + db_bind_pubkey(stmt, 0, &chan->channel_info.remote_fundingkey); + db_bind_pubkey(stmt, 1, &chan->channel_info.theirbase.revocation); + db_bind_pubkey(stmt, 2, &chan->channel_info.theirbase.payment); + db_bind_pubkey(stmt, 3, &chan->channel_info.theirbase.htlc); + db_bind_pubkey(stmt, 4, &chan->channel_info.theirbase.delayed_payment); + db_bind_pubkey(stmt, 5, &chan->channel_info.remote_per_commit); + db_bind_pubkey(stmt, 6, &chan->channel_info.old_remote_per_commit); + db_bind_int(stmt, 7, chan->channel_info.feerate_per_kw[LOCAL]); + db_bind_int(stmt, 8, chan->channel_info.feerate_per_kw[REMOTE]); + db_bind_u64(stmt, 9, chan->channel_info.their_config.id); if (chan->future_per_commitment_point) - sqlite3_bind_pubkey(stmt, 11, chan->future_per_commitment_point); + db_bind_pubkey(stmt, 10, chan->future_per_commitment_point); else - sqlite3_bind_null(stmt, 11); - sqlite3_bind_int64(stmt, 12, chan->dbid); - db_exec_prepared(w->db, stmt); + db_bind_null(stmt, 10); + db_bind_u64(stmt, 11, chan->dbid); + db_exec_prepared_v2(take(stmt)); /* If we have a last_sent_commit, store it */ last_sent_commit = tal_arr(tmpctx, u8, 0); @@ -1192,42 +1328,43 @@ void wallet_channel_save(struct wallet *w, struct channel *chan) towire_changed_htlc(&last_sent_commit, &chan->last_sent_commit[i]); - stmt = db_prepare(w->db, - "UPDATE channels SET" - " last_sent_commit=?" - " WHERE id=?"); + stmt = db_prepare_v2(w->db, SQL("UPDATE channels SET" + " last_sent_commit=?" + " WHERE id=?")); if (tal_count(last_sent_commit)) - sqlite3_bind_blob(stmt, 1, - last_sent_commit, tal_count(last_sent_commit), - SQLITE_TRANSIENT); + db_bind_blob(stmt, 0, last_sent_commit, + tal_count(last_sent_commit)); else - sqlite3_bind_null(stmt, 1); - sqlite3_bind_int64(stmt, 2, chan->dbid); - db_exec_prepared(w->db, stmt); + db_bind_null(stmt, 0); + db_bind_u64(stmt, 1, chan->dbid); + db_exec_prepared_v2(take(stmt)); } void wallet_channel_insert(struct wallet *w, struct channel *chan) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; if (chan->peer->dbid == 0) { /* Need to create the peer first */ - stmt = db_prepare(w->db, "INSERT INTO peers (node_id, address) VALUES (?, ?);"); - sqlite3_bind_node_id(stmt, 1, &chan->peer->id); - sqlite3_bind_text(stmt, 2, - type_to_string(tmpctx, struct wireaddr_internal, &chan->peer->addr), - -1, SQLITE_TRANSIENT); - db_exec_prepared(w->db, stmt); - chan->peer->dbid = sqlite3_last_insert_rowid(w->db->sql); + stmt = db_prepare_v2(w->db, + SQL("INSERT INTO peers (node_id, address) VALUES (?, ?);") + ); + db_bind_node_id(stmt, 0, &chan->peer->id); + db_bind_text(stmt, 1, + type_to_string(tmpctx, struct wireaddr_internal, + &chan->peer->addr)); + db_exec_prepared_v2(stmt); + chan->peer->dbid = db_last_insert_id_v2(take(stmt)); } /* Insert a stub, that we update, unifies INSERT and UPDATE paths */ - stmt = db_prepare(w->db, "INSERT INTO channels (" - "peer_id, first_blocknum, id) VALUES (?, ?, ?);"); - sqlite3_bind_int64(stmt, 1, chan->peer->dbid); - sqlite3_bind_int(stmt, 2, chan->first_blocknum); - sqlite3_bind_int(stmt, 3, chan->dbid); - db_exec_prepared(w->db, stmt); + stmt = db_prepare_v2( + w->db, SQL("INSERT INTO channels (" + "peer_id, first_blocknum, id) VALUES (?, ?, ?);")); + db_bind_u64(stmt, 0, chan->peer->dbid); + db_bind_int(stmt, 1, chan->first_blocknum); + db_bind_int(stmt, 2, chan->dbid); + db_exec_prepared_v2(take(stmt)); wallet_channel_config_insert(w, &chan->our_config); wallet_channel_config_insert(w, &chan->channel_info.their_config); @@ -1245,81 +1382,77 @@ void wallet_channel_close(struct wallet *w, u64 wallet_id) * considerably and that are of limited use after channel closure will * be pruned as well. */ - sqlite3_stmt *stmt; + struct db_stmt *stmt; /* Delete entries from `channel_htlcs` */ - stmt = db_prepare(w->db, - "DELETE FROM channel_htlcs " - "WHERE channel_id=?"); - sqlite3_bind_int64(stmt, 1, wallet_id); - db_exec_prepared(w->db, stmt); + stmt = db_prepare_v2(w->db, SQL("DELETE FROM channel_htlcs " + "WHERE channel_id=?")); + db_bind_u64(stmt, 0, wallet_id); + db_exec_prepared_v2(take(stmt)); /* Delete entries from `htlc_sigs` */ - stmt = db_prepare(w->db, - "DELETE FROM htlc_sigs " - "WHERE channelid=?"); - sqlite3_bind_int64(stmt, 1, wallet_id); - db_exec_prepared(w->db, stmt); + stmt = db_prepare_v2(w->db, SQL("DELETE FROM htlc_sigs " + "WHERE channelid=?")); + db_bind_u64(stmt, 0, wallet_id); + db_exec_prepared_v2(take(stmt)); /* Delete entries from `htlc_sigs` */ - stmt = db_prepare(w->db, - "DELETE FROM channeltxs " - "WHERE channel_id=?"); - sqlite3_bind_int64(stmt, 1, wallet_id); - db_exec_prepared(w->db, stmt); + stmt = db_prepare_v2(w->db, SQL("DELETE FROM channeltxs " + "WHERE channel_id=?")); + db_bind_u64(stmt, 0, wallet_id); + db_exec_prepared_v2(take(stmt)); /* Delete shachains */ - stmt = db_prepare(w->db, - "DELETE FROM shachains " - "WHERE id IN (" - " SELECT shachain_remote_id " - " FROM channels " - " WHERE channels.id=?" - ")"); - sqlite3_bind_int64(stmt, 1, wallet_id); - db_exec_prepared(w->db, stmt); + stmt = db_prepare_v2(w->db, SQL("DELETE FROM shachains " + "WHERE id IN (" + " SELECT shachain_remote_id " + " FROM channels " + " WHERE channels.id=?" + ")")); + db_bind_u64(stmt, 0, wallet_id); + db_exec_prepared_v2(take(stmt)); /* Set the channel to closed and disassociate with peer */ - stmt = db_prepare(w->db, - "UPDATE channels " - "SET state=?, peer_id=?" - "WHERE channels.id=?"); - sqlite3_bind_int64(stmt, 1, CLOSED); - sqlite3_bind_null(stmt, 2); - sqlite3_bind_int64(stmt, 3, wallet_id); - db_exec_prepared(w->db, stmt); + stmt = db_prepare_v2(w->db, SQL("UPDATE channels " + "SET state=?, peer_id=?" + "WHERE channels.id=?")); + db_bind_u64(stmt, 0, CLOSED); + db_bind_null(stmt, 1); + db_bind_u64(stmt, 2, wallet_id); + db_exec_prepared_v2(take(stmt)); } void wallet_peer_delete(struct wallet *w, u64 peer_dbid) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; /* Must not have any channels still using this peer */ - stmt = db_select(w->db, - "* FROM channels WHERE peer_id = %"PRIu64, - peer_dbid); - if (db_select_step(w->db, stmt)) + stmt = db_prepare_v2(w->db, SQL("SELECT * FROM channels WHERE peer_id = ?;")); + db_bind_u64(stmt, 0, peer_dbid); + db_query_prepared(stmt); + + if (db_step(stmt)) fatal("We have channels using peer %"PRIu64, peer_dbid); + tal_free(stmt); - stmt = db_prepare(w->db, "DELETE FROM peers WHERE id=?"); - sqlite3_bind_int64(stmt, 1, peer_dbid); - db_exec_prepared(w->db, stmt); + stmt = db_prepare_v2(w->db, SQL("DELETE FROM peers WHERE id=?")); + db_bind_u64(stmt, 0, peer_dbid); + db_exec_prepared_v2(take(stmt)); } void wallet_confirm_tx(struct wallet *w, const struct bitcoin_txid *txid, const u32 confirmation_height) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; assert(confirmation_height > 0); - stmt = db_prepare(w->db, - "UPDATE outputs " - "SET confirmation_height = ? " - "WHERE prev_out_tx = ?"); - sqlite3_bind_int(stmt, 1, confirmation_height); - sqlite3_bind_sha256_double(stmt, 2, &txid->shad); + stmt = db_prepare_v2(w->db, SQL("UPDATE outputs " + "SET confirmation_height = ? " + "WHERE prev_out_tx = ?")); + db_bind_int(stmt, 0, confirmation_height); + db_bind_sha256d(stmt, 1, &txid->shad); - db_exec_prepared(w->db, stmt); + db_exec_prepared_v2(take(stmt)); } int wallet_extract_owned_outputs(struct wallet *w, const struct bitcoin_tx *tx, @@ -1389,100 +1522,98 @@ int wallet_extract_owned_outputs(struct wallet *w, const struct bitcoin_tx *tx, void wallet_htlc_save_in(struct wallet *wallet, const struct channel *chan, struct htlc_in *in) { - sqlite3_stmt *stmt; - - stmt = db_prepare( - wallet->db, - "INSERT INTO channel_htlcs (" - " channel_id," - " channel_htlc_id, " - " direction," - " msatoshi," - " cltv_expiry," - " payment_hash, " - " payment_key," - " hstate," - " shared_secret," - " routing_onion," - " received_time) VALUES " - "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"); - - sqlite3_bind_int64(stmt, 1, chan->dbid); - sqlite3_bind_int64(stmt, 2, in->key.id); - sqlite3_bind_int(stmt, 3, DIRECTION_INCOMING); - sqlite3_bind_amount_msat(stmt, 4, in->msat); - sqlite3_bind_int(stmt, 5, in->cltv_expiry); - sqlite3_bind_sha256(stmt, 6, &in->payment_hash); + struct db_stmt *stmt; + + stmt = db_prepare_v2(wallet->db, + SQL("INSERT INTO channel_htlcs (" + " channel_id," + " channel_htlc_id, " + " direction," + " msatoshi," + " cltv_expiry," + " payment_hash, " + " payment_key," + " hstate," + " shared_secret," + " routing_onion," + " received_time) VALUES " + "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); + + db_bind_u64(stmt, 0, chan->dbid); + db_bind_u64(stmt, 1, in->key.id); + db_bind_int(stmt, 2, DIRECTION_INCOMING); + db_bind_amount_msat(stmt, 3, &in->msat); + db_bind_int(stmt, 4, in->cltv_expiry); + db_bind_sha256(stmt, 5, &in->payment_hash); if (in->preimage) - sqlite3_bind_preimage(stmt, 7, in->preimage); + db_bind_preimage(stmt, 6, in->preimage); else - sqlite3_bind_null(stmt, 7); - sqlite3_bind_int(stmt, 8, in->hstate); + db_bind_null(stmt, 6); + db_bind_int(stmt, 7, in->hstate); if (!in->shared_secret) - sqlite3_bind_null(stmt, 9); + db_bind_null(stmt, 8); else - sqlite3_bind_blob(stmt, 9, in->shared_secret, - sizeof(*in->shared_secret), SQLITE_TRANSIENT); + db_bind_secret(stmt, 8, in->shared_secret); - sqlite3_bind_blob(stmt, 10, &in->onion_routing_packet, - sizeof(in->onion_routing_packet), SQLITE_TRANSIENT); + db_bind_blob(stmt, 9, in->onion_routing_packet, + sizeof(in->onion_routing_packet)); - sqlite3_bind_timeabs(stmt, 11, in->received_time); + db_bind_timeabs(stmt, 10, in->received_time); - db_exec_prepared(wallet->db, stmt); - in->dbid = sqlite3_last_insert_rowid(wallet->db->sql); + db_exec_prepared_v2(stmt); + in->dbid = db_last_insert_id_v2(take(stmt)); } void wallet_htlc_save_out(struct wallet *wallet, const struct channel *chan, struct htlc_out *out) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; /* We absolutely need the incoming HTLC to be persisted before * we can persist it's dependent */ assert(out->in == NULL || out->in->dbid != 0); out->origin_htlc_id = out->in?out->in->dbid:0; - stmt = db_prepare( + stmt = db_prepare_v2( wallet->db, - "INSERT INTO channel_htlcs (" - " channel_id," - " channel_htlc_id," - " direction," - " origin_htlc," - " msatoshi," - " cltv_expiry," - " payment_hash," - " payment_key," - " hstate," - " routing_onion) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"); - - sqlite3_bind_int64(stmt, 1, chan->dbid); - sqlite3_bind_int64(stmt, 2, out->key.id); - sqlite3_bind_int(stmt, 3, DIRECTION_OUTGOING); + SQL("INSERT INTO channel_htlcs (" + " channel_id," + " channel_htlc_id," + " direction," + " origin_htlc," + " msatoshi," + " cltv_expiry," + " payment_hash," + " payment_key," + " hstate," + " routing_onion) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); + + db_bind_u64(stmt, 0, chan->dbid); + db_bind_u64(stmt, 1, out->key.id); + db_bind_int(stmt, 2, DIRECTION_OUTGOING); if (out->in) - sqlite3_bind_int64(stmt, 4, out->in->dbid); + db_bind_u64(stmt, 3, out->in->dbid); else - sqlite3_bind_null(stmt, 4); - sqlite3_bind_amount_msat(stmt, 5, out->msat); - sqlite3_bind_int(stmt, 6, out->cltv_expiry); - sqlite3_bind_sha256(stmt, 7, &out->payment_hash); + db_bind_null(stmt, 3); + db_bind_amount_msat(stmt, 4, &out->msat); + db_bind_int(stmt, 5, out->cltv_expiry); + db_bind_sha256(stmt, 6, &out->payment_hash); if (out->preimage) - sqlite3_bind_preimage(stmt, 8,out->preimage); + db_bind_preimage(stmt, 7, out->preimage); else - sqlite3_bind_null(stmt, 8); - sqlite3_bind_int(stmt, 9, out->hstate); - - sqlite3_bind_blob(stmt, 10, &out->onion_routing_packet, - sizeof(out->onion_routing_packet), SQLITE_TRANSIENT); + db_bind_null(stmt, 7); + db_bind_int(stmt, 8, out->hstate); - db_exec_prepared(wallet->db, stmt); + db_bind_blob(stmt, 9, out->onion_routing_packet, + sizeof(out->onion_routing_packet)); - out->dbid = sqlite3_last_insert_rowid(wallet->db->sql); + db_exec_prepared_v2(stmt); + out->dbid = db_last_insert_id_v2(stmt); + tal_free(stmt); } void wallet_htlc_update(struct wallet *wallet, const u64 htlc_dbid, @@ -1490,75 +1621,66 @@ void wallet_htlc_update(struct wallet *wallet, const u64 htlc_dbid, const struct preimage *payment_key, enum onion_type failcode, const u8 *failuremsg) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; /* The database ID must be set by a previous call to * `wallet_htlc_save_*` */ assert(htlc_dbid); - stmt = db_prepare( - wallet->db, - "UPDATE channel_htlcs SET hstate=?, payment_key=?, malformed_onion=?, failuremsg=? WHERE id=?"); + stmt = db_prepare_v2( + wallet->db, SQL("UPDATE channel_htlcs SET hstate=?, payment_key=?, " + "malformed_onion=?, failuremsg=? WHERE id=?")); /* FIXME: htlc_state_in_db */ - sqlite3_bind_int(stmt, 1, new_state); - sqlite3_bind_int64(stmt, 5, htlc_dbid); + db_bind_int(stmt, 0, new_state); + db_bind_u64(stmt, 4, htlc_dbid); if (payment_key) - sqlite3_bind_preimage(stmt, 2, payment_key); + db_bind_preimage(stmt, 1, payment_key); else - sqlite3_bind_null(stmt, 2); + db_bind_null(stmt, 1); - sqlite3_bind_int(stmt, 3, failcode); + db_bind_int(stmt, 2, failcode); if (failuremsg) - sqlite3_bind_blob(stmt, 4, - failuremsg, tal_bytelen(failuremsg), - SQLITE_TRANSIENT); + db_bind_blob(stmt, 3, failuremsg, tal_bytelen(failuremsg)); else - sqlite3_bind_null(stmt, 4); + db_bind_null(stmt, 3); - db_exec_prepared(wallet->db, stmt); + db_exec_prepared_v2(take(stmt)); } -/* origin_htlc is htlc_out only, shared_secret is htlc_in only */ -#define HTLC_FIELDS \ - "id, channel_htlc_id, msatoshi, cltv_expiry, hstate, " \ - "payment_hash, payment_key, routing_onion, " \ - "failuremsg, malformed_onion," \ - "origin_htlc, shared_secret, received_time" - static bool wallet_stmt2htlc_in(struct channel *channel, - sqlite3_stmt *stmt, struct htlc_in *in) + struct db_stmt *stmt, struct htlc_in *in) { bool ok = true; - in->dbid = sqlite3_column_int64(stmt, 0); - in->key.id = sqlite3_column_int64(stmt, 1); + in->dbid = db_column_u64(stmt, 0); + in->key.id = db_column_u64(stmt, 1); in->key.channel = channel; - in->msat = sqlite3_column_amount_msat(stmt, 2); - in->cltv_expiry = sqlite3_column_int(stmt, 3); - in->hstate = sqlite3_column_int(stmt, 4); + db_column_amount_msat(stmt, 2, &in->msat); + in->cltv_expiry = db_column_int(stmt, 3); + in->hstate = db_column_int(stmt, 4); - sqlite3_column_sha256(stmt, 5, &in->payment_hash); + db_column_sha256(stmt, 5, &in->payment_hash); - if (sqlite3_column_type(stmt, 6) != SQLITE_NULL) { + if (!db_column_is_null(stmt, 6)) { in->preimage = tal(in, struct preimage); - sqlite3_column_preimage(stmt, 6, in->preimage); + db_column_preimage(stmt, 6, in->preimage); } else { in->preimage = NULL; } - assert(sqlite3_column_bytes(stmt, 7) == sizeof(in->onion_routing_packet)); - memcpy(&in->onion_routing_packet, sqlite3_column_blob(stmt, 7), + assert(db_column_bytes(stmt, 7) == sizeof(in->onion_routing_packet)); + memcpy(&in->onion_routing_packet, db_column_blob(stmt, 7), sizeof(in->onion_routing_packet)); - in->failuremsg = sqlite3_column_arr(in, stmt, 8, u8); - in->failcode = sqlite3_column_int(stmt, 9); + in->failuremsg = db_column_arr(in, stmt, 8, u8); + in->failcode = db_column_int(stmt, 9); - if (sqlite3_column_type(stmt, 11) == SQLITE_NULL) { + if (db_column_is_null(stmt, 11)) { in->shared_secret = NULL; } else { - assert(sqlite3_column_bytes(stmt, 11) == sizeof(struct secret)); + assert(db_column_bytes(stmt, 11) == sizeof(struct secret)); in->shared_secret = tal(in, struct secret); - memcpy(in->shared_secret, sqlite3_column_blob(stmt, 11), + memcpy(in->shared_secret, db_column_blob(stmt, 11), sizeof(struct secret)); #ifdef COMPAT_V062 if (memeqzero(in->shared_secret, sizeof(*in->shared_secret))) @@ -1566,39 +1688,39 @@ static bool wallet_stmt2htlc_in(struct channel *channel, #endif } - in->received_time = sqlite3_column_timeabs(stmt, 12); + in->received_time = db_column_timeabs(stmt, 12); return ok; } static bool wallet_stmt2htlc_out(struct channel *channel, - sqlite3_stmt *stmt, struct htlc_out *out) + struct db_stmt *stmt, struct htlc_out *out) { bool ok = true; - out->dbid = sqlite3_column_int64(stmt, 0); - out->key.id = sqlite3_column_int64(stmt, 1); + out->dbid = db_column_u64(stmt, 0); + out->key.id = db_column_u64(stmt, 1); out->key.channel = channel; - out->msat = sqlite3_column_amount_msat(stmt, 2); - out->cltv_expiry = sqlite3_column_int(stmt, 3); - out->hstate = sqlite3_column_int(stmt, 4); - sqlite3_column_sha256(stmt, 5, &out->payment_hash); + db_column_amount_msat(stmt, 2, &out->msat); + out->cltv_expiry = db_column_int(stmt, 3); + out->hstate = db_column_int(stmt, 4); + db_column_sha256(stmt, 5, &out->payment_hash); - if (sqlite3_column_type(stmt, 6) != SQLITE_NULL) { + if (!db_column_is_null(stmt, 6)) { out->preimage = tal(out, struct preimage); - sqlite3_column_preimage(stmt, 6, out->preimage); + db_column_preimage(stmt, 6, out->preimage); } else { out->preimage = NULL; } - assert(sqlite3_column_bytes(stmt, 7) == sizeof(out->onion_routing_packet)); - memcpy(&out->onion_routing_packet, sqlite3_column_blob(stmt, 7), + assert(db_column_bytes(stmt, 7) == sizeof(out->onion_routing_packet)); + memcpy(&out->onion_routing_packet, db_column_blob(stmt, 7), sizeof(out->onion_routing_packet)); - out->failuremsg = sqlite3_column_arr(out, stmt, 8, u8); - out->failcode = sqlite3_column_int(stmt, 9); + out->failuremsg = db_column_arr(out, stmt, 8, u8); + out->failcode = db_column_int(stmt, 9); - if (sqlite3_column_type(stmt, 10) != SQLITE_NULL) { - out->origin_htlc_id = sqlite3_column_int64(stmt, 10); + if (!db_column_is_null(stmt, 10)) { + out->origin_htlc_id = db_column_u64(stmt, 10); out->am_origin = false; } else { out->origin_htlc_id = 0; @@ -1654,17 +1776,35 @@ bool wallet_htlcs_load_for_channel(struct wallet *wallet, struct htlc_in_map *htlcs_in, struct htlc_out_map *htlcs_out) { + struct db_stmt *stmt; bool ok = true; int incount = 0, outcount = 0; log_debug(wallet->log, "Loading HTLCs for channel %"PRIu64, chan->dbid); - sqlite3_stmt *stmt = db_select( - wallet->db, - HTLC_FIELDS " FROM channel_htlcs WHERE " - "direction=%d AND channel_id=%" PRIu64 " AND hstate != %d", - DIRECTION_INCOMING, chan->dbid, SENT_REMOVE_ACK_REVOCATION); - - while (db_select_step(wallet->db, stmt)) { + stmt = db_prepare_v2(wallet->db, SQL("SELECT" + " id" + ", channel_htlc_id" + ", msatoshi" + ", cltv_expiry" + ", hstate" + ", payment_hash" + ", payment_key" + ", routing_onion" + ", failuremsg" + ", malformed_onion" + ", origin_htlc" + ", shared_secret" + ", received_time" + " FROM channel_htlcs" + " WHERE direction= ?" + " AND channel_id= ?" + " AND hstate != ?")); + db_bind_int(stmt, 0, DIRECTION_INCOMING); + db_bind_u64(stmt, 1, chan->dbid); + db_bind_int(stmt, 2, SENT_REMOVE_ACK_REVOCATION); + db_query_prepared(stmt); + + while (db_step(stmt)) { struct htlc_in *in = tal(chan, struct htlc_in); ok &= wallet_stmt2htlc_in(chan, stmt, in); connect_htlc_in(htlcs_in, in); @@ -1672,14 +1812,32 @@ bool wallet_htlcs_load_for_channel(struct wallet *wallet, ok &= htlc_in_check(in, NULL) != NULL; incount++; } - - stmt = db_select( - wallet->db, - HTLC_FIELDS " FROM channel_htlcs WHERE " - "direction=%d AND channel_id=%" PRIu64 " AND hstate != %d", - DIRECTION_OUTGOING, chan->dbid, RCVD_REMOVE_ACK_REVOCATION); - - while (db_select_step(wallet->db, stmt)) { + tal_free(stmt); + + stmt = db_prepare_v2(wallet->db, SQL("SELECT" + " id" + ", channel_htlc_id" + ", msatoshi" + ", cltv_expiry" + ", hstate" + ", payment_hash" + ", payment_key" + ", routing_onion" + ", failuremsg" + ", malformed_onion" + ", origin_htlc" + ", shared_secret" + ", received_time" + " FROM channel_htlcs" + " WHERE direction = ?" + " AND channel_id = ?" + " AND hstate != ?")); + db_bind_int(stmt, 0, DIRECTION_OUTGOING); + db_bind_u64(stmt, 1, chan->dbid); + db_bind_int(stmt, 2, RCVD_REMOVE_ACK_REVOCATION); + db_query_prepared(stmt); + + while (db_step(stmt)) { struct htlc_out *out = tal(chan, struct htlc_out); ok &= wallet_stmt2htlc_out(chan, stmt, out); connect_htlc_out(htlcs_out, out); @@ -1687,6 +1845,7 @@ bool wallet_htlcs_load_for_channel(struct wallet *wallet, * dependencies in yet */ outcount++; } + tal_free(stmt); log_debug(wallet->log, "Restored %d incoming and %d outgoing HTLCS", incount, outcount); @@ -1778,28 +1937,33 @@ struct htlc_stub *wallet_htlc_stubs(const tal_t *ctx, struct wallet *wallet, { struct htlc_stub *stubs; struct sha256 payment_hash; - sqlite3_stmt *stmt = db_select_prepare(wallet->db, - "channel_id, direction, cltv_expiry, channel_htlc_id, payment_hash " - "FROM channel_htlcs WHERE channel_id = ?;"); + struct db_stmt *stmt; + + stmt = db_prepare_v2(wallet->db, + SQL("SELECT channel_id, direction, cltv_expiry, " + "channel_htlc_id, payment_hash " + "FROM channel_htlcs WHERE channel_id = ?;")); - sqlite3_bind_int64(stmt, 1, chan->dbid); + db_bind_u64(stmt, 0, chan->dbid); + db_query_prepared(stmt); stubs = tal_arr(ctx, struct htlc_stub, 0); - while (db_select_step(wallet->db, stmt)) { + while (db_step(stmt)) { struct htlc_stub stub; - assert(sqlite3_column_int64(stmt, 0) == chan->dbid); + assert(db_column_u64(stmt, 0) == chan->dbid); /* FIXME: merge these two enums */ - stub.owner = sqlite3_column_int(stmt, 1)==DIRECTION_INCOMING?REMOTE:LOCAL; - stub.cltv_expiry = sqlite3_column_int(stmt, 2); - stub.id = sqlite3_column_int(stmt, 3); + stub.owner = db_column_int(stmt, 1)==DIRECTION_INCOMING?REMOTE:LOCAL; + stub.cltv_expiry = db_column_int(stmt, 2); + stub.id = db_column_int(stmt, 3); - sqlite3_column_sha256(stmt, 4, &payment_hash); + db_column_sha256(stmt, 4, &payment_hash); ripemd160(&stub.ripemd, payment_hash.u.u8, sizeof(payment_hash.u)); tal_arr_expand(&stubs, stub); } + tal_free(stmt); return stubs; } @@ -1807,18 +1971,17 @@ void wallet_local_htlc_out_delete(struct wallet *wallet, struct channel *chan, const struct sha256 *payment_hash) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; - stmt = db_prepare(wallet->db, - "DELETE FROM channel_htlcs" - " WHERE direction = ?" - " AND origin_htlc = ?" - " AND payment_hash = ?"); - sqlite3_bind_int(stmt, 1, DIRECTION_OUTGOING); - sqlite3_bind_int(stmt, 2, 0); - sqlite3_bind_sha256(stmt, 3, payment_hash); + stmt = db_prepare_v2(wallet->db, SQL("DELETE FROM channel_htlcs" + " WHERE direction = ?" + " AND origin_htlc = ?" + " AND payment_hash = ?")); + db_bind_int(stmt, 0, DIRECTION_OUTGOING); + db_bind_int(stmt, 1, 0); + db_bind_sha256(stmt, 2, payment_hash); - db_exec_prepared(wallet->db, stmt); + db_exec_prepared_v2(take(stmt)); } static struct wallet_payment * @@ -1849,7 +2012,7 @@ void wallet_payment_setup(struct wallet *wallet, struct wallet_payment *payment) void wallet_payment_store(struct wallet *wallet, const struct sha256 *payment_hash) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; struct wallet_payment *payment; payment = find_unstored_payment(wallet, payment_hash); @@ -1860,13 +2023,14 @@ void wallet_payment_store(struct wallet *wallet, * (catch bug, where we call this on a payment_hash * we never paid to) */ bool res; - stmt = db_select_prepare(wallet->db, - "status FROM payments" - " WHERE payment_hash=?;"); - sqlite3_bind_sha256(stmt, 1, payment_hash); - res = db_select_step(wallet->db, stmt); + stmt = + db_prepare_v2(wallet->db, SQL("SELECT status FROM payments" + " WHERE payment_hash=?;")); + db_bind_sha256(stmt, 0, payment_hash); + db_query_prepared(stmt); + res = db_step(stmt); assert(res); - db_stmt_done(stmt); + tal_free(stmt); #endif return; } @@ -1874,9 +2038,9 @@ void wallet_payment_store(struct wallet *wallet, /* Don't attempt to add the same payment twice */ assert(!payment->id); - stmt = db_prepare( + stmt = db_prepare_v2( wallet->db, - "INSERT INTO payments (" + SQL("INSERT INTO payments (" " status," " payment_hash," " destination," @@ -1888,44 +2052,47 @@ void wallet_payment_store(struct wallet *wallet, " msatoshi_sent," " description," " bolt11" - ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"); - - sqlite3_bind_int(stmt, 1, payment->status); - sqlite3_bind_sha256(stmt, 2, &payment->payment_hash); - sqlite3_bind_node_id(stmt, 3, &payment->destination); - sqlite3_bind_amount_msat(stmt, 4, payment->msatoshi); - sqlite3_bind_int(stmt, 5, payment->timestamp); - sqlite3_bind_blob(stmt, 6, payment->path_secrets, - tal_bytelen(payment->path_secrets), - SQLITE_TRANSIENT); - sqlite3_bind_node_id_array(stmt, 7, payment->route_nodes); - sqlite3_bind_short_channel_id_array(stmt, 8, - payment->route_channels); - sqlite3_bind_amount_msat(stmt, 9, payment->msatoshi_sent); + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); + + db_bind_int(stmt, 0, payment->status); + db_bind_sha256(stmt, 1, &payment->payment_hash); + db_bind_node_id(stmt, 2, &payment->destination); + db_bind_amount_msat(stmt, 3, &payment->msatoshi); + db_bind_int(stmt, 4, payment->timestamp); + + if (payment->route_nodes) { + assert(payment->path_secrets); + assert(payment->route_nodes); + assert(payment->route_channels); + db_bind_secret_arr(stmt, 5, payment->path_secrets); + db_bind_node_id_arr(stmt, 6, payment->route_nodes); + db_bind_short_channel_id_arr(stmt, 7, payment->route_channels); + } else { + db_bind_null(stmt, 5); + db_bind_null(stmt, 6); + db_bind_null(stmt, 7); + } + + db_bind_amount_msat(stmt, 8, &payment->msatoshi_sent); if (payment->label != NULL) - sqlite3_bind_text(stmt, 10, payment->label, - strlen(payment->label), - SQLITE_TRANSIENT); + db_bind_text(stmt, 9, payment->label); else - sqlite3_bind_null(stmt, 10); + db_bind_null(stmt, 9); if (payment->bolt11 != NULL) - sqlite3_bind_text(stmt, 11, payment->bolt11, - strlen(payment->bolt11), - SQLITE_TRANSIENT); + db_bind_text(stmt, 10, payment->bolt11); else - sqlite3_bind_null(stmt, 11); - - db_exec_prepared(wallet->db, stmt); + db_bind_null(stmt, 10); + db_exec_prepared_v2(take(stmt)); tal_free(payment); } void wallet_payment_delete(struct wallet *wallet, const struct sha256 *payment_hash) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; struct wallet_payment *payment; payment = find_unstored_payment(wallet, payment_hash); @@ -1934,68 +2101,65 @@ void wallet_payment_delete(struct wallet *wallet, return; } - stmt = db_prepare( - wallet->db, - "DELETE FROM payments WHERE payment_hash = ?"); + stmt = db_prepare_v2( + wallet->db, SQL("DELETE FROM payments WHERE payment_hash = ?")); - sqlite3_bind_sha256(stmt, 1, payment_hash); + db_bind_sha256(stmt, 0, payment_hash); - db_exec_prepared(wallet->db, stmt); + db_exec_prepared_v2(take(stmt)); } static struct wallet_payment *wallet_stmt2payment(const tal_t *ctx, - sqlite3_stmt *stmt) + struct db_stmt *stmt) { struct wallet_payment *payment = tal(ctx, struct wallet_payment); - payment->id = sqlite3_column_int64(stmt, 0); - payment->status = sqlite3_column_int(stmt, 1); + payment->id = db_column_u64(stmt, 0); + payment->status = db_column_int(stmt, 1); - sqlite3_column_node_id(stmt, 2, &payment->destination); - payment->msatoshi = sqlite3_column_amount_msat(stmt, 3); - sqlite3_column_sha256(stmt, 4, &payment->payment_hash); + db_column_node_id(stmt, 2, &payment->destination); + db_column_amount_msat(stmt, 3, &payment->msatoshi); + db_column_sha256(stmt, 4, &payment->payment_hash); - payment->timestamp = sqlite3_column_int(stmt, 5); - if (sqlite3_column_type(stmt, 6) != SQLITE_NULL) { + payment->timestamp = db_column_int(stmt, 5); + if (!db_column_is_null(stmt, 6)) { payment->payment_preimage = tal(payment, struct preimage); - sqlite3_column_preimage(stmt, 6, payment->payment_preimage); + db_column_preimage(stmt, 6, payment->payment_preimage); } else payment->payment_preimage = NULL; /* Can be NULL for old db! */ - payment->path_secrets = sqlite3_column_secrets(payment, stmt, 7); + if (!db_column_is_null(stmt, 7)) { + assert(!db_column_is_null(stmt, 8)); + assert(!db_column_is_null(stmt, 9)); + payment->path_secrets = db_column_secret_arr(payment, stmt, 7); - payment->route_nodes = sqlite3_column_node_id_array(payment, stmt, 8); - payment->route_channels - = sqlite3_column_short_channel_id_array(payment, stmt, 9); + payment->route_nodes = db_column_node_id_arr(payment, stmt, 8); + payment->route_channels = + db_column_short_channel_id_arr(payment, stmt, 9); + } - payment->msatoshi_sent = sqlite3_column_amount_msat(stmt, 10); + db_column_amount_msat(stmt, 10, &payment->msatoshi_sent); - if (sqlite3_column_type(stmt, 11) != SQLITE_NULL) - payment->label = tal_strdup( - payment, (const char *)sqlite3_column_text(stmt, 11)); + if (!db_column_is_null(stmt, 11)) + payment->label = + tal_strdup(payment, (const char *)db_column_text(stmt, 11)); else payment->label = NULL; - if (sqlite3_column_type(stmt, 12) != SQLITE_NULL) - payment->bolt11 = tal_strdup(payment, - (const char *)sqlite3_column_text(stmt, 12)); + if (!db_column_is_null(stmt, 12)) + payment->bolt11 = tal_strdup( + payment, (const char *)db_column_text(stmt, 12)); else payment->bolt11 = NULL; return payment; } -/* List of the fields that stmt2payment expects to correctly convert */ -#define PAYMENT_FIELDS \ - "id, status, destination, msatoshi, payment_hash, timestamp, " \ - "payment_preimage, path_secrets, route_nodes, route_channels, " \ - "msatoshi_sent, description, bolt11 " - struct wallet_payment * wallet_payment_by_hash(const tal_t *ctx, struct wallet *wallet, const struct sha256 *payment_hash) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; struct wallet_payment *payment; /* Present the illusion that it's in the db... */ @@ -2003,14 +2167,29 @@ wallet_payment_by_hash(const tal_t *ctx, struct wallet *wallet, if (payment) return payment; - stmt = db_select_prepare(wallet->db, PAYMENT_FIELDS " FROM payments " - "WHERE payment_hash = ?"); - - sqlite3_bind_sha256(stmt, 1, payment_hash); - if (db_select_step(wallet->db, stmt)) { + stmt = db_prepare_v2(wallet->db, SQL("SELECT" + " id" + ", status" + ", destination" + ", msatoshi" + ", payment_hash" + ", timestamp" + ", payment_preimage" + ", path_secrets" + ", route_nodes" + ", route_channels" + ", msatoshi_sent" + ", description" + ", bolt11" + " FROM payments" + " WHERE payment_hash = ?")); + + db_bind_sha256(stmt, 0, payment_hash); + db_query_prepared(stmt); + if (db_step(stmt)) { payment = wallet_stmt2payment(ctx, stmt); - db_stmt_done(stmt); } + tal_free(stmt); return payment; } @@ -2019,7 +2198,7 @@ void wallet_payment_set_status(struct wallet *wallet, const enum wallet_payment_status newstatus, const struct preimage *preimage) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; struct wallet_payment *payment; /* We can only fail an unstored payment! */ @@ -2030,32 +2209,32 @@ void wallet_payment_set_status(struct wallet *wallet, return; } - stmt = db_prepare(wallet->db, - "UPDATE payments SET status=? " - "WHERE payment_hash=?"); + stmt = db_prepare_v2(wallet->db, + SQL("UPDATE payments SET status=? " + "WHERE payment_hash=?")); - sqlite3_bind_int(stmt, 1, wallet_payment_status_in_db(newstatus)); - sqlite3_bind_sha256(stmt, 2, payment_hash); - db_exec_prepared(wallet->db, stmt); + db_bind_int(stmt, 0, wallet_payment_status_in_db(newstatus)); + db_bind_sha256(stmt, 1, payment_hash); + db_exec_prepared_v2(take(stmt)); if (preimage) { - stmt = db_prepare(wallet->db, - "UPDATE payments SET payment_preimage=? " - "WHERE payment_hash=?"); + stmt = db_prepare_v2(wallet->db, + SQL("UPDATE payments SET payment_preimage=? " + "WHERE payment_hash=?")); - sqlite3_bind_preimage(stmt, 1, preimage); - sqlite3_bind_sha256(stmt, 2, payment_hash); - db_exec_prepared(wallet->db, stmt); + db_bind_preimage(stmt, 0, preimage); + db_bind_sha256(stmt, 1, payment_hash); + db_exec_prepared_v2(take(stmt)); } if (newstatus != PAYMENT_PENDING) { - stmt = db_prepare(wallet->db, - "UPDATE payments" - " SET path_secrets = NULL" - " , route_nodes = NULL" - " , route_channels = NULL" - " WHERE payment_hash = ?;"); - sqlite3_bind_sha256(stmt, 1, payment_hash); - db_exec_prepared(wallet->db, stmt); + stmt = + db_prepare_v2(wallet->db, SQL("UPDATE payments" + " SET path_secrets = NULL" + " , route_nodes = NULL" + " , route_channels = NULL" + " WHERE payment_hash = ?;")); + db_bind_sha256(stmt, 0, payment_hash); + db_exec_prepared_v2(take(stmt)); } } @@ -2073,58 +2252,59 @@ void wallet_payment_get_failinfo(const tal_t *ctx, char **faildetail, int *faildirection) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; bool resb; size_t len; - stmt = db_select_prepare(wallet->db, - "failonionreply, faildestperm" + stmt = db_prepare_v2(wallet->db, + SQL("SELECT failonionreply, faildestperm" ", failindex, failcode" ", failnode, failchannel" ", failupdate, faildetail, faildirection" " FROM payments" - " WHERE payment_hash=?;"); - sqlite3_bind_sha256(stmt, 1, payment_hash); - resb = db_select_step(wallet->db, stmt); + " WHERE payment_hash=?;")); + db_bind_sha256(stmt, 0, payment_hash); + db_query_prepared(stmt); + resb = db_step(stmt); assert(resb); - if (sqlite3_column_type(stmt, 0) == SQLITE_NULL) + + if (db_column_is_null(stmt, 0)) *failonionreply = NULL; else { - len = sqlite3_column_bytes(stmt, 0); + len = db_column_bytes(stmt, 0); *failonionreply = tal_arr(ctx, u8, len); - memcpy(*failonionreply, sqlite3_column_blob(stmt, 0), len); + memcpy(*failonionreply, db_column_blob(stmt, 0), len); } - *faildestperm = sqlite3_column_int(stmt, 1) != 0; - *failindex = sqlite3_column_int(stmt, 2); - *failcode = (enum onion_type) sqlite3_column_int(stmt, 3); - if (sqlite3_column_type(stmt, 4) == SQLITE_NULL) + *faildestperm = db_column_int(stmt, 1) != 0; + *failindex = db_column_int(stmt, 2); + *failcode = (enum onion_type) db_column_int(stmt, 3); + if (db_column_is_null(stmt, 4)) *failnode = NULL; else { *failnode = tal(ctx, struct node_id); - resb = sqlite3_column_node_id(stmt, 4, *failnode); - assert(resb); + db_column_node_id(stmt, 4, *failnode); } - if (sqlite3_column_type(stmt, 5) == SQLITE_NULL) + if (db_column_is_null(stmt, 5)) *failchannel = NULL; else { *failchannel = tal(ctx, struct short_channel_id); - resb = sqlite3_column_short_channel_id(stmt, 5, *failchannel); + resb = db_column_short_channel_id(stmt, 5, *failchannel); assert(resb); /* For pre-0.6.2 dbs, direction will be 0 */ - *faildirection = sqlite3_column_int(stmt, 8); + *faildirection = db_column_int(stmt, 8); } - if (sqlite3_column_type(stmt, 6) == SQLITE_NULL) + if (db_column_is_null(stmt, 6)) *failupdate = NULL; else { - len = sqlite3_column_bytes(stmt, 6); + len = db_column_bytes(stmt, 6); *failupdate = tal_arr(ctx, u8, len); - memcpy(*failupdate, sqlite3_column_blob(stmt, 6), len); + memcpy(*failupdate, db_column_blob(stmt, 6), len); } - *faildetail = tal_strndup(ctx, sqlite3_column_blob(stmt, 7), - sqlite3_column_bytes(stmt, 7)); + *faildetail = + tal_strndup(ctx, db_column_blob(stmt, 7), db_column_bytes(stmt, 7)); - db_stmt_done(stmt); + tal_free(stmt); } void wallet_payment_set_failinfo(struct wallet *wallet, @@ -2139,57 +2319,55 @@ void wallet_payment_set_failinfo(struct wallet *wallet, const char *faildetail, int faildirection) { - sqlite3_stmt *stmt; - - stmt = db_prepare(wallet->db, - "UPDATE payments" - " SET failonionreply=?" - " , faildestperm=?" - " , failindex=?" - " , failcode=?" - " , failnode=?" - " , failchannel=?" - " , failupdate=?" - " , faildetail=?" - " , faildirection=?" - " WHERE payment_hash=?;"); + struct db_stmt *stmt; + + stmt = db_prepare_v2(wallet->db, SQL("UPDATE payments" + " SET failonionreply=?" + " , faildestperm=?" + " , failindex=?" + " , failcode=?" + " , failnode=?" + " , failchannel=?" + " , failupdate=?" + " , faildetail=?" + " , faildirection=?" + " WHERE payment_hash=?;")); if (failonionreply) - sqlite3_bind_blob(stmt, 1, - failonionreply, tal_count(failonionreply), - SQLITE_TRANSIENT); + db_bind_blob(stmt, 0, failonionreply, + tal_count(failonionreply)); else - sqlite3_bind_null(stmt, 1); - sqlite3_bind_int(stmt, 2, faildestperm ? 1 : 0); - sqlite3_bind_int(stmt, 3, failindex); - sqlite3_bind_int(stmt, 4, (int) failcode); + db_bind_null(stmt, 0); + + db_bind_int(stmt, 1, faildestperm ? 1 : 0); + db_bind_int(stmt, 2, failindex); + db_bind_int(stmt, 3, (int) failcode); + if (failnode) - sqlite3_bind_node_id(stmt, 5, failnode); + db_bind_node_id(stmt, 4, failnode); else - sqlite3_bind_null(stmt, 5); + db_bind_null(stmt, 4); + if (failchannel) { - /* sqlite3_bind_short_channel_id requires the input + /* db_bind_short_channel_id requires the input * channel to be tal-allocated... */ struct short_channel_id *scid = tal(tmpctx, struct short_channel_id); *scid = *failchannel; - sqlite3_bind_short_channel_id(stmt, 6, scid); - sqlite3_bind_int(stmt, 9, faildirection); + db_bind_short_channel_id(stmt, 5, scid); + db_bind_int(stmt, 8, faildirection); } else { - sqlite3_bind_null(stmt, 6); - sqlite3_bind_null(stmt, 9); + db_bind_null(stmt, 5); + db_bind_null(stmt, 8); } + if (failupdate) - sqlite3_bind_blob(stmt, 7, - failupdate, tal_count(failupdate), - SQLITE_TRANSIENT); + db_bind_blob(stmt, 6, failupdate, tal_count(failupdate)); else - sqlite3_bind_null(stmt, 7); - sqlite3_bind_blob(stmt, 8, - faildetail, strlen(faildetail), - SQLITE_TRANSIENT); + db_bind_null(stmt, 6); - sqlite3_bind_sha256(stmt, 10, payment_hash); + db_bind_text(stmt, 7, faildetail); + db_bind_sha256(stmt, 9, payment_hash); - db_exec_prepared(wallet->db, stmt); + db_exec_prepared_v2(take(stmt)); } const struct wallet_payment ** @@ -2198,25 +2376,54 @@ wallet_payment_list(const tal_t *ctx, const struct sha256 *payment_hash) { const struct wallet_payment **payments; - sqlite3_stmt *stmt; + struct db_stmt *stmt; struct wallet_payment *p; size_t i; payments = tal_arr(ctx, const struct wallet_payment *, 0); if (payment_hash) { - stmt = db_select_prepare(wallet->db, - PAYMENT_FIELDS " FROM payments " - "WHERE payment_hash = ?;"); - sqlite3_bind_sha256(stmt, 1, payment_hash); + stmt = + db_prepare_v2(wallet->db, SQL("SELECT" + " id" + ", status" + ", destination" + ", msatoshi" + ", payment_hash" + ", timestamp" + ", payment_preimage" + ", path_secrets" + ", route_nodes" + ", route_channels" + ", msatoshi_sent" + ", description" + ", bolt11" + " FROM payments" + " WHERE payment_hash = ?;")); + db_bind_sha256(stmt, 0, payment_hash); } else { - stmt = db_select_prepare(wallet->db, - PAYMENT_FIELDS " FROM payments;"); - } - - for (i = 0; db_select_step(wallet->db, stmt); i++) { + stmt = db_prepare_v2(wallet->db, SQL("SELECT" + " id" + ", status" + ", destination" + ", msatoshi" + ", payment_hash" + ", timestamp" + ", payment_preimage" + ", path_secrets" + ", route_nodes" + ", route_channels" + ", msatoshi_sent" + ", description" + ", bolt11" + " FROM payments;")); + } + db_query_prepared(stmt); + + for (i = 0; db_step(stmt); i++) { tal_resize(&payments, i+1); payments[i] = wallet_stmt2payment(payments, stmt); } + tal_free(stmt); /* Now attach payments not yet in db. */ list_for_each(&wallet->unstored_payments, p, list) { @@ -2233,30 +2440,33 @@ void wallet_htlc_sigs_save(struct wallet *w, u64 channel_id, secp256k1_ecdsa_signature *htlc_sigs) { /* Clear any existing HTLC sigs for this channel */ - sqlite3_stmt *stmt = - db_prepare(w->db, "DELETE FROM htlc_sigs WHERE channelid = ?"); - sqlite3_bind_int64(stmt, 1, channel_id); - db_exec_prepared(w->db, stmt); + struct db_stmt *stmt = db_prepare_v2( + w->db, SQL("DELETE FROM htlc_sigs WHERE channelid = ?")); + db_bind_u64(stmt, 0, channel_id); + db_exec_prepared_v2(take(stmt)); /* Now insert the new ones */ for (size_t i=0; idb, "INSERT INTO htlc_sigs (channelid, signature) VALUES (?, ?)"); - sqlite3_bind_int64(stmt, 1, channel_id); - sqlite3_bind_signature(stmt, 2, &htlc_sigs[i]); - db_exec_prepared(w->db, stmt); + stmt = db_prepare_v2(w->db, + SQL("INSERT INTO htlc_sigs (channelid, " + "signature) VALUES (?, ?)")); + db_bind_u64(stmt, 0, channel_id); + db_bind_signature(stmt, 1, &htlc_sigs[i]); + db_exec_prepared_v2(take(stmt)); } } bool wallet_network_check(struct wallet *w, const struct chainparams *chainparams) { - sqlite3_stmt *stmt = db_select(w->db, - "val FROM vars WHERE name='genesis_hash'"); struct bitcoin_blkid chainhash; + struct db_stmt *stmt = db_prepare_v2( + w->db, SQL("SELECT val FROM vars WHERE name='genesis_hash'")); + db_query_prepared(stmt); - if (db_select_step(w->db, stmt)) { - sqlite3_column_sha256_double(stmt, 0, &chainhash.shad); - db_stmt_done(stmt); + if (db_step(stmt)) { + db_column_sha256d(stmt, 0, &chainhash.shad); + tal_free(stmt); if (!bitcoin_blkid_eq(&chainhash, &chainparams->genesis_blockhash)) { log_broken(w->log, "Wallet blockchain hash does not " @@ -2272,11 +2482,13 @@ bool wallet_network_check(struct wallet *w, return false; } } else { + tal_free(stmt); /* Still a pristine wallet, claim it for the chain * that we are running */ - stmt = db_prepare(w->db, "INSERT INTO vars (name, val) VALUES ('genesis_hash', ?);"); - sqlite3_bind_sha256_double(stmt, 1, &chainparams->genesis_blockhash.shad); - db_exec_prepared(w->db, stmt); + stmt = db_prepare_v2(w->db, SQL("INSERT INTO vars (name, val) " + "VALUES ('genesis_hash', ?);")); + db_bind_sha256d(stmt, 0, &chainparams->genesis_blockhash.shad); + db_exec_prepared_v2(take(stmt)); } return true; } @@ -2286,36 +2498,42 @@ bool wallet_network_check(struct wallet *w, */ static void wallet_utxoset_prune(struct wallet *w, const u32 blockheight) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; struct bitcoin_txid txid; - stmt = db_select_prepare(w->db, "txid, outnum FROM utxoset WHERE spendheight < ?"); - sqlite3_bind_int(stmt, 1, blockheight - UTXO_PRUNE_DEPTH); + stmt = db_prepare_v2( + w->db, + SQL("SELECT txid, outnum FROM utxoset WHERE spendheight < ?")); + db_bind_int(stmt, 0, blockheight - UTXO_PRUNE_DEPTH); + db_query_prepared(stmt); - while (db_select_step(w->db, stmt)) { - sqlite3_column_sha256_double(stmt, 0, &txid.shad); - outpointfilter_remove(w->utxoset_outpoints, &txid, sqlite3_column_int(stmt, 1)); + while (db_step(stmt)) { + db_column_sha256d(stmt, 0, &txid.shad); + outpointfilter_remove(w->utxoset_outpoints, &txid, + db_column_int(stmt, 1)); } + tal_free(stmt); - stmt = db_prepare(w->db, "DELETE FROM utxoset WHERE spendheight < ?"); - sqlite3_bind_int(stmt, 1, blockheight - UTXO_PRUNE_DEPTH); - db_exec_prepared(w->db, stmt); + stmt = db_prepare_v2(w->db, + SQL("DELETE FROM utxoset WHERE spendheight < ?")); + db_bind_int(stmt, 0, blockheight - UTXO_PRUNE_DEPTH); + db_exec_prepared_v2(take(stmt)); } void wallet_block_add(struct wallet *w, struct block *b) { - sqlite3_stmt *stmt = db_prepare(w->db, - "INSERT INTO blocks " - "(height, hash, prev_hash) " - "VALUES (?, ?, ?);"); - sqlite3_bind_int(stmt, 1, b->height); - sqlite3_bind_sha256_double(stmt, 2, &b->blkid.shad); + struct db_stmt *stmt = + db_prepare_v2(w->db, SQL("INSERT INTO blocks " + "(height, hash, prev_hash) " + "VALUES (?, ?, ?);")); + db_bind_int(stmt, 0, b->height); + db_bind_sha256d(stmt, 1, &b->blkid.shad); if (b->prev) { - sqlite3_bind_sha256_double(stmt, 3, &b->prev->blkid.shad); + db_bind_sha256d(stmt, 2, &b->prev->blkid.shad); }else { - sqlite3_bind_null(stmt, 3); + db_bind_null(stmt, 2); } - db_exec_prepared(w->db, stmt); + db_exec_prepared_v2(take(stmt)); /* Now cleanup UTXOs that we don't care about anymore */ wallet_utxoset_prune(w, b->height); @@ -2323,22 +2541,26 @@ void wallet_block_add(struct wallet *w, struct block *b) void wallet_block_remove(struct wallet *w, struct block *b) { - sqlite3_stmt *stmt = db_prepare(w->db, - "DELETE FROM blocks WHERE hash = ?"); - sqlite3_bind_sha256_double(stmt, 1, &b->blkid.shad); - db_exec_prepared(w->db, stmt); + struct db_stmt *stmt = + db_prepare_v2(w->db, SQL("DELETE FROM blocks WHERE hash = ?")); + db_bind_sha256d(stmt, 0, &b->blkid.shad); + db_exec_prepared_v2(take(stmt)); - stmt = db_select_prepare(w->db, "* FROM blocks WHERE height >= ?;"); - sqlite3_bind_int(stmt, 1, b->height); - assert(!db_select_step(w->db, stmt)); + /* Make sure that all descendants of the block are also deleted */ + stmt = db_prepare_v2(w->db, + SQL("SELECT * FROM blocks WHERE height >= ?;")); + db_bind_int(stmt, 0, b->height); + db_query_prepared(stmt); + assert(!db_step(stmt)); + tal_free(stmt); } void wallet_blocks_rollback(struct wallet *w, u32 height) { - sqlite3_stmt *stmt = db_prepare(w->db, "DELETE FROM blocks " - "WHERE height > ?"); - sqlite3_bind_int(stmt, 1, height); - db_exec_prepared(w->db, stmt); + struct db_stmt *stmt = db_prepare_v2(w->db, SQL("DELETE FROM blocks " + "WHERE height > ?")); + db_bind_int(stmt, 0, height); + db_exec_prepared_v2(take(stmt)); } const struct short_channel_id * @@ -2346,57 +2568,60 @@ wallet_outpoint_spend(struct wallet *w, const tal_t *ctx, const u32 blockheight, const struct bitcoin_txid *txid, const u32 outnum) { struct short_channel_id *scid; - sqlite3_stmt *stmt; + struct db_stmt *stmt; bool res; + int changes; if (outpointfilter_matches(w->owned_outpoints, txid, outnum)) { - stmt = db_prepare(w->db, - "UPDATE outputs " - "SET spend_height = ? " - "WHERE prev_out_tx = ?" - " AND prev_out_index = ?"); + stmt = db_prepare_v2(w->db, SQL("UPDATE outputs " + "SET spend_height = ? " + "WHERE prev_out_tx = ?" + " AND prev_out_index = ?")); - sqlite3_bind_int(stmt, 1, blockheight); - sqlite3_bind_sha256_double(stmt, 2, &txid->shad); - sqlite3_bind_int(stmt, 3, outnum); + db_bind_int(stmt, 0, blockheight); + db_bind_sha256d(stmt, 1, &txid->shad); + db_bind_int(stmt, 2, outnum); - db_exec_prepared(w->db, stmt); + db_exec_prepared_v2(take(stmt)); } if (outpointfilter_matches(w->utxoset_outpoints, txid, outnum)) { - stmt = db_prepare(w->db, - "UPDATE utxoset " - "SET spendheight = ? " - "WHERE txid = ?" - " AND outnum = ?"); + stmt = db_prepare_v2(w->db, SQL("UPDATE utxoset " + "SET spendheight = ? " + "WHERE txid = ?" + " AND outnum = ?")); - sqlite3_bind_int(stmt, 1, blockheight); - sqlite3_bind_sha256_double(stmt, 2, &txid->shad); - sqlite3_bind_int(stmt, 3, outnum); + db_bind_int(stmt, 0, blockheight); + db_bind_sha256d(stmt, 1, &txid->shad); + db_bind_int(stmt, 2, outnum); - db_exec_prepared(w->db, stmt); + db_exec_prepared_v2(stmt); + changes = db_count_changes(stmt); + tal_free(stmt); - if (sqlite3_changes(w->db->sql) == 0) { + if (changes == 0) { return NULL; } /* Now look for the outpoint's short_channel_id */ - stmt = db_select_prepare(w->db, - "blockheight, txindex " - "FROM utxoset " - "WHERE txid = ? AND outnum = ?"); - sqlite3_bind_sha256_double(stmt, 1, &txid->shad); - sqlite3_bind_int(stmt, 2, outnum); - - res = db_select_step(w->db, stmt); + stmt = + db_prepare_v2(w->db, SQL("SELECT " + "blockheight, txindex " + "FROM utxoset " + "WHERE txid = ? AND outnum = ?")); + db_bind_sha256d(stmt, 0, &txid->shad); + db_bind_int(stmt, 1, outnum); + db_query_prepared(stmt); + + res = db_step(stmt); assert(res); scid = tal(ctx, struct short_channel_id); - if (!mk_short_channel_id(scid, sqlite3_column_int(stmt, 0), - sqlite3_column_int(stmt, 1), outnum)) + if (!mk_short_channel_id(scid, db_column_int(stmt, 0), + db_column_int(stmt, 1), outnum)) fatal("wallet_outpoint_spend: invalid scid %u:%u:%u", - sqlite3_column_int(stmt, 0), - sqlite3_column_int(stmt, 1), outnum); - db_stmt_done(stmt); + db_column_int(stmt, 0), + db_column_int(stmt, 1), outnum); + tal_free(stmt); return scid; } return NULL; @@ -2407,63 +2632,65 @@ void wallet_utxoset_add(struct wallet *w, const struct bitcoin_tx *tx, const u32 txindex, const u8 *scriptpubkey, struct amount_sat sat) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; struct bitcoin_txid txid; bitcoin_txid(tx, &txid); - stmt = db_prepare(w->db, "INSERT INTO utxoset (" - " txid," - " outnum," - " blockheight," - " spendheight," - " txindex," - " scriptpubkey," - " satoshis" - ") VALUES(?, ?, ?, ?, ?, ?, ?);"); - sqlite3_bind_sha256_double(stmt, 1, &txid.shad); - sqlite3_bind_int(stmt, 2, outnum); - sqlite3_bind_int(stmt, 3, blockheight); - sqlite3_bind_null(stmt, 4); - sqlite3_bind_int(stmt, 5, txindex); - sqlite3_bind_blob(stmt, 6, scriptpubkey, tal_count(scriptpubkey), SQLITE_TRANSIENT); - sqlite3_bind_amount_sat(stmt, 7, sat); - db_exec_prepared(w->db, stmt); + stmt = db_prepare_v2(w->db, SQL("INSERT INTO utxoset (" + " txid," + " outnum," + " blockheight," + " spendheight," + " txindex," + " scriptpubkey," + " satoshis" + ") VALUES(?, ?, ?, ?, ?, ?, ?);")); + db_bind_sha256d(stmt, 0, &txid.shad); + db_bind_int(stmt, 1, outnum); + db_bind_int(stmt, 2, blockheight); + db_bind_null(stmt, 3); + db_bind_int(stmt, 4, txindex); + db_bind_blob(stmt, 5, scriptpubkey, tal_count(scriptpubkey)); + db_bind_amount_sat(stmt, 6, &sat); + db_exec_prepared_v2(take(stmt)); outpointfilter_add(w->utxoset_outpoints, &txid, outnum); } void wallet_filteredblock_add(struct wallet *w, const struct filteredblock *fb) { + struct db_stmt *stmt; if (wallet_have_block(w, fb->height)) return; - sqlite3_stmt *stmt = db_prepare(w->db, "INSERT OR IGNORE INTO blocks " - "(height, hash, prev_hash) " - "VALUES (?, ?, ?);"); - sqlite3_bind_int(stmt, 1, fb->height); - sqlite3_bind_sha256_double(stmt, 2, &fb->id.shad); - sqlite3_bind_sha256_double(stmt, 3, &fb->prev_hash.shad); - db_exec_prepared(w->db, stmt); + stmt = db_prepare_v2(w->db, SQL("INSERT OR IGNORE INTO blocks " + "(height, hash, prev_hash) " + "VALUES (?, ?, ?);")); + db_bind_int(stmt, 0, fb->height); + db_bind_sha256d(stmt, 1, &fb->id.shad); + db_bind_sha256d(stmt, 2, &fb->prev_hash.shad); + db_exec_prepared_v2(take(stmt)); for (size_t i = 0; i < tal_count(fb->outpoints); i++) { struct filteredblock_outpoint *o = fb->outpoints[i]; - stmt = db_prepare(w->db, "INSERT INTO utxoset (" - " txid," - " outnum," - " blockheight," - " spendheight," - " txindex," - " scriptpubkey," - " satoshis" - ") VALUES(?, ?, ?, ?, ?, ?, ?);"); - sqlite3_bind_sha256_double(stmt, 1, &o->txid.shad); - sqlite3_bind_int(stmt, 2, o->outnum); - sqlite3_bind_int(stmt, 3, fb->height); - sqlite3_bind_null(stmt, 4); - sqlite3_bind_int(stmt, 5, o->txindex); - sqlite3_bind_blob(stmt, 6, o->scriptPubKey, - tal_count(o->scriptPubKey), SQLITE_TRANSIENT); - sqlite3_bind_amount_sat(stmt, 7, o->amount); - db_exec_prepared(w->db, stmt); + stmt = + db_prepare_v2(w->db, SQL("INSERT INTO utxoset (" + " txid," + " outnum," + " blockheight," + " spendheight," + " txindex," + " scriptpubkey," + " satoshis" + ") VALUES(?, ?, ?, ?, ?, ?, ?);")); + db_bind_sha256d(stmt, 0, &o->txid.shad); + db_bind_int(stmt, 1, o->outnum); + db_bind_int(stmt, 2, fb->height); + db_bind_null(stmt, 3); + db_bind_int(stmt, 4, o->txindex); + db_bind_blob(stmt, 5, o->scriptPubKey, + tal_count(o->scriptPubKey)); + db_bind_amount_sat(stmt, 6, &o->amount); + db_exec_prepared_v2(take(stmt)); outpointfilter_add(w->utxoset_outpoints, &o->txid, o->outnum); } @@ -2472,46 +2699,50 @@ void wallet_filteredblock_add(struct wallet *w, const struct filteredblock *fb) bool wallet_have_block(struct wallet *w, u32 blockheight) { bool result; - sqlite3_stmt *stmt = db_select_prepare(w->db, "height FROM blocks WHERE height = ?"); - sqlite3_bind_int(stmt, 1, blockheight); - result = sqlite3_step(stmt) == SQLITE_ROW; - db_stmt_done(stmt); + struct db_stmt *stmt = db_prepare_v2( + w->db, SQL("SELECT height FROM blocks WHERE height = ?")); + db_bind_int(stmt, 0, blockheight); + db_query_prepared(stmt); + result = db_step(stmt); + tal_free(stmt); return result; } struct outpoint *wallet_outpoint_for_scid(struct wallet *w, tal_t *ctx, const struct short_channel_id *scid) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; struct outpoint *op; - stmt = db_select_prepare(w->db, - " txid," - " spendheight," - " scriptpubkey," - " satoshis " - "FROM utxoset " - "WHERE blockheight = ?" - " AND txindex = ?" - " AND outnum = ?" - " AND spendheight IS NULL"); - sqlite3_bind_int(stmt, 1, short_channel_id_blocknum(scid)); - sqlite3_bind_int(stmt, 2, short_channel_id_txnum(scid)); - sqlite3_bind_int(stmt, 3, short_channel_id_outnum(scid)); - - - if (!db_select_step(w->db, stmt)) + stmt = db_prepare_v2(w->db, SQL("SELECT" + " txid," + " spendheight," + " scriptpubkey," + " satoshis " + "FROM utxoset " + "WHERE blockheight = ?" + " AND txindex = ?" + " AND outnum = ?" + " AND spendheight IS NULL")); + db_bind_int(stmt, 0, short_channel_id_blocknum(scid)); + db_bind_int(stmt, 1, short_channel_id_txnum(scid)); + db_bind_int(stmt, 2, short_channel_id_outnum(scid)); + db_query_prepared(stmt); + + if (!db_step(stmt)) { + tal_free(stmt); return NULL; + } op = tal(ctx, struct outpoint); op->blockheight = short_channel_id_blocknum(scid); op->txindex = short_channel_id_txnum(scid); op->outnum = short_channel_id_outnum(scid); - sqlite3_column_sha256_double(stmt, 0, &op->txid.shad); - op->spendheight = sqlite3_column_int(stmt, 1); - op->scriptpubkey = tal_arr(op, u8, sqlite3_column_bytes(stmt, 2)); - memcpy(op->scriptpubkey, sqlite3_column_blob(stmt, 2), sqlite3_column_bytes(stmt, 2)); - op->sat = sqlite3_column_amount_sat(stmt, 3); - db_stmt_done(stmt); + db_column_sha256d(stmt, 0, &op->txid.shad); + op->spendheight = db_column_int(stmt, 1); + op->scriptpubkey = tal_arr(op, u8, db_column_bytes(stmt, 2)); + memcpy(op->scriptpubkey, db_column_blob(stmt, 2), db_column_bytes(stmt, 2)); + db_column_amount_sat(stmt, 3, &op->sat); + tal_free(stmt); return op; } @@ -2520,41 +2751,45 @@ void wallet_transaction_add(struct wallet *w, const struct bitcoin_tx *tx, const u32 blockheight, const u32 txindex) { struct bitcoin_txid txid; - sqlite3_stmt *stmt = db_select_prepare(w->db, "blockheight FROM transactions WHERE id=?"); + struct db_stmt *stmt = db_prepare_v2( + w->db, SQL("SELECT blockheight FROM transactions WHERE id=?")); bitcoin_txid(tx, &txid); - sqlite3_bind_sha256(stmt, 1, &txid.shad.sha); - if (!db_select_step(w->db, stmt)) { + db_bind_txid(stmt, 0, &txid); + db_query_prepared(stmt); + + if (!db_step(stmt)) { + tal_free(stmt); /* This transaction is still unknown, insert */ - stmt = db_prepare(w->db, - "INSERT INTO transactions (" + stmt = db_prepare_v2(w->db, + SQL("INSERT INTO transactions (" " id" ", blockheight" ", txindex" - ", rawtx) VALUES (?, ?, ?, ?);"); - sqlite3_bind_sha256(stmt, 1, &txid.shad.sha); + ", rawtx) VALUES (?, ?, ?, ?);")); + db_bind_txid(stmt, 0, &txid); if (blockheight) { - sqlite3_bind_int(stmt, 2, blockheight); - sqlite3_bind_int(stmt, 3, txindex); + db_bind_int(stmt, 1, blockheight); + db_bind_int(stmt, 2, txindex); } else { - sqlite3_bind_null(stmt, 2); - sqlite3_bind_null(stmt, 3); + db_bind_null(stmt, 1); + db_bind_null(stmt, 2); } - sqlite3_bind_tx(stmt, 4, tx); - db_exec_prepared(w->db, stmt); + db_bind_tx(stmt, 3, tx); + db_exec_prepared_v2(take(stmt)); } else { - db_stmt_done(stmt); + tal_free(stmt); if (blockheight) { /* We know about the transaction, update */ - stmt = db_prepare(w->db, - "UPDATE transactions " - "SET blockheight = ?, txindex = ? " - "WHERE id = ?"); - sqlite3_bind_int(stmt, 1, blockheight); - sqlite3_bind_int(stmt, 2, txindex); - sqlite3_bind_sha256(stmt, 3, &txid.shad.sha); - db_exec_prepared(w->db, stmt); + stmt = db_prepare_v2(w->db, + SQL("UPDATE transactions " + "SET blockheight = ?, txindex = ? " + "WHERE id = ?")); + db_bind_int(stmt, 0, blockheight); + db_bind_int(stmt, 1, txindex); + db_bind_txid(stmt, 2, &txid); + db_exec_prepared_v2(take(stmt)); } } } @@ -2563,43 +2798,52 @@ void wallet_transaction_annotate(struct wallet *w, const struct bitcoin_txid *txid, enum wallet_tx_type type, u64 channel_id) { - sqlite3_stmt *stmt = db_select_prepare(w->db, "type, channel_id FROM transactions WHERE id=?"); - sqlite3_bind_sha256(stmt, 1, &txid->shad.sha); - if (!db_select_step(w->db, stmt)) + struct db_stmt *stmt = db_prepare_v2( + w->db, SQL("SELECT type, channel_id FROM transactions WHERE id=?")); + db_bind_txid(stmt, 0, txid); + db_query_prepared(stmt); + + if (!db_step(stmt)) fatal("Attempting to annotate a transaction we don't have: %s", type_to_string(tmpctx, struct bitcoin_txid, txid)); - type |= sqlite3_column_int(stmt, 0); + + type |= db_column_int(stmt, 0); if (channel_id == 0) - channel_id = sqlite3_column_int64(stmt, 1); + channel_id = db_column_u64(stmt, 1); - db_stmt_done(stmt); + tal_free(stmt); - stmt = db_prepare(w->db, "UPDATE transactions " - "SET type = ?" - ", channel_id = ? " - "WHERE id = ?"); + stmt = db_prepare_v2(w->db, SQL("UPDATE transactions " + "SET type = ?" + ", channel_id = ? " + "WHERE id = ?")); + + db_bind_int(stmt, 0, type); - sqlite3_bind_int(stmt, 1, type); if (channel_id) - sqlite3_bind_int(stmt, 2, channel_id); + db_bind_int(stmt, 1, channel_id); else - sqlite3_bind_null(stmt, 2); - sqlite3_bind_sha256(stmt, 3, &txid->shad.sha); - db_exec_prepared(w->db, stmt); + db_bind_null(stmt, 1); + + db_bind_txid(stmt, 2, txid); + db_exec_prepared_v2(take(stmt)); } u32 wallet_transaction_height(struct wallet *w, const struct bitcoin_txid *txid) { u32 blockheight; - sqlite3_stmt *stmt = db_select_prepare( - w->db, "blockheight FROM transactions WHERE id=?"); - sqlite3_bind_sha256(stmt, 1, &txid->shad.sha); + struct db_stmt *stmt = db_prepare_v2( + w->db, SQL("SELECT blockheight FROM transactions WHERE id=?")); + db_bind_txid(stmt, 0, txid); + db_query_prepared(stmt); - if (!db_select_step(w->db, stmt)) + if (!db_step(stmt)) { + tal_free(stmt); return 0; + } - blockheight = sqlite3_column_int(stmt, 0); - db_stmt_done(stmt); + blockheight = db_column_int(stmt, 0); + tal_free(stmt); return blockheight; } @@ -2607,23 +2851,26 @@ struct txlocator *wallet_transaction_locate(const tal_t *ctx, struct wallet *w, const struct bitcoin_txid *txid) { struct txlocator *loc; - sqlite3_stmt *stmt; + struct db_stmt *stmt; - stmt = db_select_prepare( - w->db, "blockheight, txindex FROM transactions WHERE id=?"); - sqlite3_bind_sha256(stmt, 1, &txid->shad.sha); + stmt = db_prepare_v2( + w->db, SQL("SELECT blockheight, txindex FROM transactions WHERE id=?")); + db_bind_txid(stmt, 0, txid); + db_query_prepared(stmt); - if (!db_select_step(w->db, stmt)) + if (!db_step(stmt)) { + tal_free(stmt); return NULL; + } - if (sqlite3_column_type(stmt, 0) == SQLITE_NULL) + if (db_column_is_null(stmt, 0)) loc = NULL; else { loc = tal(ctx, struct txlocator); - loc->blkheight = sqlite3_column_int(stmt, 0); - loc->index = sqlite3_column_int(stmt, 1); + loc->blkheight = db_column_int(stmt, 0); + loc->index = db_column_int(stmt, 1); } - db_stmt_done(stmt); + tal_free(stmt); return loc; } @@ -2631,18 +2878,20 @@ struct bitcoin_txid *wallet_transactions_by_height(const tal_t *ctx, struct wallet *w, const u32 blockheight) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; struct bitcoin_txid *txids = tal_arr(ctx, struct bitcoin_txid, 0); int count = 0; - stmt = db_select_prepare( - w->db, "id FROM transactions WHERE blockheight=?"); - sqlite3_bind_int(stmt, 1, blockheight); + stmt = db_prepare_v2( + w->db, SQL("SELECT id FROM transactions WHERE blockheight=?")); + db_bind_int(stmt, 0, blockheight); + db_query_prepared(stmt); - while (db_select_step(w->db, stmt)) { + while (db_step(stmt)) { count++; tal_resize(&txids, count); - sqlite3_column_sha256(stmt, 0, &txids[count-1].shad.sha); + db_column_txid(stmt, 0, &txids[count-1]); } + tal_free(stmt); return txids; } @@ -2651,37 +2900,41 @@ void wallet_channeltxs_add(struct wallet *w, struct channel *chan, const int type, const struct bitcoin_txid *txid, const u32 input_num, const u32 blockheight) { - sqlite3_stmt *stmt; - stmt = db_prepare(w->db, "INSERT INTO channeltxs (" - " channel_id" - ", type" - ", transaction_id" - ", input_num" - ", blockheight" - ") VALUES (?, ?, ?, ?, ?);"); - sqlite3_bind_int(stmt, 1, chan->dbid); - sqlite3_bind_int(stmt, 2, type); - sqlite3_bind_sha256(stmt, 3, &txid->shad.sha); - sqlite3_bind_int(stmt, 4, input_num); - sqlite3_bind_int(stmt, 5, blockheight); + struct db_stmt *stmt; + stmt = db_prepare_v2(w->db, SQL("INSERT INTO channeltxs (" + " channel_id" + ", type" + ", transaction_id" + ", input_num" + ", blockheight" + ") VALUES (?, ?, ?, ?, ?);")); + db_bind_int(stmt, 0, chan->dbid); + db_bind_int(stmt, 1, type); + db_bind_sha256(stmt, 2, &txid->shad.sha); + db_bind_int(stmt, 3, input_num); + db_bind_int(stmt, 4, blockheight); - db_exec_prepared(w->db, stmt); + db_exec_prepared_v2(take(stmt)); } u32 *wallet_onchaind_channels(struct wallet *w, const tal_t *ctx) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; size_t count = 0; u32 *channel_ids = tal_arr(ctx, u32, 0); - stmt = db_select_prepare(w->db, "DISTINCT(channel_id) FROM channeltxs WHERE type = ?;"); - sqlite3_bind_int(stmt, 1, WIRE_ONCHAIN_INIT); + stmt = db_prepare_v2( + w->db, + SQL("SELECT DISTINCT(channel_id) FROM channeltxs WHERE type = ?;")); + db_bind_int(stmt, 0, WIRE_ONCHAIN_INIT); + db_query_prepared(stmt); - while (db_select_step(w->db, stmt)) { + while (db_step(stmt)) { count++; tal_resize(&channel_ids, count); - channel_ids[count-1] = sqlite3_column_int64(stmt, 0); + channel_ids[count-1] = db_column_u64(stmt, 0); } + tal_free(stmt); return channel_ids; } @@ -2689,34 +2942,37 @@ u32 *wallet_onchaind_channels(struct wallet *w, struct channeltx *wallet_channeltxs_get(struct wallet *w, const tal_t *ctx, u32 channel_id) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; size_t count = 0; struct channeltx *res = tal_arr(ctx, struct channeltx, 0); - stmt = db_select_prepare(w->db, - " c.type" - ", c.blockheight" - ", t.rawtx" - ", c.input_num" - ", c.blockheight - t.blockheight + 1 AS depth" - ", t.id as txid " - "FROM channeltxs c " - "JOIN transactions t ON t.id == c.transaction_id " - "WHERE c.channel_id = ? " - "ORDER BY c.id ASC;"); - sqlite3_bind_int(stmt, 1, channel_id); - - while (db_select_step(w->db, stmt)) { + stmt = db_prepare_v2( + w->db, SQL("SELECT" + " c.type" + ", c.blockheight" + ", t.rawtx" + ", c.input_num" + ", c.blockheight - t.blockheight + 1 AS depth" + ", t.id as txid " + "FROM channeltxs c " + "JOIN transactions t ON t.id == c.transaction_id " + "WHERE c.channel_id = ? " + "ORDER BY c.id ASC;")); + db_bind_int(stmt, 0, channel_id); + db_query_prepared(stmt); + + while (db_step(stmt)) { count++; tal_resize(&res, count); res[count-1].channel_id = channel_id; - res[count-1].type = sqlite3_column_int(stmt, 0); - res[count-1].blockheight = sqlite3_column_int(stmt, 1); - res[count-1].tx = sqlite3_column_tx(ctx, stmt, 2); - res[count-1].input_num = sqlite3_column_int(stmt, 3); - res[count-1].depth = sqlite3_column_int(stmt, 4); - sqlite3_column_sha256(stmt, 5, &res[count-1].txid.shad.sha); - } + res[count-1].type = db_column_int(stmt, 0); + res[count-1].blockheight = db_column_int(stmt, 1); + res[count-1].tx = db_column_tx(ctx, stmt, 2); + res[count-1].input_num = db_column_int(stmt, 3); + res[count-1].depth = db_column_int(stmt, 4); + db_column_txid(stmt, 5, &res[count-1].txid); + } + tal_free(stmt); return res; } @@ -2725,83 +2981,82 @@ void wallet_forwarded_payment_add(struct wallet *w, const struct htlc_in *in, enum forward_status state, enum onion_type failcode) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; struct timeabs *resolved_time; - stmt = db_prepare( - w->db, - "INSERT OR REPLACE INTO forwarded_payments (" - " in_htlc_id" - ", out_htlc_id" - ", in_channel_scid" - ", out_channel_scid" - ", in_msatoshi" - ", out_msatoshi" - ", state" - ", received_time" - ", resolved_time" - ", failcode" - ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"); - sqlite3_bind_int64(stmt, 1, in->dbid); + stmt = db_prepare_v2(w->db, + SQL("INSERT OR REPLACE INTO forwarded_payments (" + " in_htlc_id" + ", out_htlc_id" + ", in_channel_scid" + ", out_channel_scid" + ", in_msatoshi" + ", out_msatoshi" + ", state" + ", received_time" + ", resolved_time" + ", failcode" + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); + db_bind_u64(stmt, 0, in->dbid); if(out) { - sqlite3_bind_int64(stmt, 2, out->dbid); - sqlite3_bind_int64(stmt, 4, out->key.channel->scid->u64); - sqlite3_bind_amount_msat(stmt, 6, out->msat); + db_bind_u64(stmt, 1, out->dbid); + db_bind_u64(stmt, 3, out->key.channel->scid->u64); + db_bind_amount_msat(stmt, 5, &out->msat); } else { /* FORWARD_LOCAL_FAILED may occur before we get htlc_out */ assert(failcode != 0); assert(state == FORWARD_LOCAL_FAILED); - sqlite3_bind_null(stmt, 2); - sqlite3_bind_null(stmt, 4); - sqlite3_bind_null(stmt, 6); + db_bind_null(stmt, 1); + db_bind_null(stmt, 3); + db_bind_null(stmt, 5); } - sqlite3_bind_int64(stmt, 3, in->key.channel->scid->u64); + db_bind_u64(stmt, 2, in->key.channel->scid->u64); - sqlite3_bind_amount_msat(stmt, 5, in->msat); + db_bind_amount_msat(stmt, 4, &in->msat); - sqlite3_bind_int(stmt, 7, wallet_forward_status_in_db(state)); - sqlite3_bind_timeabs(stmt, 8, in->received_time); + db_bind_int(stmt, 6, wallet_forward_status_in_db(state)); + db_bind_timeabs(stmt, 7, in->received_time); if (state == FORWARD_SETTLED || state == FORWARD_FAILED) { resolved_time = tal(tmpctx, struct timeabs); *resolved_time = time_now(); - sqlite3_bind_timeabs(stmt, 9, *resolved_time); + db_bind_timeabs(stmt, 8, *resolved_time); } else { resolved_time = NULL; - sqlite3_bind_null(stmt, 9); + db_bind_null(stmt, 8); } if(failcode != 0) { assert(state == FORWARD_FAILED || state == FORWARD_LOCAL_FAILED); - sqlite3_bind_int(stmt, 10, (int)failcode); + db_bind_int(stmt, 9, (int)failcode); } else { - sqlite3_bind_null(stmt, 10); + db_bind_null(stmt, 9); } - db_exec_prepared(w->db, stmt); + db_exec_prepared_v2(take(stmt)); notify_forward_event(w->ld, in, out, state, failcode, resolved_time); } struct amount_msat wallet_total_forward_fees(struct wallet *w) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; struct amount_msat total; bool res; - stmt = db_select_prepare(w->db, - " SUM(in_msatoshi - out_msatoshi) " - "FROM forwarded_payments " - "WHERE state = ?;"); + stmt = db_prepare_v2(w->db, SQL("SELECT" + " SUM(in_msatoshi - out_msatoshi) " + "FROM forwarded_payments " + "WHERE state = ?;")); + db_bind_int(stmt, 0, wallet_forward_status_in_db(FORWARD_SETTLED)); + db_query_prepared(stmt); - sqlite3_bind_int(stmt, 1, wallet_forward_status_in_db(FORWARD_SETTLED)); - - res = db_select_step(w->db, stmt); + res = db_step(stmt); assert(res); - total = sqlite3_column_amount_msat(stmt, 0); - db_stmt_done(stmt); + db_column_amount_msat(stmt, 0, &total); + tal_free(stmt); return total; } @@ -2811,28 +3066,31 @@ const struct forwarding *wallet_forwarded_payments_get(struct wallet *w, { struct forwarding *results = tal_arr(ctx, struct forwarding, 0); size_t count = 0; - sqlite3_stmt *stmt; - stmt = db_select_prepare(w->db, - " f.state" - ", in_msatoshi" - ", out_msatoshi" - ", hin.payment_hash as payment_hash" - ", in_channel_scid" - ", out_channel_scid" - ", f.received_time" - ", f.resolved_time" - ", f.failcode " - "FROM forwarded_payments f " - "LEFT JOIN channel_htlcs hin ON (f.in_htlc_id == hin.id)"); - - for (count=0; db_select_step(w->db, stmt); count++) { + struct db_stmt *stmt; + stmt = db_prepare_v2( + w->db, + SQL("SELECT" + " f.state" + ", in_msatoshi" + ", out_msatoshi" + ", hin.payment_hash as payment_hash" + ", in_channel_scid" + ", out_channel_scid" + ", f.received_time" + ", f.resolved_time" + ", f.failcode " + "FROM forwarded_payments f " + "LEFT JOIN channel_htlcs hin ON (f.in_htlc_id == hin.id)")); + db_query_prepared(stmt); + + for (count=0; db_step(stmt); count++) { tal_resize(&results, count+1); struct forwarding *cur = &results[count]; - cur->status = sqlite3_column_int(stmt, 0); - cur->msat_in = sqlite3_column_amount_msat(stmt, 1); + cur->status = db_column_int(stmt, 0); + db_column_amount_msat(stmt, 1, &cur->msat_in); - if (sqlite3_column_type(stmt, 2) != SQLITE_NULL) { - cur->msat_out = sqlite3_column_amount_msat(stmt, 2); + if (!db_column_is_null(stmt, 2)) { + db_column_amount_msat(stmt, 2, &cur->msat_out); if (!amount_msat_sub(&cur->fee, cur->msat_in, cur->msat_out)) { log_broken(w->log, "Forwarded in %s less than out %s!", type_to_string(tmpctx, struct amount_msat, @@ -2850,40 +3108,40 @@ const struct forwarding *wallet_forwarded_payments_get(struct wallet *w, cur->fee = AMOUNT_MSAT(0); } - if (sqlite3_column_type(stmt, 3) != SQLITE_NULL) { + if (!db_column_is_null(stmt, 3)) { cur->payment_hash = tal(ctx, struct sha256); - sqlite3_column_sha256(stmt, 3, cur->payment_hash); + db_column_sha256(stmt, 3, cur->payment_hash); } else { cur->payment_hash = NULL; } - cur->channel_in.u64 = sqlite3_column_int64(stmt, 4); + cur->channel_in.u64 = db_column_u64(stmt, 4); - if (sqlite3_column_type(stmt, 5) != SQLITE_NULL) { - cur->channel_out.u64 = sqlite3_column_int64(stmt, 5); + if (!db_column_is_null(stmt, 5)) { + cur->channel_out.u64 = db_column_u64(stmt, 5); } else { assert(cur->status == FORWARD_LOCAL_FAILED); cur->channel_out.u64 = 0; } - cur->received_time = sqlite3_column_timeabs(stmt, 6); + cur->received_time = db_column_timeabs(stmt, 6); - if (sqlite3_column_type(stmt, 7) != SQLITE_NULL) { + if (!db_column_is_null(stmt, 7)) { cur->resolved_time = tal(ctx, struct timeabs); - *cur->resolved_time = sqlite3_column_timeabs(stmt, 7); + *cur->resolved_time = db_column_timeabs(stmt, 7); } else { cur->resolved_time = NULL; } - if (sqlite3_column_type(stmt, 8) != SQLITE_NULL) { + if (!db_column_is_null(stmt, 8)) { assert(cur->status == FORWARD_FAILED || cur->status == FORWARD_LOCAL_FAILED); - cur->failcode = sqlite3_column_int(stmt, 8); + cur->failcode = db_column_int(stmt, 8); } else { cur->failcode = 0; } } - + tal_free(stmt); return results; } @@ -2964,24 +3222,27 @@ void wallet_clean_utxos(struct wallet *w, struct bitcoind *bitcoind) struct wallet_transaction *wallet_transactions_get(struct wallet *w, const tal_t *ctx) { - sqlite3_stmt *stmt; + struct db_stmt *stmt; size_t count; struct wallet_transaction *cur, *txs = tal_arr(ctx, struct wallet_transaction, 0); - stmt = db_select_prepare(w->db, - "id, id, rawtx, blockheight, txindex, type, channel_id " - "FROM transactions"); - for (count = 0; db_select_step(w->db, stmt); count++) { + stmt = db_prepare_v2( + w->db, + SQL("SELECT id, id, rawtx, blockheight, txindex, type, channel_id " + "FROM transactions")); + db_query_prepared(stmt); + + for (count = 0; db_step(stmt); count++) { tal_resize(&txs, count + 1); cur = &txs[count]; - sqlite3_column_sha256_double(stmt, 1, &cur->id.shad); - cur->rawtx = tal_dup_arr(txs, u8, sqlite3_column_blob(stmt, 2), - sqlite3_column_bytes(stmt, 2), 0); - cur->blockheight = sqlite3_column_int(stmt, 3); - cur->txindex = sqlite3_column_int(stmt, 4); - cur->type = sqlite3_column_int(stmt, 5); - cur->channel_id = sqlite3_column_int(stmt, 6); - } - + db_column_txid(stmt, 1, &cur->id); + cur->rawtx = tal_dup_arr(txs, u8, db_column_blob(stmt, 2), + db_column_bytes(stmt, 2), 0); + cur->blockheight = db_column_int(stmt, 3); + cur->txindex = db_column_int(stmt, 4); + cur->type = db_column_int(stmt, 5); + cur->channel_id = db_column_int(stmt, 6); + } + tal_free(stmt); return txs; } diff --git a/wallet/wallet.h b/wallet/wallet.h index f5d16580b287..efd01e284415 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -300,7 +300,7 @@ struct wallet_transaction { }; /** - * wallet_new - Constructor for a new sqlite3 based wallet + * wallet_new - Constructor for a new DB based wallet * * This is guaranteed to either return a valid wallet, or abort with * `fatal` if it cannot be initialized.