mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-18 21:35:11 +01:00
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.
This commit is contained in:
parent
28030c3d6b
commit
7738bccf42
@ -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);
|
||||
/* Oops, didn't have enough coins available */
|
||||
if (!utxos)
|
||||
return NULL;
|
||||
|
||||
db_begin_transaction(ld->wallet->db);
|
||||
available = wallet_get_utxos(utxos, ld->wallet, output_state_available);
|
||||
|
||||
for (i=0; i<tal_count(available); i++) {
|
||||
u64 fee;
|
||||
|
||||
tal_resize(&utxos, i+1);
|
||||
utxos[i] = tal_steal(utxos, available[i]);
|
||||
|
||||
assert(wallet_update_output_status(
|
||||
ld->wallet, &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; i<tal_count(utxos); i++)
|
||||
satoshi_in += utxos[i]->amount;
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user