You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1282 lines
39 KiB
1282 lines
39 KiB
#include "wallet.h"
|
|
|
|
#include <bitcoin/script.h>
|
|
#include <ccan/tal/str/str.h>
|
|
#include <inttypes.h>
|
|
#include <lightningd/lightningd.h>
|
|
#include <lightningd/log.h>
|
|
#include <lightningd/peer_control.h>
|
|
#include <lightningd/peer_htlcs.h>
|
|
|
|
#define SQLITE_MAX_UINT 0x7FFFFFFFFFFFFFFF
|
|
#define DIRECTION_INCOMING 0
|
|
#define DIRECTION_OUTGOING 1
|
|
|
|
struct wallet *wallet_new(const tal_t *ctx, struct log *log)
|
|
{
|
|
struct wallet *wallet = tal(ctx, struct wallet);
|
|
wallet->db = db_setup(wallet);
|
|
wallet->log = log;
|
|
wallet->bip32_base = NULL;
|
|
return wallet;
|
|
}
|
|
|
|
/* We actually use the db constraints to uniquify, so OK if this fails. */
|
|
bool wallet_add_utxo(struct wallet *w, struct utxo *utxo,
|
|
enum wallet_output_type type)
|
|
{
|
|
sqlite3_stmt *stmt;
|
|
|
|
stmt = db_prepare(w->db, "INSERT INTO outputs (prev_out_tx, prev_out_index, value, type, status, keyindex) VALUES (?, ?, ?, ?, ?, ?);");
|
|
sqlite3_bind_blob(stmt, 1, &utxo->txid, sizeof(utxo->txid), SQLITE_TRANSIENT);
|
|
sqlite3_bind_int(stmt, 2, utxo->outnum);
|
|
sqlite3_bind_int64(stmt, 3, utxo->amount);
|
|
sqlite3_bind_int(stmt, 4, type);
|
|
sqlite3_bind_int(stmt, 5, output_state_available);
|
|
sqlite3_bind_int(stmt, 6, utxo->keyindex);
|
|
return db_exec_prepared_mayfail(w->db, stmt);
|
|
}
|
|
|
|
/**
|
|
* wallet_stmt2output - Extract data from stmt and fill a utxo
|
|
*
|
|
* Returns true on success.
|
|
*/
|
|
static bool wallet_stmt2output(sqlite3_stmt *stmt, struct utxo *utxo)
|
|
{
|
|
memcpy(&utxo->txid, sqlite3_column_blob(stmt, 0), sqlite3_column_bytes(stmt, 0));
|
|
utxo->outnum = sqlite3_column_int(stmt, 1);
|
|
utxo->amount = sqlite3_column_int(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);
|
|
return true;
|
|
}
|
|
|
|
bool wallet_update_output_status(struct wallet *w,
|
|
const struct sha256_double *txid,
|
|
const u32 outnum, enum output_status oldstatus,
|
|
enum output_status newstatus)
|
|
{
|
|
sqlite3_stmt *stmt;
|
|
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, newstatus);
|
|
sqlite3_bind_int(stmt, 2, oldstatus);
|
|
sqlite3_bind_blob(stmt, 3, txid, sizeof(*txid), SQLITE_TRANSIENT);
|
|
sqlite3_bind_int(stmt, 4, outnum);
|
|
} else {
|
|
stmt = db_prepare(
|
|
w->db, "UPDATE outputs SET status=? WHERE prev_out_tx=? AND prev_out_index=?");
|
|
sqlite3_bind_int(stmt, 1, newstatus);
|
|
sqlite3_bind_blob(stmt, 2, txid, sizeof(*txid), SQLITE_TRANSIENT);
|
|
sqlite3_bind_int(stmt, 3, outnum);
|
|
}
|
|
db_exec_prepared(w->db, stmt);
|
|
return sqlite3_changes(w->db->sql) > 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 = db_prepare(
|
|
w->db, "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex "
|
|
"FROM outputs WHERE status=?1 OR ?1=255");
|
|
sqlite3_bind_int(stmt, 1, state);
|
|
|
|
results = tal_arr(ctx, struct utxo*, 0);
|
|
for (i=0; sqlite3_step(stmt) == SQLITE_ROW; i++) {
|
|
tal_resize(&results, i+1);
|
|
results[i] = tal(results, struct utxo);
|
|
wallet_stmt2output(stmt, results[i]);
|
|
}
|
|
sqlite3_finalize(stmt);
|
|
|
|
return results;
|
|
}
|
|
|
|
/**
|
|
* unreserve_utxo - Mark a reserved UTXO as available again
|
|
*/
|
|
static void unreserve_utxo(struct wallet *w, const struct utxo *unres)
|
|
{
|
|
if (!wallet_update_output_status(w, &unres->txid, unres->outnum,
|
|
output_state_reserved,
|
|
output_state_available)) {
|
|
fatal("Unable to unreserve output");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* destroy_utxos - Destructor for an array of pointers to utxo
|
|
*/
|
|
static void destroy_utxos(const struct utxo **utxos, struct wallet *w)
|
|
{
|
|
for (size_t i = 0; i < tal_count(utxos); i++)
|
|
unreserve_utxo(w, utxos[i]);
|
|
}
|
|
|
|
void wallet_confirm_utxos(struct wallet *w, const struct utxo **utxos)
|
|
{
|
|
tal_del_destructor2(utxos, destroy_utxos, w);
|
|
for (size_t i = 0; i < tal_count(utxos); i++) {
|
|
if (!wallet_update_output_status(
|
|
w, &utxos[i]->txid, utxos[i]->outnum,
|
|
output_state_reserved, output_state_spent)) {
|
|
fatal("Unable to mark output as spent");
|
|
}
|
|
}
|
|
}
|
|
|
|
const struct utxo **wallet_select_coins(const tal_t *ctx, struct wallet *w,
|
|
const u64 value,
|
|
const u32 feerate_per_kw,
|
|
u64 *fee_estimate, u64 *changesatoshi)
|
|
{
|
|
size_t i = 0;
|
|
struct utxo **available;
|
|
const struct utxo **utxos = tal_arr(ctx, const struct utxo *, 0);
|
|
*fee_estimate = 0;
|
|
|
|
/* We assume two outputs for the weight. */
|
|
u64 satoshi_in = 0, weight = (4 + (8 + 22) * 2 + 4) * 4;
|
|
tal_add_destructor2(utxos, destroy_utxos, w);
|
|
|
|
available = wallet_get_utxos(ctx, w, output_state_available);
|
|
|
|
for (i = 0; i < tal_count(available); i++) {
|
|
tal_resize(&utxos, i + 1);
|
|
utxos[i] = tal_steal(utxos, available[i]);
|
|
|
|
if (!wallet_update_output_status(
|
|
w, &available[i]->txid, available[i]->outnum,
|
|
output_state_available, output_state_reserved))
|
|
fatal("Unable to reserve output");
|
|
|
|
weight += (32 + 4 + 4) * 4;
|
|
if (utxos[i]->is_p2sh)
|
|
weight += 22 * 4;
|
|
|
|
/* Account for witness (1 byte count + sig + key */
|
|
weight += 1 + (1 + 73 + 1 + 33);
|
|
*fee_estimate = weight * feerate_per_kw / 1000;
|
|
satoshi_in += utxos[i]->amount;
|
|
if (satoshi_in >= *fee_estimate + value)
|
|
break;
|
|
}
|
|
tal_free(available);
|
|
|
|
if (satoshi_in < *fee_estimate + value) {
|
|
/* Could not collect enough inputs, cleanup and bail */
|
|
utxos = tal_free(utxos);
|
|
} else {
|
|
*changesatoshi = satoshi_in - value - *fee_estimate;
|
|
|
|
}
|
|
return utxos;
|
|
}
|
|
|
|
bool wallet_can_spend(struct wallet *w, const u8 *script,
|
|
u32 *index, bool *output_is_p2sh)
|
|
{
|
|
struct ext_key ext;
|
|
u64 bip32_max_index = db_get_intvar(w->db, "bip32_max_index", 0);
|
|
u32 i;
|
|
|
|
/* If not one of these, can't be for us. */
|
|
if (is_p2sh(script, NULL))
|
|
*output_is_p2sh = true;
|
|
else if (is_p2wpkh(script, NULL))
|
|
*output_is_p2sh = false;
|
|
else
|
|
return false;
|
|
|
|
for (i = 0; i <= bip32_max_index; i++) {
|
|
u8 *s;
|
|
|
|
if (bip32_key_from_parent(w->bip32_base, i,
|
|
BIP32_FLAG_KEY_PUBLIC, &ext)
|
|
!= WALLY_OK) {
|
|
abort();
|
|
}
|
|
s = scriptpubkey_p2wpkh_derkey(w, ext.pub_key);
|
|
if (*output_is_p2sh) {
|
|
u8 *p2sh = scriptpubkey_p2sh(w, s);
|
|
tal_free(s);
|
|
s = p2sh;
|
|
}
|
|
if (scripteq(s, script)) {
|
|
tal_free(s);
|
|
*index = i;
|
|
return true;
|
|
}
|
|
tal_free(s);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
s64 wallet_get_newindex(struct lightningd *ld)
|
|
{
|
|
u64 newidx = db_get_intvar(ld->wallet->db, "bip32_max_index", 0) + 1;
|
|
|
|
if (newidx == BIP32_INITIAL_HARDENED_CHILD)
|
|
return -1;
|
|
|
|
db_set_intvar(ld->wallet->db, "bip32_max_index", newidx);
|
|
return newidx;
|
|
}
|
|
|
|
void wallet_shachain_init(struct wallet *wallet, struct wallet_shachain *chain)
|
|
{
|
|
sqlite3_stmt *stmt;
|
|
/* 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);
|
|
|
|
chain->id = sqlite3_last_insert_rowid(wallet->db->sql);
|
|
}
|
|
|
|
/* TODO(cdecker) Stolen from shachain, move to some appropriate location */
|
|
static unsigned int count_trailing_zeroes(uint64_t index)
|
|
{
|
|
#if HAVE_BUILTIN_CTZLL
|
|
return index ? (unsigned int)__builtin_ctzll(index) : SHACHAIN_BITS;
|
|
#else
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < SHACHAIN_BITS; i++) {
|
|
if (index & (1ULL << i))
|
|
break;
|
|
}
|
|
return i;
|
|
#endif
|
|
}
|
|
|
|
bool wallet_shachain_add_hash(struct wallet *wallet,
|
|
struct wallet_shachain *chain,
|
|
uint64_t index,
|
|
const struct sha256 *hash)
|
|
{
|
|
sqlite3_stmt *stmt;
|
|
u32 pos = count_trailing_zeroes(index);
|
|
assert(index < SQLITE_MAX_UINT);
|
|
if (!shachain_add_hash(&chain->chain, index, hash)) {
|
|
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);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool wallet_shachain_load(struct wallet *wallet, u64 id,
|
|
struct wallet_shachain *chain)
|
|
{
|
|
int err;
|
|
sqlite3_stmt *stmt;
|
|
chain->id = id;
|
|
shachain_init(&chain->chain);
|
|
|
|
/* Load shachain metadata */
|
|
stmt = db_prepare(wallet->db, "SELECT min_index, num_valid FROM shachains WHERE id=?");
|
|
sqlite3_bind_int64(stmt, 1, id);
|
|
|
|
err = sqlite3_step(stmt);
|
|
if (err != SQLITE_ROW) {
|
|
sqlite3_finalize(stmt);
|
|
return false;
|
|
}
|
|
|
|
chain->chain.min_index = sqlite3_column_int64(stmt, 0);
|
|
chain->chain.num_valid = sqlite3_column_int64(stmt, 1);
|
|
sqlite3_finalize(stmt);
|
|
|
|
/* Load shachain known entries */
|
|
stmt = db_prepare(wallet->db, "SELECT idx, hash, pos FROM shachain_known WHERE shachain_id=?");
|
|
sqlite3_bind_int64(stmt, 1, id);
|
|
|
|
while (sqlite3_step(stmt) == SQLITE_ROW) {
|
|
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));
|
|
}
|
|
|
|
sqlite3_finalize(stmt);
|
|
return true;
|
|
}
|
|
|
|
static bool wallet_peer_load(struct wallet *w, const u64 id, struct peer *peer)
|
|
{
|
|
bool ok = true;
|
|
sqlite3_stmt *stmt = db_query(__func__, w->db, "SELECT id, node_id FROM peers WHERE id=%"PRIu64";", id);
|
|
if (!stmt || sqlite3_step(stmt) != SQLITE_ROW) {
|
|
sqlite3_finalize(stmt);
|
|
return false;
|
|
}
|
|
peer->dbid = sqlite3_column_int64(stmt, 0);
|
|
ok &= sqlite3_column_pubkey(stmt, 1, &peer->id);
|
|
sqlite3_finalize(stmt);
|
|
return ok;
|
|
}
|
|
|
|
bool wallet_peer_by_nodeid(struct wallet *w, const struct pubkey *nodeid,
|
|
struct peer *peer)
|
|
{
|
|
bool ok;
|
|
tal_t *tmpctx = tal_tmpctx(w);
|
|
sqlite3_stmt *stmt = db_prepare(w->db, "SELECT id, node_id FROM peers WHERE node_id=?;");
|
|
sqlite3_bind_pubkey(stmt, 1, nodeid);
|
|
|
|
ok = stmt != NULL && sqlite3_step(stmt) == SQLITE_ROW;
|
|
if (ok) {
|
|
peer->dbid = sqlite3_column_int64(stmt, 0);
|
|
ok &= sqlite3_column_pubkey(stmt, 1, &peer->id);
|
|
} else {
|
|
/* Make sure we mark this as a new peer */
|
|
peer->dbid = 0;
|
|
}
|
|
sqlite3_finalize(stmt);
|
|
tal_free(tmpctx);
|
|
return ok;
|
|
}
|
|
|
|
/**
|
|
* wallet_stmt2channel - Helper to populate a wallet_channel from a sqlite3_stmt
|
|
*
|
|
* Returns true on success.
|
|
*/
|
|
static bool wallet_stmt2channel(struct wallet *w, sqlite3_stmt *stmt,
|
|
struct wallet_channel *chan)
|
|
{
|
|
bool ok = true;
|
|
int col = 0;
|
|
struct channel_info *channel_info;
|
|
u64 remote_config_id;
|
|
|
|
if (!chan->peer) {
|
|
chan->peer = talz(chan, struct peer);
|
|
}
|
|
chan->id = sqlite3_column_int64(stmt, col++);
|
|
chan->peer->dbid = sqlite3_column_int64(stmt, col++);
|
|
wallet_peer_load(w, chan->peer->dbid, chan->peer);
|
|
|
|
if (sqlite3_column_type(stmt, col) != SQLITE_NULL) {
|
|
chan->peer->scid = tal(chan->peer, struct short_channel_id);
|
|
sqlite3_column_short_channel_id(stmt, col++, chan->peer->scid);
|
|
} else {
|
|
chan->peer->scid = NULL;
|
|
col++;
|
|
}
|
|
|
|
chan->peer->our_config.id = sqlite3_column_int64(stmt, col++);
|
|
wallet_channel_config_load(w, chan->peer->our_config.id, &chan->peer->our_config);
|
|
remote_config_id = sqlite3_column_int64(stmt, col++);
|
|
|
|
chan->peer->state = sqlite3_column_int(stmt, col++);
|
|
chan->peer->funder = sqlite3_column_int(stmt, col++);
|
|
chan->peer->channel_flags = sqlite3_column_int(stmt, col++);
|
|
chan->peer->minimum_depth = sqlite3_column_int(stmt, col++);
|
|
chan->peer->next_index[LOCAL] = sqlite3_column_int64(stmt, col++);
|
|
chan->peer->next_index[REMOTE] = sqlite3_column_int64(stmt, col++);
|
|
chan->peer->next_htlc_id = sqlite3_column_int64(stmt, col++);
|
|
|
|
if (sqlite3_column_type(stmt, col) != SQLITE_NULL) {
|
|
assert(sqlite3_column_bytes(stmt, col) == 32);
|
|
chan->peer->funding_txid = tal(chan->peer, struct sha256_double);
|
|
memcpy(chan->peer->funding_txid, sqlite3_column_blob(stmt, col), 32);
|
|
} else {
|
|
chan->peer->funding_txid = NULL;
|
|
}
|
|
col++;
|
|
|
|
chan->peer->funding_outnum = sqlite3_column_int(stmt, col++);
|
|
chan->peer->funding_satoshi = sqlite3_column_int64(stmt, col++);
|
|
chan->peer->remote_funding_locked =
|
|
sqlite3_column_int(stmt, col++) != 0;
|
|
chan->peer->push_msat = sqlite3_column_int64(stmt, col++);
|
|
|
|
if (sqlite3_column_type(stmt, col) != SQLITE_NULL) {
|
|
chan->peer->our_msatoshi = tal(chan->peer, u64);
|
|
*chan->peer->our_msatoshi = sqlite3_column_int64(stmt, col);
|
|
}else {
|
|
chan->peer->our_msatoshi = tal_free(chan->peer->our_msatoshi);
|
|
}
|
|
col++;
|
|
|
|
/* See if we have a valid commit_sig indicating the presence
|
|
* of channel_info */
|
|
if (sqlite3_column_type(stmt, col) != SQLITE_NULL) {
|
|
/* OK, so we have a valid sig, instantiate and/or fill
|
|
* in channel_info */
|
|
if (!chan->peer->channel_info)
|
|
chan->peer->channel_info = tal(chan->peer, struct channel_info);
|
|
channel_info = chan->peer->channel_info;
|
|
|
|
/* Populate channel_info */
|
|
ok &= sqlite3_column_pubkey(stmt, col++, &chan->peer->channel_info->remote_fundingkey);
|
|
ok &= sqlite3_column_pubkey(stmt, col++, &channel_info->theirbase.revocation);
|
|
ok &= sqlite3_column_pubkey(stmt, col++, &channel_info->theirbase.payment);
|
|
ok &= sqlite3_column_pubkey(stmt, col++, &channel_info->theirbase.htlc);
|
|
ok &= sqlite3_column_pubkey(stmt, col++, &channel_info->theirbase.delayed_payment);
|
|
ok &= sqlite3_column_pubkey(stmt, col++, &channel_info->remote_per_commit);
|
|
ok &= sqlite3_column_pubkey(stmt, col++, &channel_info->old_remote_per_commit);
|
|
channel_info->feerate_per_kw[LOCAL] = sqlite3_column_int(stmt, col++);
|
|
channel_info->feerate_per_kw[REMOTE] = sqlite3_column_int(stmt, col++);
|
|
wallet_channel_config_load(w, remote_config_id, &chan->peer->channel_info->their_config);
|
|
} else {
|
|
/* No channel_info, skip positions in the result */
|
|
col += 9;
|
|
}
|
|
|
|
/* Load shachain */
|
|
u64 shachain_id = sqlite3_column_int64(stmt, col++);
|
|
ok &= wallet_shachain_load(w, shachain_id, &chan->peer->their_shachain);
|
|
|
|
/* Do we have a non-null remote_shutdown_scriptpubkey? */
|
|
if (sqlite3_column_type(stmt, col) != SQLITE_NULL) {
|
|
chan->peer->remote_shutdown_scriptpubkey = tal_arr(chan->peer, u8, sqlite3_column_bytes(stmt, col));
|
|
memcpy(chan->peer->remote_shutdown_scriptpubkey, sqlite3_column_blob(stmt, col), sqlite3_column_bytes(stmt, col));
|
|
chan->peer->local_shutdown_idx = sqlite3_column_int64(stmt, col++);
|
|
} else {
|
|
chan->peer->remote_shutdown_scriptpubkey = tal_free(chan->peer->remote_shutdown_scriptpubkey);
|
|
chan->peer->local_shutdown_idx = -1;
|
|
col += 2;
|
|
}
|
|
|
|
/* Do we have a last_sent_commit, if yes, populate */
|
|
if (sqlite3_column_type(stmt, col) != SQLITE_NULL) {
|
|
if (!chan->peer->last_sent_commit) {
|
|
chan->peer->last_sent_commit = tal(chan->peer, struct changed_htlc);
|
|
}
|
|
chan->peer->last_sent_commit->newstate = sqlite3_column_int64(stmt, col++);
|
|
chan->peer->last_sent_commit->id = sqlite3_column_int64(stmt, col++);
|
|
} else {
|
|
chan->peer->last_sent_commit = tal_free(chan->peer->last_sent_commit);
|
|
col += 2;
|
|
}
|
|
|
|
/* Do we have last_tx? If so, populate. */
|
|
if (sqlite3_column_type(stmt, col) != SQLITE_NULL) {
|
|
chan->peer->last_tx = sqlite3_column_tx(chan->peer, stmt, col++);
|
|
chan->peer->last_sig = tal(chan->peer, secp256k1_ecdsa_signature);
|
|
sqlite3_column_signature(stmt, col++, chan->peer->last_sig);
|
|
} else {
|
|
chan->peer->last_tx = tal_free(chan->peer->last_tx);
|
|
chan->peer->last_sig = tal_free(chan->peer->last_sig);
|
|
col += 2;
|
|
}
|
|
|
|
assert(col == 34);
|
|
|
|
chan->peer->channel = chan;
|
|
|
|
return ok;
|
|
}
|
|
|
|
/* List of fields to retrieve from the channels DB table, in the order
|
|
* that wallet_stmt2channel understands and will parse correctly */
|
|
const char *channel_fields =
|
|
"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";
|
|
|
|
bool wallet_channel_load(struct wallet *w, const u64 id,
|
|
struct wallet_channel *chan)
|
|
{
|
|
bool ok;
|
|
/* The explicit query that matches the columns and their order in
|
|
* wallet_stmt2channel. */
|
|
sqlite3_stmt *stmt = db_query(
|
|
__func__, w->db, "SELECT %s FROM channels WHERE id=%" PRIu64 ";",
|
|
channel_fields, id);
|
|
|
|
if (!stmt || sqlite3_step(stmt) != SQLITE_ROW) {
|
|
sqlite3_finalize(stmt);
|
|
return false;
|
|
}
|
|
|
|
ok = wallet_stmt2channel(w, stmt, chan);
|
|
|
|
sqlite3_finalize(stmt);
|
|
return ok;
|
|
}
|
|
|
|
bool wallet_channels_load_active(struct wallet *w, struct list_head *peers)
|
|
{
|
|
bool ok = true;
|
|
/* Channels are active if they have reached at least the
|
|
* opening state and they are not marked as complete */
|
|
sqlite3_stmt *stmt = db_query(
|
|
__func__, w->db, "SELECT %s FROM channels WHERE state >= %d AND state != %d;",
|
|
channel_fields, OPENINGD, CLOSINGD_COMPLETE);
|
|
|
|
int count = 0;
|
|
while (ok && stmt && sqlite3_step(stmt) == SQLITE_ROW) {
|
|
struct wallet_channel *c = talz(w, struct wallet_channel);
|
|
ok &= wallet_stmt2channel(w, stmt, c);
|
|
list_add(peers, &c->peer->list);
|
|
count++;
|
|
}
|
|
log_debug(w->log, "Loaded %d channels from DB", count);
|
|
sqlite3_finalize(stmt);
|
|
return ok;
|
|
}
|
|
|
|
void wallet_channel_config_save(struct wallet *w, struct channel_config *cc)
|
|
{
|
|
sqlite3_stmt *stmt;
|
|
/* Is this an update? If not insert a stub first */
|
|
if (!cc->id) {
|
|
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(w->db, "UPDATE channel_configs SET"
|
|
" dust_limit_satoshis=?,"
|
|
" max_htlc_value_in_flight_msat=?,"
|
|
" channel_reserve_satoshis=?,"
|
|
" htlc_minimum_msat=?,"
|
|
" to_self_delay=?,"
|
|
" max_accepted_htlcs=?"
|
|
" WHERE id=?;");
|
|
sqlite3_bind_int64(stmt, 1, cc->dust_limit_satoshis);
|
|
sqlite3_bind_int64(stmt, 2, cc->max_htlc_value_in_flight_msat);
|
|
sqlite3_bind_int64(stmt, 3, cc->channel_reserve_satoshis);
|
|
sqlite3_bind_int64(stmt, 4, cc->htlc_minimum_msat);
|
|
sqlite3_bind_int(stmt, 5, cc->to_self_delay);
|
|
sqlite3_bind_int(stmt, 6, cc->max_accepted_htlcs);
|
|
sqlite3_bind_int64(stmt, 7, cc->id);
|
|
db_exec_prepared(w->db, stmt);
|
|
}
|
|
|
|
bool wallet_channel_config_load(struct wallet *w, const u64 id,
|
|
struct channel_config *cc)
|
|
{
|
|
bool ok = true;
|
|
int col = 1;
|
|
const char *query =
|
|
"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_query(__func__, w->db, query, id);
|
|
if (!stmt || sqlite3_step(stmt) != SQLITE_ROW) {
|
|
sqlite3_finalize(stmt);
|
|
return false;
|
|
}
|
|
cc->id = id;
|
|
cc->dust_limit_satoshis = sqlite3_column_int64(stmt, col++);
|
|
cc->max_htlc_value_in_flight_msat = sqlite3_column_int64(stmt, col++);
|
|
cc->channel_reserve_satoshis = sqlite3_column_int64(stmt, col++);
|
|
cc->htlc_minimum_msat = sqlite3_column_int64(stmt, col++);
|
|
cc->to_self_delay = sqlite3_column_int(stmt, col++);
|
|
cc->max_accepted_htlcs = sqlite3_column_int(stmt, col++);
|
|
assert(col == 7);
|
|
sqlite3_finalize(stmt);
|
|
return ok;
|
|
}
|
|
|
|
void wallet_channel_save(struct wallet *w, struct wallet_channel *chan){
|
|
struct peer *p = chan->peer;
|
|
tal_t *tmpctx = tal_tmpctx(w);
|
|
sqlite3_stmt *stmt;
|
|
|
|
if (p->dbid == 0) {
|
|
/* Need to store the peer first */
|
|
stmt = db_prepare(w->db, "INSERT INTO peers (node_id) VALUES (?);");
|
|
sqlite3_bind_pubkey(stmt, 1, &chan->peer->id);
|
|
db_exec_prepared(w->db, stmt);
|
|
p->dbid = sqlite3_last_insert_rowid(w->db->sql);
|
|
}
|
|
|
|
/* Insert a stub, that we can update, unifies INSERT and UPDATE paths */
|
|
if (chan->id == 0) {
|
|
stmt = db_prepare(w->db, "INSERT INTO channels (peer_id) VALUES (?);");
|
|
sqlite3_bind_int64(stmt, 1, p->dbid);
|
|
db_exec_prepared(w->db, stmt);
|
|
chan->id = sqlite3_last_insert_rowid(w->db->sql);
|
|
}
|
|
|
|
/* Need to initialize the shachain first so we get an id */
|
|
if (p->their_shachain.id == 0) {
|
|
wallet_shachain_init(w, &p->their_shachain);
|
|
}
|
|
|
|
wallet_channel_config_save(w, &p->our_config);
|
|
|
|
/* Now do the real update */
|
|
stmt = db_prepare(w->db, tal_fmt(w, "UPDATE channels SET"
|
|
" shachain_remote_id=?,"
|
|
" short_channel_id=?,"
|
|
" state=?,"
|
|
" funder=?,"
|
|
" channel_flags=?,"
|
|
" minimum_depth=?,"
|
|
" next_index_local=?,"
|
|
" next_index_remote=?,"
|
|
" next_htlc_id=?,"
|
|
" funding_tx_id=?,"
|
|
" funding_tx_outnum=?,"
|
|
" funding_satoshi=?,"
|
|
" funding_locked_remote=?,"
|
|
" push_msatoshi=?,"
|
|
" msatoshi_local=?,"
|
|
" shutdown_scriptpubkey_remote=?,"
|
|
" shutdown_keyidx_local=?,"
|
|
" channel_config_local=?,"
|
|
" last_tx=?, last_sig=?"
|
|
" WHERE id=?"));
|
|
sqlite3_bind_int64(stmt, 1, p->their_shachain.id);
|
|
if (p->scid)
|
|
sqlite3_bind_short_channel_id(stmt, 2, p->scid);
|
|
sqlite3_bind_int(stmt, 3, p->state);
|
|
sqlite3_bind_int(stmt, 4, p->funder);
|
|
sqlite3_bind_int(stmt, 5, p->channel_flags);
|
|
sqlite3_bind_int(stmt, 6, p->minimum_depth);
|
|
|
|
sqlite3_bind_int64(stmt, 7, p->next_index[LOCAL]);
|
|
sqlite3_bind_int64(stmt, 8, p->next_index[REMOTE]);
|
|
sqlite3_bind_int64(stmt, 9, p->next_htlc_id);
|
|
|
|
if (p->funding_txid)
|
|
sqlite3_bind_blob(stmt, 10, p->funding_txid, sizeof(*p->funding_txid), SQLITE_TRANSIENT);
|
|
|
|
sqlite3_bind_int(stmt, 11, p->funding_outnum);
|
|
sqlite3_bind_int64(stmt, 12, p->funding_satoshi);
|
|
sqlite3_bind_int(stmt, 13, p->remote_funding_locked);
|
|
sqlite3_bind_int64(stmt, 14, p->push_msat);
|
|
|
|
if (p->our_msatoshi)
|
|
sqlite3_bind_int64(stmt, 15, *p->our_msatoshi);
|
|
|
|
if (p->remote_shutdown_scriptpubkey)
|
|
sqlite3_bind_blob(stmt, 16, p->remote_shutdown_scriptpubkey,
|
|
tal_len(p->remote_shutdown_scriptpubkey),
|
|
SQLITE_TRANSIENT);
|
|
|
|
sqlite3_bind_int64(stmt, 17, p->local_shutdown_idx);
|
|
sqlite3_bind_int64(stmt, 18, p->our_config.id);
|
|
if (p->last_tx)
|
|
sqlite3_bind_tx(stmt, 19, p->last_tx);
|
|
if (p->last_sig)
|
|
sqlite3_bind_signature(stmt, 20, p->last_sig);
|
|
sqlite3_bind_int64(stmt, 21, chan->id);
|
|
db_exec_prepared(w->db, stmt);
|
|
|
|
if (chan->peer->channel_info) {
|
|
wallet_channel_config_save(w, &p->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=?"
|
|
" WHERE id=?");
|
|
sqlite3_bind_pubkey(stmt, 1, &p->channel_info->remote_fundingkey);
|
|
sqlite3_bind_pubkey(stmt, 2, &p->channel_info->theirbase.revocation);
|
|
sqlite3_bind_pubkey(stmt, 3, &p->channel_info->theirbase.payment);
|
|
sqlite3_bind_pubkey(stmt, 4, &p->channel_info->theirbase.htlc);
|
|
sqlite3_bind_pubkey(stmt, 5, &p->channel_info->theirbase.delayed_payment);
|
|
sqlite3_bind_pubkey(stmt, 6, &p->channel_info->remote_per_commit);
|
|
sqlite3_bind_pubkey(stmt, 7, &p->channel_info->old_remote_per_commit);
|
|
sqlite3_bind_int(stmt, 8, p->channel_info->feerate_per_kw[LOCAL]);
|
|
sqlite3_bind_int(stmt, 9, p->channel_info->feerate_per_kw[REMOTE]);
|
|
sqlite3_bind_int64(stmt, 10, p->channel_info->their_config.id);
|
|
sqlite3_bind_int64(stmt, 11, chan->id);
|
|
db_exec_prepared(w->db, stmt);
|
|
}
|
|
|
|
/* If we have a last_sent_commit, store it */
|
|
if (chan->peer->last_sent_commit) {
|
|
stmt = db_prepare(w->db,
|
|
"UPDATE channels SET"
|
|
" last_sent_commit_state=?,"
|
|
" last_sent_commit_id=?"
|
|
" WHERE id=?");
|
|
sqlite3_bind_int(stmt, 1, p->last_sent_commit->newstate);
|
|
sqlite3_bind_int64(stmt, 2, p->last_sent_commit->id);
|
|
sqlite3_bind_int64(stmt, 3, chan->id);
|
|
db_exec_prepared(w->db, stmt);
|
|
}
|
|
|
|
tal_free(tmpctx);
|
|
}
|
|
|
|
int wallet_extract_owned_outputs(struct wallet *w, const struct bitcoin_tx *tx,
|
|
u64 *total_satoshi)
|
|
{
|
|
int num_utxos = 0;
|
|
for (size_t output = 0; output < tal_count(tx->output); output++) {
|
|
struct utxo *utxo;
|
|
u32 index;
|
|
bool is_p2sh;
|
|
|
|
if (!wallet_can_spend(w, tx->output[output].script, &index,
|
|
&is_p2sh))
|
|
continue;
|
|
|
|
utxo = tal(w, struct utxo);
|
|
utxo->keyindex = index;
|
|
utxo->is_p2sh = is_p2sh;
|
|
utxo->amount = tx->output[output].amount;
|
|
utxo->status = output_state_available;
|
|
bitcoin_txid(tx, &utxo->txid);
|
|
utxo->outnum = output;
|
|
log_debug(w->log, "Owning output %zu %"PRIu64" (%s) txid %s",
|
|
output, tx->output[output].amount,
|
|
is_p2sh ? "P2SH" : "SEGWIT",
|
|
type_to_string(ltmp, struct sha256_double,
|
|
&utxo->txid));
|
|
|
|
if (!wallet_add_utxo(w, utxo, is_p2sh ? p2sh_wpkh : our_change)) {
|
|
tal_free(utxo);
|
|
return -1;
|
|
}
|
|
*total_satoshi += utxo->amount;
|
|
num_utxos++;
|
|
}
|
|
return num_utxos;
|
|
}
|
|
|
|
void wallet_htlc_save_in(struct wallet *wallet,
|
|
const struct wallet_channel *chan, struct htlc_in *in)
|
|
{
|
|
tal_t *tmpctx = tal_tmpctx(wallet);
|
|
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) VALUES "
|
|
"(?, ?, ?, ?, ?, ?, ?, ?, ?, ?);");
|
|
|
|
sqlite3_bind_int64(stmt, 1, chan->id);
|
|
sqlite3_bind_int64(stmt, 2, in->key.id);
|
|
sqlite3_bind_int(stmt, 3, DIRECTION_INCOMING);
|
|
sqlite3_bind_int64(stmt, 4, in->msatoshi);
|
|
sqlite3_bind_int(stmt, 5, in->cltv_expiry);
|
|
sqlite3_bind_sha256(stmt, 6, &in->payment_hash);
|
|
|
|
if (in->preimage)
|
|
sqlite3_bind_preimage(stmt, 7, in->preimage);
|
|
sqlite3_bind_int(stmt, 8, in->hstate);
|
|
|
|
sqlite3_bind_blob(stmt, 9, &in->shared_secret,
|
|
sizeof(in->shared_secret), SQLITE_TRANSIENT);
|
|
|
|
sqlite3_bind_blob(stmt, 10, &in->onion_routing_packet,
|
|
sizeof(in->onion_routing_packet), SQLITE_TRANSIENT);
|
|
|
|
db_exec_prepared(wallet->db, stmt);
|
|
in->dbid = sqlite3_last_insert_rowid(wallet->db->sql);
|
|
tal_free(tmpctx);
|
|
}
|
|
|
|
void wallet_htlc_save_out(struct wallet *wallet,
|
|
const struct wallet_channel *chan,
|
|
struct htlc_out *out)
|
|
{
|
|
tal_t *tmpctx = tal_tmpctx(wallet);
|
|
sqlite3_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(
|
|
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->id);
|
|
sqlite3_bind_int64(stmt, 2, out->key.id);
|
|
sqlite3_bind_int(stmt, 3, DIRECTION_OUTGOING);
|
|
if (out->in)
|
|
sqlite3_bind_int64(stmt, 4, out->in->dbid);
|
|
sqlite3_bind_int64(stmt, 5, out->msatoshi);
|
|
sqlite3_bind_int(stmt, 6, out->cltv_expiry);
|
|
sqlite3_bind_sha256(stmt, 7, &out->payment_hash);
|
|
|
|
if (out->preimage)
|
|
sqlite3_bind_preimage(stmt, 8,out->preimage);
|
|
sqlite3_bind_int(stmt, 9, out->hstate);
|
|
|
|
sqlite3_bind_blob(stmt, 10, &out->onion_routing_packet,
|
|
sizeof(out->onion_routing_packet), SQLITE_TRANSIENT);
|
|
|
|
db_exec_prepared(wallet->db, stmt);
|
|
|
|
out->dbid = sqlite3_last_insert_rowid(wallet->db->sql);
|
|
tal_free(tmpctx);
|
|
}
|
|
|
|
void wallet_htlc_update(struct wallet *wallet, const u64 htlc_dbid,
|
|
const enum htlc_state new_state,
|
|
const struct preimage *payment_key)
|
|
{
|
|
sqlite3_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=? WHERE id=?");
|
|
|
|
sqlite3_bind_int(stmt, 1, new_state);
|
|
sqlite3_bind_int64(stmt, 3, htlc_dbid);
|
|
|
|
if (payment_key)
|
|
sqlite3_bind_preimage(stmt, 2, payment_key);
|
|
|
|
db_exec_prepared(wallet->db, stmt);
|
|
}
|
|
|
|
static bool wallet_stmt2htlc_in(const struct wallet_channel *channel,
|
|
sqlite3_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->key.peer = channel->peer;
|
|
in->msatoshi = sqlite3_column_int64(stmt, 2);
|
|
in->cltv_expiry = sqlite3_column_int(stmt, 3);
|
|
in->hstate = sqlite3_column_int(stmt, 4);
|
|
|
|
sqlite3_column_sha256(stmt, 5, &in->payment_hash);
|
|
|
|
assert(sqlite3_column_bytes(stmt, 6) == sizeof(struct secret));
|
|
memcpy(&in->shared_secret, sqlite3_column_blob(stmt, 6),
|
|
sizeof(struct secret));
|
|
|
|
if (sqlite3_column_type(stmt, 7) != SQLITE_NULL) {
|
|
in->preimage = tal(in, struct preimage);
|
|
sqlite3_column_preimage(stmt, 7, in->preimage);
|
|
} else {
|
|
in->preimage = NULL;
|
|
}
|
|
|
|
assert(sqlite3_column_bytes(stmt, 8) == sizeof(in->onion_routing_packet));
|
|
memcpy(&in->onion_routing_packet, sqlite3_column_blob(stmt, 8),
|
|
sizeof(in->onion_routing_packet));
|
|
|
|
in->failuremsg = NULL;
|
|
in->malformed = 0;
|
|
|
|
return ok;
|
|
}
|
|
static bool wallet_stmt2htlc_out(const struct wallet_channel *channel,
|
|
sqlite3_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->key.peer = channel->peer;
|
|
out->msatoshi = sqlite3_column_int64(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);
|
|
|
|
if (sqlite3_column_type(stmt, 6) != SQLITE_NULL) {
|
|
out->origin_htlc_id = sqlite3_column_int64(stmt, 6);
|
|
} else {
|
|
out->origin_htlc_id = 0;
|
|
}
|
|
|
|
if (sqlite3_column_type(stmt, 7) != SQLITE_NULL) {
|
|
out->preimage = tal(out, struct preimage);
|
|
sqlite3_column_preimage(stmt, 7, out->preimage);
|
|
} else {
|
|
out->preimage = NULL;
|
|
}
|
|
|
|
assert(sqlite3_column_bytes(stmt, 8) == sizeof(out->onion_routing_packet));
|
|
memcpy(&out->onion_routing_packet, sqlite3_column_blob(stmt, 8),
|
|
sizeof(out->onion_routing_packet));
|
|
|
|
out->failuremsg = NULL;
|
|
out->malformed = 0;
|
|
|
|
/* Need to defer wiring until we can look up all incoming
|
|
* htlcs, will wire using origin_htlc_id */
|
|
out->in = NULL;
|
|
|
|
return ok;
|
|
}
|
|
|
|
bool wallet_htlcs_load_for_channel(struct wallet *wallet,
|
|
struct wallet_channel *chan,
|
|
struct htlc_in_map *htlcs_in,
|
|
struct htlc_out_map *htlcs_out)
|
|
{
|
|
bool ok = true;
|
|
int incount = 0, outcount = 0;
|
|
|
|
log_debug(wallet->log, "Loading HTLCs for channel %"PRIu64, chan->id);
|
|
sqlite3_stmt *stmt = db_query(
|
|
__func__, wallet->db,
|
|
"SELECT id, channel_htlc_id, msatoshi, cltv_expiry, hstate, "
|
|
"payment_hash, shared_secret, payment_key, routing_onion FROM channel_htlcs WHERE "
|
|
"direction=%d AND channel_id=%" PRIu64 " AND hstate != %d",
|
|
DIRECTION_INCOMING, chan->id, SENT_REMOVE_ACK_REVOCATION);
|
|
|
|
if (!stmt) {
|
|
log_broken(wallet->log, "Could not select htlc_ins");
|
|
return false;
|
|
}
|
|
|
|
while (ok && stmt && sqlite3_step(stmt) == SQLITE_ROW) {
|
|
struct htlc_in *in = tal(chan, struct htlc_in);
|
|
ok &= wallet_stmt2htlc_in(chan, stmt, in);
|
|
connect_htlc_in(htlcs_in, in);
|
|
ok &= htlc_in_check(in, "wallet_htlcs_load") != NULL;
|
|
incount++;
|
|
}
|
|
sqlite3_finalize(stmt);
|
|
|
|
stmt = db_query(
|
|
__func__, wallet->db,
|
|
"SELECT id, channel_htlc_id, msatoshi, cltv_expiry, hstate, "
|
|
"payment_hash, origin_htlc, payment_key, routing_onion FROM channel_htlcs WHERE "
|
|
"direction=%d AND channel_id=%" PRIu64 " AND hstate != %d",
|
|
DIRECTION_OUTGOING, chan->id, RCVD_REMOVE_ACK_REVOCATION);
|
|
|
|
if (!stmt) {
|
|
log_broken(wallet->log, "Could not select htlc_outs");
|
|
return false;
|
|
}
|
|
|
|
while (ok && stmt && sqlite3_step(stmt) == SQLITE_ROW) {
|
|
struct htlc_out *out = tal(chan, struct htlc_out);
|
|
ok &= wallet_stmt2htlc_out(chan, stmt, out);
|
|
connect_htlc_out(htlcs_out, out);
|
|
/* Cannot htlc_out_check because we haven't wired the
|
|
* dependencies in yet */
|
|
outcount++;
|
|
}
|
|
sqlite3_finalize(stmt);
|
|
log_debug(wallet->log, "Restored %d incoming and %d outgoing HTLCS", incount, outcount);
|
|
|
|
return ok;
|
|
}
|
|
|
|
bool wallet_htlcs_reconnect(struct wallet *wallet,
|
|
struct htlc_in_map *htlcs_in,
|
|
struct htlc_out_map *htlcs_out)
|
|
{
|
|
struct htlc_in_map_iter ini;
|
|
struct htlc_out_map_iter outi;
|
|
struct htlc_in *hin;
|
|
struct htlc_out *hout;
|
|
|
|
for (hout = htlc_out_map_first(htlcs_out, &outi); hout;
|
|
hout = htlc_out_map_next(htlcs_out, &outi)) {
|
|
|
|
if (hout->origin_htlc_id == 0) {
|
|
continue;
|
|
}
|
|
|
|
for (hin = htlc_in_map_first(htlcs_in, &ini); hin;
|
|
hin = htlc_in_map_next(htlcs_in, &ini)) {
|
|
if (hout->origin_htlc_id == hin->dbid) {
|
|
log_debug(wallet->log,
|
|
"Found corresponding htlc_in %" PRIu64
|
|
" for htlc_out %" PRIu64,
|
|
hin->dbid, hout->dbid);
|
|
hout->in = hin;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!hout->in) {
|
|
log_broken(
|
|
wallet->log,
|
|
"Unable to find corresponding htlc_in %"PRIu64" for htlc_out %"PRIu64,
|
|
hout->origin_htlc_id, hout->dbid);
|
|
}
|
|
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void wallet_invoice_save(struct wallet *wallet, struct invoice *inv)
|
|
{
|
|
/* 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... */
|
|
sqlite3_stmt *stmt;
|
|
if (!inv->id) {
|
|
stmt = db_prepare(wallet->db,
|
|
"INSERT INTO invoices (payment_hash, payment_key, state, msatoshi, label, expiry_time) VALUES (?, ?, ?, ?, ?, ?);");
|
|
|
|
sqlite3_bind_blob(stmt, 1, &inv->rhash, sizeof(inv->rhash), SQLITE_TRANSIENT);
|
|
sqlite3_bind_blob(stmt, 2, &inv->r, sizeof(inv->r), SQLITE_TRANSIENT);
|
|
sqlite3_bind_int(stmt, 3, inv->state);
|
|
sqlite3_bind_int64(stmt, 4, inv->msatoshi);
|
|
sqlite3_bind_text(stmt, 5, inv->label, strlen(inv->label), SQLITE_TRANSIENT);
|
|
sqlite3_bind_int64(stmt, 6, inv->expiry_time);
|
|
|
|
db_exec_prepared(wallet->db, stmt);
|
|
|
|
inv->id = sqlite3_last_insert_rowid(wallet->db->sql);
|
|
} else {
|
|
stmt = db_prepare(wallet->db, "UPDATE invoices SET state=? WHERE id=?;");
|
|
|
|
sqlite3_bind_int(stmt, 1, inv->state);
|
|
sqlite3_bind_int64(stmt, 2, inv->id);
|
|
|
|
db_exec_prepared(wallet->db, stmt);
|
|
}
|
|
}
|
|
|
|
static bool wallet_stmt2invoice(sqlite3_stmt *stmt, struct invoice *inv)
|
|
{
|
|
inv->id = sqlite3_column_int64(stmt, 0);
|
|
inv->state = sqlite3_column_int(stmt, 1);
|
|
|
|
assert(sqlite3_column_bytes(stmt, 2) == sizeof(struct preimage));
|
|
memcpy(&inv->r, sqlite3_column_blob(stmt, 2), sqlite3_column_bytes(stmt, 2));
|
|
|
|
assert(sqlite3_column_bytes(stmt, 3) == sizeof(struct sha256));
|
|
memcpy(&inv->rhash, sqlite3_column_blob(stmt, 3), sqlite3_column_bytes(stmt, 3));
|
|
|
|
inv->label = tal_strndup(inv, sqlite3_column_blob(stmt, 4), sqlite3_column_bytes(stmt, 4));
|
|
inv->msatoshi = sqlite3_column_int64(stmt, 5);
|
|
inv->expiry_time = sqlite3_column_int64(stmt, 6);
|
|
return true;
|
|
}
|
|
|
|
bool wallet_invoices_load(struct wallet *wallet, struct invoices *invs)
|
|
{
|
|
struct invoice *i;
|
|
int count = 0;
|
|
sqlite3_stmt *stmt = db_query(__func__, wallet->db,
|
|
"SELECT id, state, payment_key, payment_hash, "
|
|
"label, msatoshi, expiry_time FROM invoices;");
|
|
if (!stmt) {
|
|
log_broken(wallet->log, "Could not load invoices");
|
|
return false;
|
|
}
|
|
|
|
while (sqlite3_step(stmt) == SQLITE_ROW) {
|
|
i = tal(invs, struct invoice);
|
|
if (!wallet_stmt2invoice(stmt, i)) {
|
|
log_broken(wallet->log, "Error deserializing invoice");
|
|
return false;
|
|
}
|
|
invoice_add(invs, i);
|
|
count++;
|
|
}
|
|
|
|
log_debug(wallet->log, "Loaded %d invoices from DB", count);
|
|
return true;
|
|
}
|
|
|
|
bool wallet_invoice_remove(struct wallet *wallet, struct invoice *inv)
|
|
{
|
|
sqlite3_stmt *stmt = db_prepare(wallet->db, "DELETE FROM invoices WHERE id=?");
|
|
sqlite3_bind_int64(stmt, 1, inv->id);
|
|
db_exec_prepared(wallet->db, stmt);
|
|
return sqlite3_changes(wallet->db->sql) == 1;
|
|
}
|
|
|
|
struct htlc_stub *wallet_htlc_stubs(tal_t *ctx, struct wallet *wallet,
|
|
struct wallet_channel *chan)
|
|
{
|
|
struct htlc_stub *stubs;
|
|
struct sha256 payment_hash;
|
|
sqlite3_stmt *stmt = db_prepare(wallet->db,
|
|
"SELECT channel_id, direction, cltv_expiry, payment_hash "
|
|
"FROM channel_htlcs WHERE channel_id = ?;");
|
|
|
|
sqlite3_bind_int64(stmt, 1, chan->id);
|
|
|
|
stubs = tal_arr(ctx, struct htlc_stub, 0);
|
|
|
|
while (sqlite3_step(stmt) == SQLITE_ROW) {
|
|
int n = tal_count(stubs);
|
|
tal_resize(&stubs, n+1);
|
|
|
|
assert(sqlite3_column_int64(stmt, 0) == chan->id);
|
|
|
|
/* FIXME: merge these two enums */
|
|
stubs[n].owner = sqlite3_column_int(stmt, 1)==DIRECTION_INCOMING?REMOTE:LOCAL;
|
|
stubs[n].cltv_expiry = sqlite3_column_int(stmt, 2);
|
|
|
|
sqlite3_column_sha256(stmt, 3, &payment_hash);
|
|
ripemd160(&stubs[n].ripemd, payment_hash.u.u8, sizeof(payment_hash.u));
|
|
}
|
|
sqlite3_finalize(stmt);
|
|
return stubs;
|
|
}
|
|
|
|
bool wallet_payment_add(struct wallet *wallet,
|
|
struct wallet_payment *payment)
|
|
{
|
|
sqlite3_stmt *stmt;
|
|
|
|
/* Don't attempt to add the same payment twice */
|
|
assert(!payment->id);
|
|
|
|
stmt = db_prepare(
|
|
wallet->db,
|
|
"INSERT INTO payments ("
|
|
" status,"
|
|
" payment_hash,"
|
|
" direction,"
|
|
" destination,"
|
|
" msatoshi,"
|
|
" timestamp"
|
|
") VALUES (?, ?, ?, ?, ?, ?);");
|
|
|
|
sqlite3_bind_int(stmt, 1, payment->status);
|
|
sqlite3_bind_sha256(stmt, 2, &payment->payment_hash);
|
|
sqlite3_bind_int(stmt, 3, payment->incoming?DIRECTION_INCOMING:DIRECTION_OUTGOING);
|
|
|
|
if (payment->destination)
|
|
sqlite3_bind_pubkey(stmt, 4, payment->destination);
|
|
else
|
|
sqlite3_bind_null(stmt, 4);
|
|
|
|
sqlite3_bind_int64(stmt, 5, payment->msatoshi);
|
|
|
|
sqlite3_bind_int(stmt, 6, payment->timestamp);
|
|
|
|
db_exec_prepared(wallet->db, stmt);
|
|
payment->id = sqlite3_last_insert_rowid(wallet->db->sql);
|
|
return true;
|
|
}
|
|
|
|
static struct wallet_payment *wallet_stmt2payment(const tal_t *ctx,
|
|
sqlite3_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->incoming = sqlite3_column_int(stmt, 2) == DIRECTION_INCOMING;
|
|
|
|
if (sqlite3_column_type(stmt, 3) != SQLITE_NULL) {
|
|
payment->destination = tal(payment, struct pubkey);
|
|
sqlite3_column_pubkey(stmt, 3, payment->destination);
|
|
} else {
|
|
payment->destination = NULL;
|
|
}
|
|
|
|
payment->msatoshi = sqlite3_column_int64(stmt, 4);
|
|
sqlite3_column_sha256(stmt, 5, &payment->payment_hash);
|
|
|
|
payment->timestamp = sqlite3_column_int(stmt, 6);
|
|
return payment;
|
|
}
|
|
|
|
struct wallet_payment *
|
|
wallet_payment_by_hash(const tal_t *ctx, struct wallet *wallet,
|
|
const struct sha256 *payment_hash)
|
|
{
|
|
sqlite3_stmt *stmt;
|
|
struct wallet_payment *payment = NULL;
|
|
|
|
stmt = db_prepare(wallet->db,
|
|
"SELECT id, status, direction, destination,"
|
|
"msatoshi , payment_hash, timestamp "
|
|
"FROM payments "
|
|
"WHERE payment_hash = ?");
|
|
|
|
sqlite3_bind_sha256(stmt, 1, payment_hash);
|
|
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
|
payment = wallet_stmt2payment(ctx, stmt);
|
|
}
|
|
sqlite3_finalize(stmt);
|
|
return payment;
|
|
}
|
|
|
|
void wallet_payment_set_status(struct wallet *wallet,
|
|
const struct sha256 *payment_hash,
|
|
const enum wallet_payment_status newstatus)
|
|
{
|
|
sqlite3_stmt *stmt;
|
|
|
|
stmt = db_prepare(wallet->db,
|
|
"UPDATE payments SET status=? "
|
|
"WHERE payment_hash=?");
|
|
|
|
sqlite3_bind_int(stmt, 1, newstatus);
|
|
sqlite3_bind_sha256(stmt, 2, payment_hash);
|
|
db_exec_prepared(wallet->db, stmt);
|
|
}
|
|
|
|
const struct wallet_payment **wallet_payment_list(const tal_t *ctx,
|
|
struct wallet *wallet)
|
|
{
|
|
const struct wallet_payment **payments;
|
|
sqlite3_stmt *stmt;
|
|
|
|
payments = tal_arr(ctx, const struct wallet_payment *, 0);
|
|
stmt = db_prepare(
|
|
wallet->db,
|
|
"SELECT id, status, direction, destination, "
|
|
"msatoshi , payment_hash, timestamp "
|
|
"FROM payments;");
|
|
|
|
for (int i = 0; sqlite3_step(stmt) == SQLITE_ROW; i++) {
|
|
tal_resize(&payments, i+1);
|
|
payments[i] = wallet_stmt2payment(payments, stmt);
|
|
}
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
return payments;
|
|
}
|
|
|