From 7738bccf42572cfa38d37bb7911dade51c7034a0 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 13 Jun 2017 15:42:31 +0200 Subject: [PATCH] wallet: Move coin-selection primitives to wallet We'll re-use them a few times so having them at a central location is nice. We also fix a bug that was unreserving UTXO entries upon free, instead of promoting them to being spent. --- lightningd/build_utxos.c | 83 +++++++++------------------------------ lightningd/build_utxos.h | 2 - lightningd/peer_control.c | 1 + wallet/wallet.c | 82 ++++++++++++++++++++++++++++++++++++++ wallet/wallet.h | 14 +++++++ 5 files changed, 115 insertions(+), 67 deletions(-) diff --git a/lightningd/build_utxos.c b/lightningd/build_utxos.c index 1a7b9162a..fb7ec09fd 100644 --- a/lightningd/build_utxos.c +++ b/lightningd/build_utxos.c @@ -167,80 +167,33 @@ static const struct json_command addfunds_command = { }; AUTODATA(json_command, &addfunds_command); -static void unreserve_utxo(struct lightningd *ld, const struct utxo *unres) -{ - assert(wallet_update_output_status(ld->wallet, &unres->txid, - unres->outnum, output_state_reserved, - output_state_available)); -} - -static void destroy_utxos(const struct utxo **utxos, struct lightningd *ld) -{ - size_t i; - - for (i = 0; i < tal_count(utxos); i++) - unreserve_utxo(ld, utxos[i]); -} - -void confirm_utxos(struct lightningd *ld, const struct utxo **utxos) -{ - tal_del_destructor2(utxos, destroy_utxos, ld); -} - const struct utxo **build_utxos(const tal_t *ctx, struct lightningd *ld, u64 satoshi_out, u32 feerate_per_kw, u64 dust_limit, u64 *change_satoshis, u32 *change_keyindex) { - size_t i = 0; - struct utxo **available; - const struct utxo **utxos = tal_arr(ctx, const struct utxo *, 0); - /* We assume two outputs for the weight. */ - u64 satoshi_in = 0, weight = (4 + (8 + 22) * 2 + 4) * 4; + u64 satoshi_in = 0; + u64 fee_estimate = 0; u64 bip32_max_index = db_get_intvar(ld->wallet->db, "bip32_max_index", 0); + const struct utxo **utxos = + wallet_select_coins(ctx, ld->wallet, satoshi_out, feerate_per_kw, &fee_estimate); - tal_add_destructor2(utxos, destroy_utxos, ld); - - db_begin_transaction(ld->wallet->db); - available = wallet_get_utxos(utxos, ld->wallet, output_state_available); - - for (i=0; iwallet, &available[i]->txid, available[i]->outnum, - output_state_available, output_state_reserved)); - - /* Add this input's weight. */ - 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 = weight * feerate_per_kw / 1000; + /* How much are we actually claiming? */ + for (size_t i=0; iamount; - if (satoshi_in >= fee + satoshi_out) { - /* We simply eliminate change if it's dust. */ - *change_satoshis = satoshi_in - (fee + satoshi_out); - if (*change_satoshis < dust_limit) { - *change_satoshis = 0; - *change_keyindex = 0; - } else { - *change_keyindex = bip32_max_index + 1; - db_set_intvar(ld->wallet->db, "bip32_max_index", *change_keyindex); - } - - db_commit_transaction(ld->wallet->db); - tal_free(available); - return utxos; - } + /* Do we need a change output? */ + *change_satoshis = satoshi_in - (fee_estimate + satoshi_out); + if (*change_satoshis < dust_limit) { + *change_satoshis = 0; + *change_keyindex = 0; + } else { + *change_keyindex = bip32_max_index + 1; + db_set_intvar(ld->wallet->db, "bip32_max_index", *change_keyindex); } - db_rollback_transaction(ld->wallet->db); - tal_free(available); - return tal_free(utxos); + return utxos; } diff --git a/lightningd/build_utxos.h b/lightningd/build_utxos.h index 9fa0fde91..706fcaf8b 100644 --- a/lightningd/build_utxos.h +++ b/lightningd/build_utxos.h @@ -11,6 +11,4 @@ const struct utxo **build_utxos(const tal_t *ctx, u32 feerate_per_kw, u64 dust_limit, u64 *change_satoshis, u32 *change_keyindex); -/* Once we've spent them, mark them confirmed. */ -void confirm_utxos(struct lightningd *ld, const struct utxo **utxos); #endif /* LIGHTNING_LIGHTNINGD_BUILD_UTXOS_H */ diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index d674e6ba8..3158db3f3 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -850,6 +850,7 @@ static bool opening_got_hsm_funding_sig(struct subd *hsm, const u8 *resp, /* Start normal channel daemon. */ peer_start_channeld(fc->peer, GETTING_SIG_FROM_HSM, NULL); + wallet_confirm_utxos(fc->peer->ld->wallet, fc->utxomap); tal_free(fc); return true; } diff --git a/wallet/wallet.c b/wallet/wallet.c index c895110a5..7e25b4042 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -92,3 +92,85 @@ struct utxo **wallet_get_utxos(const tal_t *ctx, struct wallet *w, const enum ou 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: %s", w->db->err); + } +} + +/** + * 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: %s", w->db->err); + } + } +} + +const struct utxo **wallet_select_coins(const tal_t *ctx, struct wallet *w, + const u64 value, + const u32 feerate_per_kw, + u64 *fee_estimate) +{ + size_t i = 0; + struct utxo **available; + const struct utxo **utxos = tal_arr(ctx, const struct utxo *, 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); + + db_begin_transaction(w->db); + 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: %s", w->db->err); + + 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); + db_rollback_transaction(w->db); + } else { + /* Commit the db transaction to persist markings */ + db_commit_transaction(w->db); + } + return utxos; +} diff --git a/wallet/wallet.h b/wallet/wallet.h index 36e13d51a..9f21ea2f7 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -74,4 +74,18 @@ bool wallet_update_output_status(struct wallet *w, struct utxo **wallet_get_utxos(const tal_t *ctx, struct wallet *w, const enum output_status state); +const struct utxo **wallet_select_coins(const tal_t *ctx, struct wallet *w, + const u64 value, + const u32 feerate_per_kw, + u64 *fee_estimate); + +/** + * wallet_confirm_utxos - Once we've spent a set of utxos, mark them confirmed. + * + * May be called once the transaction spending these UTXOs has been + * broadcast. If something fails use `tal_free(utxos)` instead to undo + * the reservation. + */ +void wallet_confirm_utxos(struct wallet *w, const struct utxo **utxos); + #endif /* WALLET_WALLET_H */