diff --git a/wallet/wallet.c b/wallet/wallet.c index a7e9cb655..646e8528c 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -2,8 +2,11 @@ #include #include +#include #include +#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; +} + diff --git a/wallet/wallet.h b/wallet/wallet.h index e49fbc8cf..c9cba05a9 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -3,6 +3,7 @@ #include "config.h" #include "db.h" +#include #include #include #include @@ -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 */