diff --git a/lightningd/build_utxos.c b/lightningd/build_utxos.c index 50ff442d1..0e0f41ef0 100644 --- a/lightningd/build_utxos.c +++ b/lightningd/build_utxos.c @@ -179,7 +179,12 @@ static void unreserve_utxo(struct lightningd *ld, const struct utxo *unres) { struct tracked_utxo *utxo; - list_for_each(&ld->utxos, utxo, list) { + assert(wallet_update_output_status(ld->wallet, &unres->txid, + unres->outnum, output_state_reserved, + output_state_available)); + + list_for_each(&ld->utxos, utxo, list) + { if (unres != &utxo->utxo) continue; assert(utxo->reserved); @@ -225,6 +230,10 @@ const struct utxo **build_utxos(const tal_t *ctx, utxos[i] = &utxo->utxo; utxo->reserved = true; + assert(wallet_update_output_status( + ld->wallet, &utxo->utxo.txid, utxo->utxo.outnum, + output_state_available, output_state_reserved)); + /* Add this input's weight. */ weight += (32 + 4 + 4) * 4; if (utxos[i]->is_p2sh) diff --git a/wallet/wallet.c b/wallet/wallet.c index d593d4e7c..a59e673eb 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -25,3 +25,27 @@ bool wallet_add_utxo(struct wallet *w, struct utxo *utxo, tal_free(tmpctx); return result; } + +bool wallet_update_output_status(struct wallet *w, + const struct sha256_double *txid, + const u32 outnum, enum output_status oldstatus, + enum output_status newstatus) +{ + tal_t *tmpctx = tal_tmpctx(w); + char *hextxid = tal_hexstr(tmpctx, txid, sizeof(*txid)); + if (oldstatus != output_state_any) { + db_exec(__func__, w->db, + "UPDATE outputs SET status=%d WHERE status=%d " + "AND prev_out_tx = '%s' AND prev_out_index = " + "%d;", + newstatus, oldstatus, hextxid, outnum); + } else { + db_exec(__func__, w->db, + "UPDATE outputs SET status=%d WHERE " + "AND prev_out_tx = '%s' AND prev_out_index = " + "%d;", + newstatus, hextxid, outnum); + } + tal_free(tmpctx); + return sqlite3_changes(w->db->sql) > 0; +} diff --git a/wallet/wallet.h b/wallet/wallet.h index d2630bd54..82b25f2ab 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -50,4 +50,19 @@ struct wallet *wallet_new(const tal_t *ctx, struct log *log); bool wallet_add_utxo(struct wallet *w, struct utxo *utxo, enum wallet_output_type type); +/** + * wallet_update_output_status - Perform an output state transition + * + * Change the current status of an output we are tracking in the + * database. Returns true if the output exists with the @oldstatus and + * was successfully updated to @newstatus. May fail if either the + * output does not exist, or it does not have the expected + * @oldstatus. In case we don't care about the previous state use + * `output_state_any` as @oldstatus. + */ +bool wallet_update_output_status(struct wallet *w, + const struct sha256_double *txid, + const u32 outnum, enum output_status oldstatus, + enum output_status newstatus); + #endif /* WALLET_WALLET_H */ diff --git a/wallet/wallet_tests.c b/wallet/wallet_tests.c index 84bcab134..dccb160f8 100644 --- a/wallet/wallet_tests.c +++ b/wallet/wallet_tests.c @@ -6,7 +6,7 @@ #include #include -static bool test_wallet_add_utxo(void) +static bool test_wallet_outputs(void) { char filename[] = "/tmp/ldb-XXXXXX"; struct utxo u; @@ -16,26 +16,52 @@ static bool test_wallet_add_utxo(void) close(fd); w->db = db_open(w, filename); - CHECK_MSG(w->db,"Failed opening the db"); + CHECK_MSG(w->db, "Failed opening the db"); CHECK_MSG(db_migrate(w->db), "DB migration failed"); - + memset(&u, 0, sizeof(u)); /* Should work, it's the first time we add it */ - CHECK_MSG(wallet_add_utxo(w, &u, p2sh_wpkh), "wallet_add_utxo failed on first add"); + CHECK_MSG(wallet_add_utxo(w, &u, p2sh_wpkh), + "wallet_add_utxo failed on first add"); /* Should fail, we already have that UTXO */ - CHECK_MSG(!wallet_add_utxo(w, &u, p2sh_wpkh), "wallet_add_utxo succeeded on second add"); + CHECK_MSG(!wallet_add_utxo(w, &u, p2sh_wpkh), + "wallet_add_utxo succeeded on second add"); + + /* Attempt to reserve the utxo */ + CHECK_MSG(wallet_update_output_status(w, &u.txid, u.outnum, + output_state_available, + output_state_reserved), + "could not reserve available output"); + + /* Reserving twice should fail */ + CHECK_MSG(!wallet_update_output_status(w, &u.txid, u.outnum, + output_state_available, + output_state_reserved), + "could reserve already reserved output"); + + /* Un-reserving should work */ + CHECK_MSG(wallet_update_output_status(w, &u.txid, u.outnum, + output_state_reserved, + output_state_available), + "could not unreserve reserved output"); + + /* Switching from any to something else */ + CHECK_MSG(wallet_update_output_status(w, &u.txid, u.outnum, + output_state_any, + output_state_spent), + "could not change output state ignoring oldstate"); + tal_free(w); return true; - } int main(void) { bool ok = true; - ok &= test_wallet_add_utxo(); + ok &= test_wallet_outputs(); return !ok; }