From 50ff0b26fd48e0b495990ac55a2b72d1735873d9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 15 Jul 2020 05:00:26 +0930 Subject: [PATCH] wallet: explicit routines to reserve/unreserve a UTXO. These keep the struct utxo in sync with the database, explicitly: these will be the only places where utxo->status is set. The old routines will be removed at the end. Signed-off-by: Rusty Russell --- wallet/wallet.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++ wallet/wallet.h | 17 ++++++++++ 2 files changed, 101 insertions(+) diff --git a/wallet/wallet.c b/wallet/wallet.c index 1dfce9445..3f096e7c0 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -28,6 +28,9 @@ * to prune? */ #define UTXO_PRUNE_DEPTH 144 +/* 12 hours is usually enough reservation time */ +#define RESERVATION_INC (6 * 12) + static void outpointfilters_init(struct wallet *w) { struct db_stmt *stmt; @@ -418,6 +421,87 @@ void wallet_confirm_utxos(struct wallet *w, const struct utxo **utxos) } } +static void db_set_utxo(struct db *db, const struct utxo *utxo) +{ + struct db_stmt *stmt; + + if (utxo->status == output_state_reserved) + assert(utxo->reserved_til); + else + assert(!utxo->reserved_til); + + stmt = db_prepare_v2( + db, SQL("UPDATE outputs SET status=?, reserved_til=?" + "WHERE prev_out_tx=? AND prev_out_index=?")); + db_bind_int(stmt, 0, output_status_in_db(utxo->status)); + if (utxo->reserved_til) + db_bind_int(stmt, 1, *utxo->reserved_til); + else + db_bind_null(stmt, 1); + db_bind_txid(stmt, 2, &utxo->txid); + db_bind_int(stmt, 3, utxo->outnum); + db_exec_prepared_v2(take(stmt)); +} + +bool wallet_reserve_utxo(struct wallet *w, struct utxo *utxo, u32 current_height) +{ + u32 reservation_height; + + if (utxo->status == output_state_reserved) + assert(utxo->reserved_til); + else + assert(!utxo->reserved_til); + + switch (utxo->status) { + case output_state_spent: + return false; + case output_state_available: + case output_state_reserved: + break; + case output_state_any: + abort(); + } + + /* We simple increase existing reservations, which DTRT if we unreserve */ + if (utxo->reserved_til + && *utxo->reserved_til >= current_height) + reservation_height = *utxo->reserved_til + RESERVATION_INC; + else + reservation_height = current_height + RESERVATION_INC; + + utxo->status = output_state_reserved; + tal_free(utxo->reserved_til); + utxo->reserved_til = tal_dup(utxo, u32, &reservation_height); + + db_set_utxo(w->db, utxo); + + return true; +} + +void wallet_unreserve_utxo(struct wallet *w, struct utxo *utxo, u32 current_height) +{ + if (utxo->status == output_state_reserved) { + /* FIXME: old code didn't set reserved_til, so fake it here */ + if (!utxo->reserved_til) + utxo->reserved_til = tal_dup(utxo, u32, ¤t_height); + assert(utxo->reserved_til); + } else + assert(!utxo->reserved_til); + + if (utxo->status != output_state_reserved) + fatal("UTXO %s:%u is not reserved", + type_to_string(tmpctx, struct bitcoin_txid, &utxo->txid), + utxo->outnum); + + if (*utxo->reserved_til <= current_height + RESERVATION_INC) { + utxo->status = output_state_available; + utxo->reserved_til = tal_free(utxo->reserved_til); + } else + *utxo->reserved_til -= RESERVATION_INC; + + db_set_utxo(w->db, utxo); +} + bool wallet_add_onchaind_utxo(struct wallet *w, const struct bitcoin_txid *txid, u32 outnum, diff --git a/wallet/wallet.h b/wallet/wallet.h index 19ef4fe58..34e76989c 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -392,6 +392,23 @@ bool wallet_add_onchaind_utxo(struct wallet *w, /* NULL if option_static_remotekey */ const struct pubkey *commitment_point); +/** + * wallet_reserve_utxo - set a reservation on a UTXO. + * + * If the reservation is already reserved, refreshes the reservation, + * otherwise if it's not available, returns false. + */ +bool wallet_reserve_utxo(struct wallet *w, + struct utxo *utxo, + u32 reservation_blocknum); + +/* wallet_unreserve_utxo - make a reserved UTXO available again. + * + * Must be reserved. + */ +void wallet_unreserve_utxo(struct wallet *w, struct utxo *utxo, + u32 current_height); + /** wallet_utxo_get - Retrive a utxo. * * Returns a utxo, or NULL if not found.