mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-03-03 10:46:58 +01:00
wallet: Implement shachain persistence
This needed a rather annoying hack since sqlite3 can only store integers up to 2^63, so I just squash it down/invert it, and hope that we never ever have more than 2^63 updates.
This commit is contained in:
parent
b0c64909e7
commit
34baf05973
2 changed files with 152 additions and 0 deletions
112
wallet/wallet.c
112
wallet/wallet.c
|
@ -2,8 +2,11 @@
|
|||
|
||||
#include <bitcoin/script.h>
|
||||
#include <ccan/str/hex/hex.h>
|
||||
#include <inttypes.h>
|
||||
#include <lightningd/lightningd.h>
|
||||
|
||||
#define SQLITE_MAX_UINT 0x7FFFFFFFFFFFFFFF
|
||||
|
||||
struct wallet *wallet_new(const tal_t *ctx, struct log *log)
|
||||
{
|
||||
struct wallet *wallet = tal(ctx, struct wallet);
|
||||
|
@ -232,3 +235,112 @@ s64 wallet_get_newindex(struct lightningd *ld)
|
|||
db_set_intvar(ld->wallet->db, "bip32_max_index", newidx);
|
||||
return newidx;
|
||||
}
|
||||
|
||||
bool wallet_shachain_init(struct wallet *wallet, struct wallet_shachain *chain)
|
||||
{
|
||||
/* Create shachain */
|
||||
shachain_init(&chain->chain);
|
||||
if (!db_exec(__func__, wallet->db,
|
||||
"INSERT INTO shachains (min_index, num_valid) VALUES (0,0);")) {
|
||||
return false;
|
||||
}
|
||||
chain->id = sqlite3_last_insert_rowid(wallet->db->sql);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* TODO(cdecker) Stolen from shachain, move to some appropriate location */
|
||||
static unsigned int count_trailing_zeroes(shachain_index_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,
|
||||
shachain_index_t index,
|
||||
const struct sha256 *hash)
|
||||
{
|
||||
tal_t *tmpctx = tal_tmpctx(wallet);
|
||||
bool ok = true;
|
||||
u32 pos = count_trailing_zeroes(index);
|
||||
assert(index < SQLITE_MAX_UINT);
|
||||
char *hexhash = tal_hexstr(tmpctx, hash, sizeof(struct sha256));
|
||||
if (!shachain_add_hash(&chain->chain, index, hash)) {
|
||||
tal_free(tmpctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
db_begin_transaction(wallet->db);
|
||||
|
||||
ok &= db_exec(__func__, wallet->db,
|
||||
"UPDATE shachains SET num_valid=%d, min_index=%" PRIu64
|
||||
" WHERE id=%" PRIu64,
|
||||
chain->chain.num_valid, index, chain->id);
|
||||
|
||||
ok &= db_exec(__func__, wallet->db,
|
||||
"REPLACE INTO shachain_known "
|
||||
"(shachain_id, pos, idx, hash) VALUES "
|
||||
"(%" PRIu64 ", %d, %" PRIu64 ", '%s');",
|
||||
chain->id, pos, index, hexhash);
|
||||
|
||||
if (ok)
|
||||
ok &= db_commit_transaction(wallet->db);
|
||||
else
|
||||
db_rollback_transaction(wallet->db);
|
||||
tal_free(tmpctx);
|
||||
return ok;
|
||||
}
|
||||
|
||||
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_query(
|
||||
__func__, wallet->db,
|
||||
"SELECT min_index, num_valid FROM shachains WHERE id=%" PRIu64, id);
|
||||
if (!stmt)
|
||||
return false;
|
||||
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_query(
|
||||
__func__, wallet->db,
|
||||
"SELECT idx, hash, pos FROM shachain_known WHERE shachain_id=%" PRIu64,
|
||||
id);
|
||||
|
||||
if (!stmt)
|
||||
return false;
|
||||
while (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||
int pos = sqlite3_column_int(stmt, 2);
|
||||
chain->chain.known[pos].index = sqlite3_column_int64(stmt, 0);
|
||||
hex_decode(
|
||||
sqlite3_column_blob(stmt, 1), sqlite3_column_bytes(stmt, 1),
|
||||
&chain->chain.known[pos].hash, sizeof(struct sha256));
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "config.h"
|
||||
#include "db.h"
|
||||
#include <ccan/crypto/shachain/shachain.h>
|
||||
#include <ccan/tal/tal.h>
|
||||
#include <lightningd/utxo.h>
|
||||
#include <wally_bip32.h>
|
||||
|
@ -38,6 +39,14 @@ enum wallet_output_type {
|
|||
htlc_recv = 4
|
||||
};
|
||||
|
||||
/* A database backed shachain struct. The datastructure is
|
||||
* writethrough, reads are performed from an in-memory version, all
|
||||
* writes are passed through to the DB. */
|
||||
struct wallet_shachain {
|
||||
u64 id;
|
||||
struct shachain chain;
|
||||
};
|
||||
|
||||
/**
|
||||
* wallet_new - Constructor for a new sqlite3 based wallet
|
||||
*
|
||||
|
@ -114,4 +123,35 @@ bool wallet_can_spend(struct wallet *w, const u8 *script,
|
|||
*/
|
||||
s64 wallet_get_newindex(struct lightningd *ld);
|
||||
|
||||
/**
|
||||
* wallet_shachain_init -- wallet wrapper around shachain_init
|
||||
*/
|
||||
bool wallet_shachain_init(struct wallet *wallet, struct wallet_shachain *chain);
|
||||
|
||||
/**
|
||||
* wallet_shachain_add_hash -- wallet wrapper around shachain_add_hash
|
||||
*/
|
||||
bool wallet_shachain_add_hash(struct wallet *wallet,
|
||||
struct wallet_shachain *chain,
|
||||
shachain_index_t index,
|
||||
const struct sha256 *hash);
|
||||
|
||||
/* Simply passes through to shachain_get_hash since it doesn't touch
|
||||
* the DB */
|
||||
static inline bool wallet_shachain_get_hash(struct wallet *w,
|
||||
struct wallet_shachain *chain,
|
||||
u64 index, struct sha256 *hash)
|
||||
{
|
||||
return shachain_get_hash(&chain->chain, index, hash);
|
||||
}
|
||||
/**
|
||||
* wallet_shachain_load -- Load an existing shachain from the wallet.
|
||||
*
|
||||
* @wallet: the wallet to load from
|
||||
* @id: the shachain id to load
|
||||
* @chain: where to load the shachain into
|
||||
*/
|
||||
bool wallet_shachain_load(struct wallet *wallet, u64 id,
|
||||
struct wallet_shachain *chain);
|
||||
|
||||
#endif /* WALLET_WALLET_H */
|
||||
|
|
Loading…
Add table
Reference in a new issue