core-lightning/wallet/test/run-db.c
Rusty Russell 785fe973a6 runes: ensure that uniqueid is a valid number.
It always is for runes we create, but in theory you can take our secret key
and make our own runes with your own tools.

(We correctly refuse runes without uniqueids if they're *not* ours
anyway: uniqueid is only used for our own runes).

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2023-09-12 15:19:02 +09:30

471 lines
19 KiB
C

#include "config.h"
#include <lightningd/log.h>
static void db_log_(struct logger *log UNUSED, enum log_level level UNUSED, const struct node_id *node_id UNUSED, bool call_notifier UNUSED, const char *fmt UNUSED, ...)
{
}
#define log_ db_log_
#include "db/bindings.c"
#include "db/db_sqlite3.c"
#include "db/exec.c"
#include "db/utils.c"
#include "wallet/db.c"
#include "wallet/wallet.c"
#include "test_utils.h"
#include <common/setup.h>
#include <common/utils.h>
#include <lightningd/channel.h>
#include <stdio.h>
#include <unistd.h>
/* AUTOGENERATED MOCKS START */
/* Generated stub for bip32_pubkey */
void bip32_pubkey(struct lightningd *ld UNNEEDED, struct pubkey *pubkey UNNEEDED, u32 index UNNEEDED)
{ fprintf(stderr, "bip32_pubkey called!\n"); abort(); }
/* Generated stub for channel_scid_or_local_alias */
const struct short_channel_id *channel_scid_or_local_alias(const struct channel *chan UNNEEDED)
{ fprintf(stderr, "channel_scid_or_local_alias called!\n"); abort(); }
/* Generated stub for connect_htlc_in */
void connect_htlc_in(struct htlc_in_map *map UNNEEDED, struct htlc_in *hin UNNEEDED)
{ fprintf(stderr, "connect_htlc_in called!\n"); abort(); }
/* Generated stub for connect_htlc_out */
void connect_htlc_out(struct htlc_out_map *map UNNEEDED, struct htlc_out *hout UNNEEDED)
{ fprintf(stderr, "connect_htlc_out called!\n"); abort(); }
/* Generated stub for create_onionreply */
struct onionreply *create_onionreply(const tal_t *ctx UNNEEDED,
const struct secret *shared_secret UNNEEDED,
const u8 *failure_msg UNNEEDED)
{ fprintf(stderr, "create_onionreply called!\n"); abort(); }
/* Generated stub for derive_channel_id */
void derive_channel_id(struct channel_id *channel_id UNNEEDED,
const struct bitcoin_outpoint *outpoint UNNEEDED)
{ fprintf(stderr, "derive_channel_id called!\n"); abort(); }
/* Generated stub for fatal */
void fatal(const char *fmt UNNEEDED, ...)
{ fprintf(stderr, "fatal called!\n"); abort(); }
/* Generated stub for fatal_vfmt */
void fatal_vfmt(const char *fmt UNNEEDED, va_list ap UNNEEDED)
{ fprintf(stderr, "fatal_vfmt called!\n"); abort(); }
/* Generated stub for find_peer_by_dbid */
struct peer *find_peer_by_dbid(struct lightningd *ld UNNEEDED, u64 dbid UNNEEDED)
{ fprintf(stderr, "find_peer_by_dbid called!\n"); abort(); }
/* Generated stub for fromwire_hsmd_get_channel_basepoints_reply */
bool fromwire_hsmd_get_channel_basepoints_reply(const void *p UNNEEDED, struct basepoints *basepoints UNNEEDED, struct pubkey *funding_pubkey UNNEEDED)
{ fprintf(stderr, "fromwire_hsmd_get_channel_basepoints_reply called!\n"); abort(); }
/* Generated stub for fromwire_hsmd_get_output_scriptpubkey_reply */
bool fromwire_hsmd_get_output_scriptpubkey_reply(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **script UNNEEDED)
{ fprintf(stderr, "fromwire_hsmd_get_output_scriptpubkey_reply called!\n"); abort(); }
/* Generated stub for get_channel_basepoints */
void get_channel_basepoints(struct lightningd *ld UNNEEDED,
const struct node_id *peer_id UNNEEDED,
const u64 dbid UNNEEDED,
struct basepoints *local_basepoints UNNEEDED,
struct pubkey *local_funding_pubkey UNNEEDED)
{ fprintf(stderr, "get_channel_basepoints called!\n"); abort(); }
/* Generated stub for htlc_in_check */
struct htlc_in *htlc_in_check(const struct htlc_in *hin UNNEEDED, const char *abortstr UNNEEDED)
{ fprintf(stderr, "htlc_in_check called!\n"); abort(); }
/* Generated stub for htlc_out_connect_htlc_in */
void htlc_out_connect_htlc_in(struct htlc_out *hout UNNEEDED, struct htlc_in *hin UNNEEDED)
{ fprintf(stderr, "htlc_out_connect_htlc_in called!\n"); abort(); }
/* Generated stub for invoices_new */
struct invoices *invoices_new(const tal_t *ctx UNNEEDED,
struct wallet *wallet UNNEEDED,
struct timers *timers UNNEEDED)
{ fprintf(stderr, "invoices_new called!\n"); abort(); }
/* Generated stub for logv */
void logv(struct logger *logger UNNEEDED, enum log_level level UNNEEDED, const struct node_id *node_id UNNEEDED,
bool call_notifier UNNEEDED, const char *fmt UNNEEDED, va_list ap UNNEEDED)
{ fprintf(stderr, "logv called!\n"); abort(); }
/* Generated stub for new_channel */
struct channel *new_channel(struct peer *peer UNNEEDED, u64 dbid UNNEEDED,
/* NULL or stolen */
struct wallet_shachain *their_shachain STEALS UNNEEDED,
enum channel_state state UNNEEDED,
enum side opener UNNEEDED,
/* NULL or stolen */
struct logger *log STEALS UNNEEDED,
const char *transient_billboard TAKES UNNEEDED,
u8 channel_flags UNNEEDED,
bool req_confirmed_ins_local UNNEEDED,
bool req_confirmed_ins_remote UNNEEDED,
const struct channel_config *our_config UNNEEDED,
u32 minimum_depth UNNEEDED,
u64 next_index_local UNNEEDED,
u64 next_index_remote UNNEEDED,
u64 next_htlc_id UNNEEDED,
const struct bitcoin_outpoint *funding UNNEEDED,
struct amount_sat funding_sats UNNEEDED,
struct amount_msat push UNNEEDED,
struct amount_sat our_funds UNNEEDED,
bool remote_channel_ready UNNEEDED,
/* NULL or stolen */
struct short_channel_id *scid STEALS UNNEEDED,
struct short_channel_id *alias_local STEALS UNNEEDED,
struct short_channel_id *alias_remote STEALS UNNEEDED,
struct channel_id *cid UNNEEDED,
struct amount_msat our_msatoshi UNNEEDED,
struct amount_msat msatoshi_to_us_min UNNEEDED,
struct amount_msat msatoshi_to_us_max UNNEEDED,
struct bitcoin_tx *last_tx STEALS UNNEEDED,
const struct bitcoin_signature *last_sig UNNEEDED,
/* NULL or stolen */
const struct bitcoin_signature *last_htlc_sigs STEALS UNNEEDED,
const struct channel_info *channel_info UNNEEDED,
const struct fee_states *fee_states TAKES UNNEEDED,
/* NULL or stolen */
u8 *remote_shutdown_scriptpubkey STEALS UNNEEDED,
const u8 *local_shutdown_scriptpubkey UNNEEDED,
u64 final_key_idx UNNEEDED,
bool last_was_revoke UNNEEDED,
/* NULL or stolen */
struct changed_htlc *last_sent_commit STEALS UNNEEDED,
u32 first_blocknum UNNEEDED,
u32 min_possible_feerate UNNEEDED,
u32 max_possible_feerate UNNEEDED,
const struct basepoints *local_basepoints UNNEEDED,
const struct pubkey *local_funding_pubkey UNNEEDED,
const struct pubkey *future_per_commitment_point UNNEEDED,
u32 feerate_base UNNEEDED,
u32 feerate_ppm UNNEEDED,
/* NULL or stolen */
const u8 *remote_upfront_shutdown_script STEALS UNNEEDED,
u64 local_static_remotekey_start UNNEEDED,
u64 remote_static_remotekey_start UNNEEDED,
const struct channel_type *type STEALS UNNEEDED,
enum side closer UNNEEDED,
enum state_change reason UNNEEDED,
/* NULL or stolen */
const struct bitcoin_outpoint *shutdown_wrong_funding STEALS UNNEEDED,
const struct height_states *height_states TAKES UNNEEDED,
u32 lease_expiry UNNEEDED,
secp256k1_ecdsa_signature *lease_commit_sig STEALS UNNEEDED,
u32 lease_chan_max_msat UNNEEDED,
u16 lease_chan_max_ppt UNNEEDED,
struct amount_msat htlc_minimum_msat UNNEEDED,
struct amount_msat htlc_maximum_msat UNNEEDED,
bool ignore_fee_limits UNNEEDED)
{ fprintf(stderr, "new_channel called!\n"); abort(); }
/* Generated stub for new_coin_wallet_deposit */
struct chain_coin_mvt *new_coin_wallet_deposit(const tal_t *ctx UNNEEDED,
const struct bitcoin_outpoint *outpoint UNNEEDED,
u32 blockheight UNNEEDED,
struct amount_sat amount UNNEEDED,
enum mvt_tag tag)
{ fprintf(stderr, "new_coin_wallet_deposit called!\n"); abort(); }
/* Generated stub for new_inflight */
struct channel_inflight *new_inflight(struct channel *channel UNNEEDED,
const struct bitcoin_outpoint *funding_outpoint UNNEEDED,
u32 funding_feerate UNNEEDED,
struct amount_sat funding_sat UNNEEDED,
struct amount_sat our_funds UNNEEDED,
struct wally_psbt *funding_psbt STEALS UNNEEDED,
struct bitcoin_tx *last_tx STEALS UNNEEDED,
const struct bitcoin_signature last_sig UNNEEDED,
const u32 lease_expiry UNNEEDED,
const secp256k1_ecdsa_signature *lease_commit_sig UNNEEDED,
const u32 lease_chan_max_msat UNNEEDED,
const u16 lease_chan_max_ppt UNNEEDED,
const u32 lease_blockheight_start UNNEEDED,
const struct amount_msat lease_fee UNNEEDED,
const struct amount_sat lease_amt UNNEEDED,
s64 splice_amnt UNNEEDED,
bool i_am_initiator UNNEEDED)
{ fprintf(stderr, "new_inflight called!\n"); abort(); }
/* Generated stub for new_logger */
struct logger *new_logger(const tal_t *ctx UNNEEDED, struct log_book *record UNNEEDED,
const struct node_id *default_node_id UNNEEDED,
const char *fmt UNNEEDED, ...)
{ fprintf(stderr, "new_logger called!\n"); abort(); }
/* Generated stub for new_peer */
struct peer *new_peer(struct lightningd *ld UNNEEDED, u64 dbid UNNEEDED,
const struct node_id *id UNNEEDED,
const struct wireaddr_internal *addr UNNEEDED,
const u8 *their_features TAKES UNNEEDED,
bool connected_incoming UNNEEDED)
{ fprintf(stderr, "new_peer called!\n"); abort(); }
/* Generated stub for notify_chain_mvt */
void notify_chain_mvt(struct lightningd *ld UNNEEDED, const struct chain_coin_mvt *mvt UNNEEDED)
{ fprintf(stderr, "notify_chain_mvt called!\n"); abort(); }
/* Generated stub for notify_forward_event */
void notify_forward_event(struct lightningd *ld UNNEEDED,
const struct htlc_in *in UNNEEDED,
/* May be NULL if we don't know. */
const struct short_channel_id *scid_out UNNEEDED,
/* May be NULL. */
const struct amount_msat *amount_out UNNEEDED,
enum forward_status state UNNEEDED,
enum onion_wire failcode UNNEEDED,
struct timeabs *resolved_time UNNEEDED,
enum forward_style forward_style UNNEEDED)
{ fprintf(stderr, "notify_forward_event called!\n"); abort(); }
/* Generated stub for onion_wire_name */
const char *onion_wire_name(int e UNNEEDED)
{ fprintf(stderr, "onion_wire_name called!\n"); abort(); }
/* Generated stub for outpointfilter_add */
void outpointfilter_add(struct outpointfilter *of UNNEEDED,
const struct bitcoin_outpoint *outpoint UNNEEDED)
{ fprintf(stderr, "outpointfilter_add called!\n"); abort(); }
/* Generated stub for outpointfilter_matches */
bool outpointfilter_matches(struct outpointfilter *of UNNEEDED,
const struct bitcoin_outpoint *outpoint UNNEEDED)
{ fprintf(stderr, "outpointfilter_matches called!\n"); abort(); }
/* Generated stub for outpointfilter_new */
struct outpointfilter *outpointfilter_new(tal_t *ctx UNNEEDED)
{ fprintf(stderr, "outpointfilter_new called!\n"); abort(); }
/* Generated stub for outpointfilter_remove */
void outpointfilter_remove(struct outpointfilter *of UNNEEDED,
const struct bitcoin_outpoint *outpoint UNNEEDED)
{ fprintf(stderr, "outpointfilter_remove called!\n"); abort(); }
/* Generated stub for peer_set_dbid */
void peer_set_dbid(struct peer *peer UNNEEDED, u64 dbid UNNEEDED)
{ fprintf(stderr, "peer_set_dbid called!\n"); abort(); }
/* Generated stub for psbt_fixup */
const u8 *psbt_fixup(const tal_t *ctx UNNEEDED, const u8 *psbtblob UNNEEDED)
{ fprintf(stderr, "psbt_fixup called!\n"); abort(); }
/* Generated stub for remove_htlc_in_by_dbid */
struct htlc_in *remove_htlc_in_by_dbid(struct htlc_in_map *remaining_htlcs_in UNNEEDED,
u64 dbid UNNEEDED)
{ fprintf(stderr, "remove_htlc_in_by_dbid called!\n"); abort(); }
/* Generated stub for rune_is_ours */
const char *rune_is_ours(struct lightningd *ld UNNEEDED, const struct rune *rune UNNEEDED)
{ fprintf(stderr, "rune_is_ours called!\n"); abort(); }
/* Generated stub for rune_unique_id */
u64 rune_unique_id(const struct rune *rune UNNEEDED)
{ fprintf(stderr, "rune_unique_id called!\n"); abort(); }
/* Generated stub for to_canonical_invstr */
const char *to_canonical_invstr(const tal_t *ctx UNNEEDED, const char *invstring UNNEEDED)
{ fprintf(stderr, "to_canonical_invstr called!\n"); abort(); }
/* Generated stub for towire_hsmd_get_channel_basepoints */
u8 *towire_hsmd_get_channel_basepoints(const tal_t *ctx UNNEEDED, const struct node_id *peerid UNNEEDED, u64 dbid UNNEEDED)
{ fprintf(stderr, "towire_hsmd_get_channel_basepoints called!\n"); abort(); }
/* Generated stub for towire_hsmd_get_output_scriptpubkey */
u8 *towire_hsmd_get_output_scriptpubkey(const tal_t *ctx UNNEEDED, u64 channel_id UNNEEDED, const struct node_id *peer_id UNNEEDED, const struct pubkey *commitment_point UNNEEDED)
{ fprintf(stderr, "towire_hsmd_get_output_scriptpubkey called!\n"); abort(); }
/* Generated stub for towire_temporary_node_failure */
u8 *towire_temporary_node_failure(const tal_t *ctx UNNEEDED)
{ fprintf(stderr, "towire_temporary_node_failure called!\n"); abort(); }
/* Generated stub for trace_span_end */
void trace_span_end(const void *key UNNEEDED)
{ fprintf(stderr, "trace_span_end called!\n"); abort(); }
/* Generated stub for trace_span_start */
void trace_span_start(const char *name UNNEEDED, const void *key UNNEEDED)
{ fprintf(stderr, "trace_span_start called!\n"); abort(); }
/* Generated stub for txfilter_add_scriptpubkey */
void txfilter_add_scriptpubkey(struct txfilter *filter UNNEEDED, const u8 *script TAKES UNNEEDED)
{ fprintf(stderr, "txfilter_add_scriptpubkey called!\n"); abort(); }
/* Generated stub for wait_index_name */
const char *wait_index_name(enum wait_index index UNNEEDED)
{ fprintf(stderr, "wait_index_name called!\n"); abort(); }
/* Generated stub for wait_subsystem_name */
const char *wait_subsystem_name(enum wait_subsystem subsystem UNNEEDED)
{ fprintf(stderr, "wait_subsystem_name called!\n"); abort(); }
/* Generated stub for wire_sync_read */
u8 *wire_sync_read(const tal_t *ctx UNNEEDED, int fd UNNEEDED)
{ fprintf(stderr, "wire_sync_read called!\n"); abort(); }
/* Generated stub for wire_sync_write */
bool wire_sync_write(int fd UNNEEDED, const void *msg TAKES UNNEEDED)
{ fprintf(stderr, "wire_sync_write called!\n"); abort(); }
/* AUTOGENERATED MOCKS END */
void plugin_hook_db_sync(struct db *db UNNEEDED)
{
}
static struct db *create_test_db(void)
{
struct db *db;
char *dsn, *filename;
int fd = tmpdir_mkstemp(tmpctx, "ldb-XXXXXX", &filename);
if (fd == -1)
return NULL;
close(fd);
dsn = tal_fmt(NULL, "sqlite3://%s", filename);
tal_free(filename);
db = db_open(NULL, dsn, db_error, (struct lightningd *)NULL);
db->data_version = 0;
db->report_changes_fn = NULL;
tal_free(dsn);
return db;
}
static bool test_empty_db_migrate(struct lightningd *ld)
{
struct db *db = create_test_db();
const struct ext_key *bip32_base = NULL;
CHECK(db);
db_begin_transaction(db);
CHECK(db_get_version(db) == -1);
db_migrate(ld, db, bip32_base);
db_commit_transaction(db);
db_begin_transaction(db);
CHECK(db_get_version(db) == ARRAY_SIZE(dbmigrations) - 1);
db_commit_transaction(db);
tal_free(db);
return true;
}
static bool test_primitives(void)
{
struct db_stmt *stmt;
struct db *db = create_test_db();
db_begin_transaction(db);
CHECK(db->in_transaction);
db_commit_transaction(db);
CHECK(!db->in_transaction);
db_begin_transaction(db);
db_commit_transaction(db);
db_begin_transaction(db);
stmt = db_prepare_v2(db, SQL("SELECT name FROM sqlite_master WHERE type='table';"));
db_exec_prepared_v2(stmt);
tal_free(stmt);
/* We didn't migrate the DB, so don't have the vars table. Pretend we
* didn't change anything so we don't bump the data_version. */
db->dirty = false;
db_commit_transaction(db);
CHECK(!db->in_transaction);
tal_free(db);
return true;
}
static bool test_vars(struct lightningd *ld)
{
struct db *db = create_test_db();
char *varname = "testvar";
const struct ext_key *bip32_base = NULL;
CHECK(db);
db_begin_transaction(db);
db_migrate(ld, db, bip32_base);
/* Check default behavior */
CHECK(db_get_intvar(db, varname, 42) == 42);
/* Check setting and getting */
db_set_intvar(db, varname, 1);
CHECK(db_get_intvar(db, varname, 42) == 1);
/* Check updating */
db_set_intvar(db, varname, 2);
CHECK(db_get_intvar(db, varname, 42) == 2);
db_commit_transaction(db);
tal_free(db);
return true;
}
static bool test_manip_columns(void)
{
struct db_stmt *stmt;
struct db *db = create_test_db();
const char *field1 = "field1";
db_begin_transaction(db);
/* tablea refers to tableb */
stmt = db_prepare_v2(db, SQL("CREATE TABLE tablea ("
" id BIGSERIAL"
", field1 INTEGER"
", PRIMARY KEY (id))"));
db_exec_prepared_v2(stmt);
tal_free(stmt);
stmt = db_prepare_v2(db, SQL("INSERT INTO tablea (id, field1) VALUES (0, 1);"));
db_exec_prepared_v2(stmt);
tal_free(stmt);
stmt = db_prepare_v2(db, SQL("CREATE TABLE tableb ("
" id REFERENCES tablea(id) ON DELETE CASCADE"
", field1 INTEGER"
", field2 INTEGER);"));
db_exec_prepared_v2(stmt);
tal_free(stmt);
stmt = db_prepare_v2(db, SQL("INSERT INTO tableb (id, field1, field2) VALUES (0, 1, 2);"));
db_exec_prepared_v2(stmt);
tal_free(stmt);
/* Needs vars table, since this changes db. */
stmt = db_prepare_v2(db, SQL("CREATE TABLE vars (name VARCHAR(32), intval);"));
db_exec_prepared_v2(stmt);
tal_free(stmt);
stmt = db_prepare_v2(db, SQL("INSERT INTO vars VALUES ('data_version', 0);"));
db_exec_prepared_v2(stmt);
tal_free(stmt);
/* Rename tablea.field1 -> table1.field1a. */
CHECK(db->config->rename_column(db, "tablea", "field1", "field1a"));
/* Remove tableb.field1. */
CHECK(db->config->delete_columns(db, "tableb", &field1, 1));
stmt = db_prepare_v2(db, SQL("SELECT id, field1a FROM tablea;"));
CHECK_MSG(db_query_prepared_canfail(stmt), "db_query_prepared must succeed");
CHECK(db_step(stmt));
CHECK(db_col_u64(stmt, "id") == 0);
CHECK(db_col_u64(stmt, "field1a") == 1);
CHECK(!db_step(stmt));
tal_free(stmt);
stmt = db_prepare_v2(db, SQL("SELECT id, field2 FROM tableb;"));
CHECK_MSG(db_query_prepared_canfail(stmt), "db_query_prepared must succeed");
CHECK(db_step(stmt));
CHECK(db_col_u64(stmt, "id") == 0);
CHECK(db_col_u64(stmt, "field2") == 2);
CHECK(!db_step(stmt));
tal_free(stmt);
db->dirty = false;
db->changes = tal_arr(db, const char *, 0);
db_commit_transaction(db);
db_begin_transaction(db);
/* This will actually fail */
stmt = db_prepare_v2(db, SQL("SELECT field1 FROM tablea;"));
CHECK_MSG(!db_query_prepared_canfail(stmt), "db_query_prepared must fail");
db->dirty = false;
db->changes = tal_arr(db, const char *, 0);
db_commit_transaction(db);
db_begin_transaction(db);
/* This will actually fail */
stmt = db_prepare_v2(db, SQL("SELECT field1 FROM tableb;"));
CHECK_MSG(!db_query_prepared_canfail(stmt), "db_query_prepared must fail");
db->dirty = false;
db->changes = tal_arr(db, const char *, 0);
db_commit_transaction(db);
tal_free(db);
return true;
}
int main(int argc, char *argv[])
{
bool ok = true;
/* Dummy for migration hooks */
struct lightningd *ld = tal(NULL, struct lightningd);
common_setup(argv[0]);
ld->config = test_config;
/* We do a runtime test here, so we still check compile! */
if (HAVE_SQLITE3) {
ok &= test_empty_db_migrate(ld);
ok &= test_vars(ld);
ok &= test_primitives();
ok &= test_manip_columns();
}
tal_free(ld);
common_shutdown();
return !ok;
}