mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-19 15:14:23 +01:00
wallet: switch over to withdraw in module, remove lots of unused code.
This removes the reservation cleanup at startup, too, now they're all using 'reserved_til'. This changes test_withdraw, since it asserted that outputs were marked spent as soon as we broadcast a transaction: now they're reserved until it's mined. Similarly, test_addfunds_from_block assumed we'd see funds as soon as we broadcast the tx. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> Changelog-Changed: JSON-RPC: `withdraw` now randomizes input and output order, not BIP69.
This commit is contained in:
@@ -74,10 +74,8 @@ COMMON_SRC_NOGEN := \
|
|||||||
common/utxo.c \
|
common/utxo.c \
|
||||||
common/version.c \
|
common/version.c \
|
||||||
common/wallet.c \
|
common/wallet.c \
|
||||||
common/wallet_tx.c \
|
|
||||||
common/wireaddr.c \
|
common/wireaddr.c \
|
||||||
common/wire_error.c \
|
common/wire_error.c
|
||||||
common/withdraw_tx.c
|
|
||||||
|
|
||||||
COMMON_SRC_GEN := common/status_wiregen.c common/peer_status_wiregen.c
|
COMMON_SRC_GEN := common/status_wiregen.c common/peer_status_wiregen.c
|
||||||
|
|
||||||
|
|||||||
@@ -1,264 +0,0 @@
|
|||||||
#include <ccan/ccan/opt/opt.h>
|
|
||||||
#include <ccan/tal/str/str.h>
|
|
||||||
#include <common/json_command.h>
|
|
||||||
#include <common/json_helpers.h>
|
|
||||||
#include <common/jsonrpc_errors.h>
|
|
||||||
#include <common/utils.h>
|
|
||||||
#include <common/wallet_tx.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <wallet/wallet.h>
|
|
||||||
|
|
||||||
void wtx_init(struct command *cmd, struct wallet_tx *wtx, struct amount_sat max)
|
|
||||||
{
|
|
||||||
wtx->cmd = cmd;
|
|
||||||
wtx->amount = max;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct command_result *param_wtx(struct command *cmd,
|
|
||||||
const char *name,
|
|
||||||
const char *buffer,
|
|
||||||
const jsmntok_t *tok,
|
|
||||||
struct wallet_tx *wtx)
|
|
||||||
{
|
|
||||||
struct amount_sat max = wtx->amount;
|
|
||||||
|
|
||||||
if (json_tok_streq(buffer, tok, "all")) {
|
|
||||||
wtx->all_funds = true;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
wtx->all_funds = false;
|
|
||||||
|
|
||||||
if (!parse_amount_sat(&wtx->amount,
|
|
||||||
buffer + tok->start, tok->end - tok->start))
|
|
||||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
||||||
"'%s' should be an amount in satoshis or all, not '%.*s'",
|
|
||||||
name,
|
|
||||||
tok->end - tok->start,
|
|
||||||
buffer + tok->start);
|
|
||||||
|
|
||||||
if (amount_sat_greater(wtx->amount, max))
|
|
||||||
return command_fail(wtx->cmd, FUND_MAX_EXCEEDED,
|
|
||||||
"Amount exceeded %s",
|
|
||||||
type_to_string(tmpctx, struct amount_sat,
|
|
||||||
&max));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct command_result *param_utxos(struct command *cmd,
|
|
||||||
const char *name,
|
|
||||||
const char *buffer,
|
|
||||||
const jsmntok_t *tok,
|
|
||||||
const struct utxo ***utxos)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
const jsmntok_t *curr;
|
|
||||||
struct bitcoin_txid **txids = tal_arr(cmd, struct bitcoin_txid*, 0);
|
|
||||||
unsigned int **outnums = tal_arr(cmd, unsigned int*, 0);
|
|
||||||
|
|
||||||
json_for_each_arr(i, curr, tok) {
|
|
||||||
jsmntok_t txid_tok, outnum_tok;
|
|
||||||
if (!split_tok(buffer, curr, ':', &txid_tok, &outnum_tok))
|
|
||||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
||||||
"Could not decode the outpoint from \"%s\""
|
|
||||||
" The utxos should be specified as"
|
|
||||||
" 'txid:output_index'.",
|
|
||||||
json_strdup(tmpctx, buffer, curr));
|
|
||||||
|
|
||||||
struct bitcoin_txid *txid = tal(txids, struct bitcoin_txid);
|
|
||||||
unsigned int *outnum = tal(txids, unsigned int);
|
|
||||||
if (!json_to_txid(buffer, (const jsmntok_t*)&txid_tok, txid)) {
|
|
||||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
||||||
"Could not get a txid out of \"%s\"",
|
|
||||||
json_strdup(tmpctx, buffer, &txid_tok));
|
|
||||||
}
|
|
||||||
if (!json_to_number(buffer, (const jsmntok_t*)&outnum_tok, outnum))
|
|
||||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
||||||
"Could not get a vout out of \"%s\"",
|
|
||||||
json_strdup(tmpctx, buffer, &outnum_tok));
|
|
||||||
|
|
||||||
tal_arr_expand(&txids, txid);
|
|
||||||
tal_arr_expand(&outnums, outnum);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!tal_count(txids) || !tal_count(outnums))
|
|
||||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
||||||
"Please specify an array of 'txid:output_index',"
|
|
||||||
" not \"%.*s\"",
|
|
||||||
tok->end - tok->start,
|
|
||||||
buffer + tok->start);
|
|
||||||
|
|
||||||
*utxos = wallet_select_specific(cmd, cmd->ld->wallet, txids, outnums);
|
|
||||||
tal_free(txids);
|
|
||||||
tal_free(outnums);
|
|
||||||
|
|
||||||
if (!*utxos)
|
|
||||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
||||||
"Could not decode all of the outpoints. The utxos"
|
|
||||||
" should be specified as an array of "
|
|
||||||
" 'txid:output_index'.");
|
|
||||||
if (tal_count(*utxos) == 0)
|
|
||||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
||||||
"No matching utxo was found from the wallet. "
|
|
||||||
"You can get a list of the wallet utxos with"
|
|
||||||
" the `listfunds` RPC call.");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct command_result *check_amount(const struct wallet_tx *wtx,
|
|
||||||
struct amount_sat amount)
|
|
||||||
{
|
|
||||||
if (tal_count(wtx->utxos) == 0) {
|
|
||||||
/* Since it's possible the lack of utxos is because we haven't finished
|
|
||||||
* syncing yet, report a sync timing error first */
|
|
||||||
if (!topology_synced(wtx->cmd->ld->topology))
|
|
||||||
return command_fail(wtx->cmd, FUNDING_STILL_SYNCING_BITCOIN,
|
|
||||||
"Still syncing with bitcoin network");
|
|
||||||
|
|
||||||
return command_fail(wtx->cmd, FUND_CANNOT_AFFORD,
|
|
||||||
"Cannot afford transaction");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (amount_sat_less(amount, chainparams->dust_limit)) {
|
|
||||||
return command_fail(wtx->cmd, FUND_OUTPUT_IS_DUST,
|
|
||||||
"Output %s would be dust",
|
|
||||||
type_to_string(tmpctx, struct amount_sat,
|
|
||||||
&amount));
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct command_result *wtx_select_utxos(struct wallet_tx *tx,
|
|
||||||
u32 fee_rate_per_kw,
|
|
||||||
size_t out_len,
|
|
||||||
u32 maxheight)
|
|
||||||
{
|
|
||||||
struct command_result *res;
|
|
||||||
struct amount_sat fee_estimate;
|
|
||||||
|
|
||||||
if (tx->all_funds) {
|
|
||||||
struct amount_sat amount;
|
|
||||||
tx->utxos = wallet_select_all(tx, tx->cmd->ld->wallet,
|
|
||||||
fee_rate_per_kw, out_len,
|
|
||||||
maxheight,
|
|
||||||
&amount,
|
|
||||||
&fee_estimate);
|
|
||||||
res = check_amount(tx, amount);
|
|
||||||
if (res)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
/* tx->amount is max permissible */
|
|
||||||
if (amount_sat_less_eq(amount, tx->amount)) {
|
|
||||||
tx->change = AMOUNT_SAT(0);
|
|
||||||
tx->change_key_index = 0;
|
|
||||||
tx->amount = amount;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Too much? Try again, but ask for limit instead. */
|
|
||||||
tx->all_funds = false;
|
|
||||||
tx->utxos = tal_free(tx->utxos);
|
|
||||||
}
|
|
||||||
|
|
||||||
tx->utxos = wallet_select_coins(tx, tx->cmd->ld->wallet,
|
|
||||||
true, tx->amount,
|
|
||||||
fee_rate_per_kw, out_len,
|
|
||||||
maxheight,
|
|
||||||
&fee_estimate, &tx->change);
|
|
||||||
if (!tx->utxos) {
|
|
||||||
/* Try again, without change this time */
|
|
||||||
tx->utxos = wallet_select_coins(tx, tx->cmd->ld->wallet,
|
|
||||||
false, tx->amount,
|
|
||||||
fee_rate_per_kw, out_len,
|
|
||||||
maxheight,
|
|
||||||
&fee_estimate, &tx->change);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
res = check_amount(tx, tx->amount);
|
|
||||||
if (res)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
if (amount_sat_less(tx->change, chainparams->dust_limit)) {
|
|
||||||
tx->change = AMOUNT_SAT(0);
|
|
||||||
tx->change_key_index = 0;
|
|
||||||
} else {
|
|
||||||
tx->change_key_index = wallet_get_newindex(tx->cmd->ld);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct command_result *wtx_from_utxos(struct wallet_tx *tx,
|
|
||||||
u32 fee_rate_per_kw,
|
|
||||||
size_t out_len,
|
|
||||||
u32 maxheight,
|
|
||||||
const struct utxo **utxos)
|
|
||||||
{
|
|
||||||
size_t weight;
|
|
||||||
struct amount_sat total_amount, fee_estimate;
|
|
||||||
|
|
||||||
tx->change = AMOUNT_SAT(0);
|
|
||||||
tx->change_key_index = 0;
|
|
||||||
total_amount = AMOUNT_SAT(0);
|
|
||||||
|
|
||||||
/* The transaction has `tal_count(tx.utxos)` inputs and one output */
|
|
||||||
/* (version + in count + out count + locktime) (index + value + script length) */
|
|
||||||
/* + segwit marker + flag */
|
|
||||||
weight = 4 * (4 + 1 + 1 + 4) + 4 * (8 + 1 + out_len) + 1 + 1;
|
|
||||||
for (size_t i = 0; i < tal_count(utxos); i++) {
|
|
||||||
if (maxheight > 0 &&
|
|
||||||
(!utxos[i]->blockheight || *utxos[i]->blockheight > maxheight)) {
|
|
||||||
tal_arr_remove(&utxos, i);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
/* txid + index + sequence + script_len */
|
|
||||||
weight += (32 + 4 + 4 + 1) * 4;
|
|
||||||
/* P2SH variants include push of <0 <20-byte-key-hash>> */
|
|
||||||
if (utxos[i]->is_p2sh)
|
|
||||||
weight += 23 * 4;
|
|
||||||
/* Account for witness (1 byte count + sig + key) */
|
|
||||||
weight += 1 + (1 + 73 + 1 + 33);
|
|
||||||
if (!amount_sat_add(&total_amount, total_amount, utxos[i]->amount))
|
|
||||||
fatal("Overflow when computing input amount");
|
|
||||||
}
|
|
||||||
tx->utxos = tal_steal(tx, utxos);
|
|
||||||
|
|
||||||
if (!tx->all_funds && amount_sat_less(tx->amount, total_amount)
|
|
||||||
&& !amount_sat_sub(&tx->change, total_amount, tx->amount))
|
|
||||||
fatal("Overflow when computing change");
|
|
||||||
|
|
||||||
if (amount_sat_greater_eq(tx->change, chainparams->dust_limit)) {
|
|
||||||
/* Add the change output's weight */
|
|
||||||
weight += (8 + 1 + out_len) * 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
fee_estimate = amount_tx_fee(fee_rate_per_kw, weight);
|
|
||||||
|
|
||||||
if (tx->all_funds || amount_sat_eq(tx->change, AMOUNT_SAT(0))) {
|
|
||||||
tx->amount = total_amount;
|
|
||||||
if (!amount_sat_sub(&tx->amount, tx->amount, fee_estimate))
|
|
||||||
return command_fail(tx->cmd, FUND_CANNOT_AFFORD,
|
|
||||||
"Cannot afford transaction with %s"
|
|
||||||
" sats of fees, make sure to use "
|
|
||||||
"confirmed utxos.",
|
|
||||||
type_to_string(tmpctx, struct amount_sat,
|
|
||||||
&fee_estimate));
|
|
||||||
} else {
|
|
||||||
if (!amount_sat_sub(&tx->change, tx->change, fee_estimate)) {
|
|
||||||
/* Try again without a change output */
|
|
||||||
weight -= (8 + 1 + out_len) * 4;
|
|
||||||
fee_estimate = amount_tx_fee(fee_rate_per_kw, weight);
|
|
||||||
if (!amount_sat_sub(&tx->change, tx->change, fee_estimate))
|
|
||||||
return command_fail(tx->cmd, FUND_CANNOT_AFFORD,
|
|
||||||
"Cannot afford transaction with %s"
|
|
||||||
" sats of fees, make sure to use "
|
|
||||||
"confirmed utxos.",
|
|
||||||
type_to_string(tmpctx, struct amount_sat,
|
|
||||||
&fee_estimate));
|
|
||||||
tx->change = AMOUNT_SAT(0);
|
|
||||||
} else {
|
|
||||||
tx->change_key_index = wallet_get_newindex(tx->cmd->ld);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return check_amount(tx, tx->amount);
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
#ifndef LIGHTNING_COMMON_WALLET_TX_H
|
|
||||||
#define LIGHTNING_COMMON_WALLET_TX_H
|
|
||||||
#include "config.h"
|
|
||||||
#include <ccan/short_types/short_types.h>
|
|
||||||
#include <common/amount.h>
|
|
||||||
#include <lightningd/json.h>
|
|
||||||
#include <lightningd/jsonrpc.h>
|
|
||||||
#include <lightningd/lightningd.h>
|
|
||||||
|
|
||||||
/* A specification of funds in the wallet used for funding channels and
|
|
||||||
* withdrawal.
|
|
||||||
*/
|
|
||||||
struct wallet_tx {
|
|
||||||
struct command *cmd;
|
|
||||||
struct amount_sat amount, change;
|
|
||||||
u32 change_key_index;
|
|
||||||
const struct utxo **utxos;
|
|
||||||
|
|
||||||
bool all_funds; /* In this case, amount is a maximum. */
|
|
||||||
};
|
|
||||||
|
|
||||||
void wtx_init(struct command *cmd, struct wallet_tx *wtx, struct amount_sat max);
|
|
||||||
|
|
||||||
struct command_result *param_wtx(struct command *cmd,
|
|
||||||
const char *name,
|
|
||||||
const char *buffer,
|
|
||||||
const jsmntok_t *tok,
|
|
||||||
struct wallet_tx *wtx);
|
|
||||||
|
|
||||||
struct command_result *param_utxos(struct command *cmd,
|
|
||||||
const char *name,
|
|
||||||
const char *buffer,
|
|
||||||
const jsmntok_t *tok,
|
|
||||||
const struct utxo ***utxos);
|
|
||||||
|
|
||||||
struct command_result *wtx_select_utxos(struct wallet_tx *tx,
|
|
||||||
u32 fee_rate_per_kw,
|
|
||||||
size_t out_len,
|
|
||||||
u32 maxheight);
|
|
||||||
|
|
||||||
struct command_result *wtx_from_utxos(struct wallet_tx *tx,
|
|
||||||
u32 fee_rate_per_kw,
|
|
||||||
size_t out_len,
|
|
||||||
u32 maxheight,
|
|
||||||
const struct utxo **utxos);
|
|
||||||
|
|
||||||
static inline u32 minconf_to_maxheight(u32 minconf, struct lightningd *ld)
|
|
||||||
{
|
|
||||||
/* No confirmations is special, we need to disable the check in the
|
|
||||||
* selection */
|
|
||||||
if (minconf == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* Avoid wrapping around and suddenly allowing any confirmed
|
|
||||||
* outputs. Since we can't have a coinbase output, and 0 is taken for
|
|
||||||
* the disable case, we can just clamp to 1. */
|
|
||||||
if (minconf >= ld->topology->tip->height)
|
|
||||||
return 1;
|
|
||||||
return ld->topology->tip->height - minconf + 1;
|
|
||||||
}
|
|
||||||
#endif /* LIGHTNING_COMMON_WALLET_TX_H */
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
#include "withdraw_tx.h"
|
|
||||||
#include <assert.h>
|
|
||||||
#include <bitcoin/pubkey.h>
|
|
||||||
#include <bitcoin/script.h>
|
|
||||||
#include <ccan/ptrint/ptrint.h>
|
|
||||||
#include <common/key_derive.h>
|
|
||||||
#include <common/permute_tx.h>
|
|
||||||
#include <common/utils.h>
|
|
||||||
#include <common/utxo.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <wally_bip32.h>
|
|
||||||
|
|
||||||
struct bitcoin_tx *withdraw_tx(const tal_t *ctx,
|
|
||||||
const struct chainparams *chainparams,
|
|
||||||
const struct utxo **utxos,
|
|
||||||
struct bitcoin_tx_output **outputs,
|
|
||||||
const struct ext_key *bip32_base,
|
|
||||||
u32 nlocktime)
|
|
||||||
{
|
|
||||||
struct bitcoin_tx *tx;
|
|
||||||
int output_count;
|
|
||||||
|
|
||||||
tx = tx_spending_utxos(ctx, chainparams, utxos, bip32_base,
|
|
||||||
false, tal_count(outputs), nlocktime,
|
|
||||||
BITCOIN_TX_DEFAULT_SEQUENCE - 1);
|
|
||||||
|
|
||||||
output_count = bitcoin_tx_add_multi_outputs(tx, outputs);
|
|
||||||
assert(output_count == tal_count(outputs));
|
|
||||||
|
|
||||||
permute_outputs(tx, NULL, (const void **)outputs);
|
|
||||||
permute_inputs(tx, (const void **)utxos);
|
|
||||||
|
|
||||||
bitcoin_tx_finalize(tx);
|
|
||||||
assert(bitcoin_tx_check(tx));
|
|
||||||
return tx;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
#ifndef LIGHTNING_COMMON_WITHDRAW_TX_H
|
|
||||||
#define LIGHTNING_COMMON_WITHDRAW_TX_H
|
|
||||||
#include "config.h"
|
|
||||||
#include <bitcoin/chainparams.h>
|
|
||||||
#include <bitcoin/tx.h>
|
|
||||||
#include <ccan/short_types/short_types.h>
|
|
||||||
#include <ccan/tal/tal.h>
|
|
||||||
#include <common/amount.h>
|
|
||||||
|
|
||||||
struct bitcoin_tx;
|
|
||||||
struct ext_key;
|
|
||||||
struct privkey;
|
|
||||||
struct pubkey;
|
|
||||||
struct bitcoin_address;
|
|
||||||
struct utxo;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* withdraw_tx - Create a p2pkh withdrawal transaction
|
|
||||||
*
|
|
||||||
* @ctx: context to tal from.
|
|
||||||
* @chainparams: (in) the params for the created transaction.
|
|
||||||
* @utxos: (in/out) tal_arr of UTXO pointers to spend (permuted to match)
|
|
||||||
* @outputs: (in) tal_arr of bitcoin_tx_output, scriptPubKeys with amount to send to.
|
|
||||||
* @bip32_base: (in) bip32 base for key derivation, or NULL.
|
|
||||||
* @nlocktime: (in) the value to set as the transaction's nLockTime.
|
|
||||||
*/
|
|
||||||
struct bitcoin_tx *withdraw_tx(const tal_t *ctx,
|
|
||||||
const struct chainparams *chainparams,
|
|
||||||
const struct utxo **utxos,
|
|
||||||
struct bitcoin_tx_output **outputs,
|
|
||||||
const struct ext_key *bip32_base,
|
|
||||||
u32 nlocktime);
|
|
||||||
|
|
||||||
#endif /* LIGHTNING_COMMON_WITHDRAW_TX_H */
|
|
||||||
@@ -39,8 +39,7 @@ HSMD_COMMON_OBJS := \
|
|||||||
common/type_to_string.o \
|
common/type_to_string.o \
|
||||||
common/utils.o \
|
common/utils.o \
|
||||||
common/utxo.o \
|
common/utxo.o \
|
||||||
common/version.o \
|
common/version.o
|
||||||
common/withdraw_tx.o
|
|
||||||
|
|
||||||
|
|
||||||
lightningd/lightning_hsmd: $(HSMD_OBJS) $(HSMD_COMMON_OBJS) $(BITCOIN_OBJS) $(WIRE_OBJS)
|
lightningd/lightning_hsmd: $(HSMD_OBJS) $(HSMD_COMMON_OBJS) $(BITCOIN_OBJS) $(WIRE_OBJS)
|
||||||
|
|||||||
@@ -37,7 +37,6 @@
|
|||||||
#include <common/type_to_string.h>
|
#include <common/type_to_string.h>
|
||||||
#include <common/utils.h>
|
#include <common/utils.h>
|
||||||
#include <common/version.h>
|
#include <common/version.h>
|
||||||
#include <common/withdraw_tx.h>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <hsmd/capabilities.h>
|
#include <hsmd/capabilities.h>
|
||||||
|
|||||||
@@ -110,10 +110,8 @@ LIGHTNINGD_COMMON_OBJS := \
|
|||||||
common/utxo.o \
|
common/utxo.o \
|
||||||
common/version.o \
|
common/version.o \
|
||||||
common/wallet.o \
|
common/wallet.o \
|
||||||
common/wallet_tx.o \
|
|
||||||
common/wire_error.o \
|
common/wire_error.o \
|
||||||
common/wireaddr.o \
|
common/wireaddr.o \
|
||||||
common/withdraw_tx.o
|
|
||||||
|
|
||||||
include wallet/Makefile
|
include wallet/Makefile
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
#include <common/per_peer_state.h>
|
#include <common/per_peer_state.h>
|
||||||
#include <common/timeout.h>
|
#include <common/timeout.h>
|
||||||
#include <common/utils.h>
|
#include <common/utils.h>
|
||||||
#include <common/wallet_tx.h>
|
|
||||||
#include <common/wire_error.h>
|
#include <common/wire_error.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ struct channel;
|
|||||||
struct crypto_state;
|
struct crypto_state;
|
||||||
struct lightningd;
|
struct lightningd;
|
||||||
struct per_peer_state;
|
struct per_peer_state;
|
||||||
|
struct peer;
|
||||||
|
|
||||||
void peer_start_channeld(struct channel *channel,
|
void peer_start_channeld(struct channel *channel,
|
||||||
struct per_peer_state *pps,
|
struct per_peer_state *pps,
|
||||||
|
|||||||
@@ -935,9 +935,6 @@ int main(int argc, char *argv[])
|
|||||||
min_blockheight, max_blockheight);
|
min_blockheight, max_blockheight);
|
||||||
|
|
||||||
db_begin_transaction(ld->wallet->db);
|
db_begin_transaction(ld->wallet->db);
|
||||||
/*~ Tell the wallet to start figuring out what to do for any reserved
|
|
||||||
* unspent outputs we may have crashed with. */
|
|
||||||
wallet_clean_utxos(ld->wallet, ld->topology->bitcoind);
|
|
||||||
|
|
||||||
/*~ Pull peers, channels and HTLCs from db. Needs to happen after the
|
/*~ Pull peers, channels and HTLCs from db. Needs to happen after the
|
||||||
* topology is initialized since some decisions rely on being able to
|
* topology is initialized since some decisions rely on being able to
|
||||||
@@ -1045,7 +1042,6 @@ int main(int argc, char *argv[])
|
|||||||
* unreserving UTXOs (see #1737) */
|
* unreserving UTXOs (see #1737) */
|
||||||
db_begin_transaction(ld->wallet->db);
|
db_begin_transaction(ld->wallet->db);
|
||||||
tal_free(ld->jsonrpc);
|
tal_free(ld->jsonrpc);
|
||||||
free_unreleased_txs(ld->wallet);
|
|
||||||
db_commit_transaction(ld->wallet->db);
|
db_commit_transaction(ld->wallet->db);
|
||||||
|
|
||||||
/* Clean our our HTLC maps, since they use malloc. */
|
/* Clean our our HTLC maps, since they use malloc. */
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
#include <common/penalty_base.h>
|
#include <common/penalty_base.h>
|
||||||
#include <common/per_peer_state.h>
|
#include <common/per_peer_state.h>
|
||||||
#include <common/utils.h>
|
#include <common/utils.h>
|
||||||
#include <common/wallet_tx.h>
|
|
||||||
#include <common/wire_error.h>
|
#include <common/wire_error.h>
|
||||||
#include <connectd/connectd_wiregen.h>
|
#include <connectd/connectd_wiregen.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|||||||
@@ -521,7 +521,7 @@ static const struct plugin_command commands[] = {
|
|||||||
json_txsend
|
json_txsend
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"newwithdraw",
|
"withdraw",
|
||||||
"bitcoin",
|
"bitcoin",
|
||||||
"Send funds to {destination} address",
|
"Send funds to {destination} address",
|
||||||
"Send to {destination} {satoshi} (or 'all') at optional {feerate} using utxos from {minconf} or {utxos}.",
|
"Send to {destination} {satoshi} (or 'all') at optional {feerate} using utxos from {minconf} or {utxos}.",
|
||||||
|
|||||||
@@ -522,7 +522,7 @@ def test_withdraw_misc(node_factory, bitcoind, chainparams):
|
|||||||
l1.rpc.withdraw(waddr, 'not an amount')
|
l1.rpc.withdraw(waddr, 'not an amount')
|
||||||
with pytest.raises(RpcError):
|
with pytest.raises(RpcError):
|
||||||
l1.rpc.withdraw(waddr, -amount)
|
l1.rpc.withdraw(waddr, -amount)
|
||||||
with pytest.raises(RpcError, match=r'Cannot afford transaction'):
|
with pytest.raises(RpcError, match=r'Could not afford'):
|
||||||
l1.rpc.withdraw(waddr, amount * 100)
|
l1.rpc.withdraw(waddr, amount * 100)
|
||||||
|
|
||||||
out = l1.rpc.withdraw(waddr, amount)
|
out = l1.rpc.withdraw(waddr, amount)
|
||||||
@@ -638,7 +638,7 @@ def test_withdraw_misc(node_factory, bitcoind, chainparams):
|
|||||||
assert l1.db_query('SELECT COUNT(*) as c FROM outputs WHERE status=0')[0]['c'] == 0
|
assert l1.db_query('SELECT COUNT(*) as c FROM outputs WHERE status=0')[0]['c'] == 0
|
||||||
|
|
||||||
# This should fail, can't even afford fee.
|
# This should fail, can't even afford fee.
|
||||||
with pytest.raises(RpcError, match=r'Cannot afford transaction'):
|
with pytest.raises(RpcError, match=r'Could not afford'):
|
||||||
l1.rpc.withdraw(waddr, 'all')
|
l1.rpc.withdraw(waddr, 'all')
|
||||||
|
|
||||||
bitcoind.generate_block(1)
|
bitcoind.generate_block(1)
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ from utils import (
|
|||||||
import os
|
import os
|
||||||
import pytest
|
import pytest
|
||||||
import subprocess
|
import subprocess
|
||||||
import time
|
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
@@ -42,7 +41,7 @@ def test_withdraw(node_factory, bitcoind):
|
|||||||
l1.rpc.withdraw(waddr, 'not an amount')
|
l1.rpc.withdraw(waddr, 'not an amount')
|
||||||
with pytest.raises(RpcError):
|
with pytest.raises(RpcError):
|
||||||
l1.rpc.withdraw(waddr, -amount)
|
l1.rpc.withdraw(waddr, -amount)
|
||||||
with pytest.raises(RpcError, match=r'Cannot afford transaction'):
|
with pytest.raises(RpcError, match=r'Could not afford'):
|
||||||
l1.rpc.withdraw(waddr, amount * 100)
|
l1.rpc.withdraw(waddr, amount * 100)
|
||||||
|
|
||||||
out = l1.rpc.withdraw(waddr, 2 * amount)
|
out = l1.rpc.withdraw(waddr, 2 * amount)
|
||||||
@@ -67,15 +66,23 @@ def test_withdraw(node_factory, bitcoind):
|
|||||||
# lightningd uses P2SH-P2WPKH
|
# lightningd uses P2SH-P2WPKH
|
||||||
waddr = l2.rpc.newaddr('bech32')['bech32']
|
waddr = l2.rpc.newaddr('bech32')['bech32']
|
||||||
l1.rpc.withdraw(waddr, 2 * amount)
|
l1.rpc.withdraw(waddr, 2 * amount)
|
||||||
|
|
||||||
|
# Now make sure an additional two of them were marked as reserved
|
||||||
|
assert l1.db_query('SELECT COUNT(*) as c FROM outputs WHERE status=2')[0]['c'] == 2
|
||||||
|
assert l1.db_query('SELECT COUNT(*) as c FROM outputs WHERE status=1')[0]['c'] == 2
|
||||||
|
|
||||||
|
# They're turned into spent once the node sees them mined.
|
||||||
bitcoind.generate_block(1)
|
bitcoind.generate_block(1)
|
||||||
|
sync_blockheight(l1.bitcoin, [l1, l2])
|
||||||
|
|
||||||
# Make sure l2 received the withdrawal.
|
# Make sure l2 received the withdrawal.
|
||||||
wait_for(lambda: len(l2.rpc.listfunds()['outputs']) == 1)
|
assert len(l2.rpc.listfunds()['outputs']) == 1
|
||||||
outputs = l2.db_query('SELECT value FROM outputs WHERE status=0;')
|
outputs = l2.db_query('SELECT value FROM outputs WHERE status=0;')
|
||||||
assert only_one(outputs)['value'] == 2 * amount
|
assert only_one(outputs)['value'] == 2 * amount
|
||||||
|
|
||||||
# Now make sure an additional two of them were marked as spent
|
# Now make sure an additional two of them were marked as spent
|
||||||
assert l1.db_query('SELECT COUNT(*) as c FROM outputs WHERE status=2')[0]['c'] == 4
|
assert l1.db_query('SELECT COUNT(*) as c FROM outputs WHERE status=2')[0]['c'] == 4
|
||||||
|
assert l1.db_query('SELECT COUNT(*) as c FROM outputs WHERE status=1')[0]['c'] == 0
|
||||||
|
|
||||||
# Simple test for withdrawal to P2WPKH
|
# Simple test for withdrawal to P2WPKH
|
||||||
# Address from: https://bc-2.jp/tools/bech32demo/index.html
|
# Address from: https://bc-2.jp/tools/bech32demo/index.html
|
||||||
@@ -88,6 +95,7 @@ def test_withdraw(node_factory, bitcoind):
|
|||||||
l1.rpc.withdraw('tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxxxxxx', 2 * amount)
|
l1.rpc.withdraw('tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxxxxxx', 2 * amount)
|
||||||
l1.rpc.withdraw(waddr, 2 * amount)
|
l1.rpc.withdraw(waddr, 2 * amount)
|
||||||
bitcoind.generate_block(1)
|
bitcoind.generate_block(1)
|
||||||
|
sync_blockheight(l1.bitcoin, [l1])
|
||||||
# Now make sure additional two of them were marked as spent
|
# Now make sure additional two of them were marked as spent
|
||||||
assert l1.db_query('SELECT COUNT(*) as c FROM outputs WHERE status=2')[0]['c'] == 6
|
assert l1.db_query('SELECT COUNT(*) as c FROM outputs WHERE status=2')[0]['c'] == 6
|
||||||
|
|
||||||
@@ -102,6 +110,7 @@ def test_withdraw(node_factory, bitcoind):
|
|||||||
l1.rpc.withdraw('tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qxxxxxx', 2 * amount)
|
l1.rpc.withdraw('tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qxxxxxx', 2 * amount)
|
||||||
l1.rpc.withdraw(waddr, 2 * amount)
|
l1.rpc.withdraw(waddr, 2 * amount)
|
||||||
bitcoind.generate_block(1)
|
bitcoind.generate_block(1)
|
||||||
|
sync_blockheight(l1.bitcoin, [l1])
|
||||||
# Now make sure additional two of them were marked as spent
|
# Now make sure additional two of them were marked as spent
|
||||||
assert l1.db_query('SELECT COUNT(*) as c FROM outputs WHERE status=2')[0]['c'] == 8
|
assert l1.db_query('SELECT COUNT(*) as c FROM outputs WHERE status=2')[0]['c'] == 8
|
||||||
|
|
||||||
@@ -143,7 +152,7 @@ def test_withdraw(node_factory, bitcoind):
|
|||||||
assert l1.db_query('SELECT COUNT(*) as c FROM outputs WHERE status=0')[0]['c'] == 0
|
assert l1.db_query('SELECT COUNT(*) as c FROM outputs WHERE status=0')[0]['c'] == 0
|
||||||
|
|
||||||
# This should fail, can't even afford fee.
|
# This should fail, can't even afford fee.
|
||||||
with pytest.raises(RpcError, match=r'Cannot afford transaction'):
|
with pytest.raises(RpcError, match=r'Could not afford'):
|
||||||
l1.rpc.withdraw(waddr, 'all')
|
l1.rpc.withdraw(waddr, 'all')
|
||||||
|
|
||||||
# Add some funds to withdraw
|
# Add some funds to withdraw
|
||||||
@@ -225,7 +234,7 @@ def test_addfunds_from_block(node_factory, bitcoind):
|
|||||||
addr = l1.rpc.newaddr("bech32")['bech32']
|
addr = l1.rpc.newaddr("bech32")['bech32']
|
||||||
l1.rpc.withdraw(addr, "all")
|
l1.rpc.withdraw(addr, "all")
|
||||||
bitcoind.generate_block(1)
|
bitcoind.generate_block(1)
|
||||||
time.sleep(1)
|
sync_blockheight(bitcoind, [l1])
|
||||||
|
|
||||||
# The address we detect must match what was paid to.
|
# The address we detect must match what was paid to.
|
||||||
output = only_one(l1.rpc.listfunds()['outputs'])
|
output = only_one(l1.rpc.listfunds()['outputs'])
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
#include <common/json_helpers.h>
|
#include <common/json_helpers.h>
|
||||||
#include <common/jsonrpc_errors.h>
|
#include <common/jsonrpc_errors.h>
|
||||||
#include <common/key_derive.h>
|
#include <common/key_derive.h>
|
||||||
#include <common/wallet_tx.h>
|
|
||||||
#include <lightningd/jsonrpc.h>
|
#include <lightningd/jsonrpc.h>
|
||||||
#include <lightningd/lightningd.h>
|
#include <lightningd/lightningd.h>
|
||||||
#include <wallet/wallet.h>
|
#include <wallet/wallet.h>
|
||||||
@@ -314,6 +313,21 @@ static struct command_result *finish_psbt(struct command *cmd,
|
|||||||
return command_success(cmd, response);
|
return command_success(cmd, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline u32 minconf_to_maxheight(u32 minconf, struct lightningd *ld)
|
||||||
|
{
|
||||||
|
/* No confirmations is special, we need to disable the check in the
|
||||||
|
* selection */
|
||||||
|
if (minconf == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Avoid wrapping around and suddenly allowing any confirmed
|
||||||
|
* outputs. Since we can't have a coinbase output, and 0 is taken for
|
||||||
|
* the disable case, we can just clamp to 1. */
|
||||||
|
if (minconf >= ld->topology->tip->height)
|
||||||
|
return 1;
|
||||||
|
return ld->topology->tip->height - minconf + 1;
|
||||||
|
}
|
||||||
|
|
||||||
static struct command_result *json_fundpsbt(struct command *cmd,
|
static struct command_result *json_fundpsbt(struct command *cmd,
|
||||||
const char *buffer,
|
const char *buffer,
|
||||||
const jsmntok_t *obj UNNEEDED,
|
const jsmntok_t *obj UNNEEDED,
|
||||||
|
|||||||
@@ -908,7 +908,7 @@ static bool test_wallet_outputs(struct lightningd *ld, const tal_t *ctx)
|
|||||||
struct utxo u;
|
struct utxo u;
|
||||||
struct pubkey pk;
|
struct pubkey pk;
|
||||||
struct node_id id;
|
struct node_id id;
|
||||||
struct amount_sat fee_estimate, change_satoshis;
|
struct utxo *one_utxo;
|
||||||
const struct utxo **utxos;
|
const struct utxo **utxos;
|
||||||
CHECK(w);
|
CHECK(w);
|
||||||
|
|
||||||
@@ -941,19 +941,23 @@ static bool test_wallet_outputs(struct lightningd *ld, const tal_t *ctx)
|
|||||||
"wallet_add_utxo with close_info");
|
"wallet_add_utxo with close_info");
|
||||||
|
|
||||||
/* Now select them */
|
/* Now select them */
|
||||||
utxos = wallet_select_coins(w, w, true, AMOUNT_SAT(2), 0, 21,
|
utxos = tal_arr(w, const struct utxo *, 0);
|
||||||
0 /* no confirmations required */,
|
while ((one_utxo = wallet_find_utxo(w, w, 0, NULL, 253,
|
||||||
&fee_estimate, &change_satoshis);
|
0 /* no confirmations required */,
|
||||||
CHECK(utxos && tal_count(utxos) == 2);
|
utxos)) != NULL) {
|
||||||
|
tal_arr_expand(&utxos, one_utxo);
|
||||||
|
}
|
||||||
|
CHECK(tal_count(utxos) == 2);
|
||||||
|
|
||||||
|
if (utxos[0]->close_info)
|
||||||
|
u = *utxos[0];
|
||||||
|
else
|
||||||
|
u = *utxos[1];
|
||||||
|
|
||||||
u = *utxos[1];
|
|
||||||
CHECK(u.close_info->channel_id == 42 &&
|
CHECK(u.close_info->channel_id == 42 &&
|
||||||
pubkey_eq(u.close_info->commitment_point, &pk) &&
|
pubkey_eq(u.close_info->commitment_point, &pk) &&
|
||||||
node_id_eq(&u.close_info->peer_id, &id) &&
|
node_id_eq(&u.close_info->peer_id, &id) &&
|
||||||
u.close_info->option_anchor_outputs == false);
|
u.close_info->option_anchor_outputs == false);
|
||||||
/* Now un-reserve them for the tests below */
|
|
||||||
tal_free(utxos);
|
|
||||||
|
|
||||||
|
|
||||||
/* Attempt to reserve the utxo */
|
/* Attempt to reserve the utxo */
|
||||||
CHECK_MSG(wallet_update_output_status(w, &u.txid, u.outnum,
|
CHECK_MSG(wallet_update_output_status(w, &u.txid, u.outnum,
|
||||||
@@ -993,12 +997,19 @@ static bool test_wallet_outputs(struct lightningd *ld, const tal_t *ctx)
|
|||||||
"wallet_add_utxo with close_info no commitment_point");
|
"wallet_add_utxo with close_info no commitment_point");
|
||||||
|
|
||||||
/* Now select it */
|
/* Now select it */
|
||||||
utxos = wallet_select_coins(w, w, true, AMOUNT_SAT(5), 0, 21,
|
utxos = tal_arr(w, const struct utxo *, 0);
|
||||||
0 /* no confirmations required */,
|
while ((one_utxo = wallet_find_utxo(w, w, 0, NULL, 253,
|
||||||
&fee_estimate, &change_satoshis);
|
0 /* no confirmations required */,
|
||||||
CHECK(utxos && tal_count(utxos) == 2);
|
utxos)) != NULL) {
|
||||||
|
tal_arr_expand(&utxos, one_utxo);
|
||||||
|
}
|
||||||
|
CHECK(tal_count(utxos) == 2);
|
||||||
|
|
||||||
|
if (utxos[0]->close_info)
|
||||||
|
u = *utxos[0];
|
||||||
|
else
|
||||||
|
u = *utxos[1];
|
||||||
|
|
||||||
u = *utxos[1];
|
|
||||||
CHECK(u.close_info->channel_id == 42 &&
|
CHECK(u.close_info->channel_id == 42 &&
|
||||||
u.close_info->commitment_point == NULL &&
|
u.close_info->commitment_point == NULL &&
|
||||||
node_id_eq(&u.close_info->peer_id, &id) &&
|
node_id_eq(&u.close_info->peer_id, &id) &&
|
||||||
|
|||||||
265
wallet/wallet.c
265
wallet/wallet.c
@@ -67,7 +67,6 @@ struct wallet *wallet_new(struct lightningd *ld, struct timers *timers,
|
|||||||
wallet->bip32_base = tal_steal(wallet, bip32_base);
|
wallet->bip32_base = tal_steal(wallet, bip32_base);
|
||||||
wallet->keyscan_gap = 50;
|
wallet->keyscan_gap = 50;
|
||||||
list_head_init(&wallet->unstored_payments);
|
list_head_init(&wallet->unstored_payments);
|
||||||
list_head_init(&wallet->unreleased_txs);
|
|
||||||
wallet->db = db_setup(wallet, ld, wallet->bip32_base);
|
wallet->db = db_setup(wallet, ld, wallet->bip32_base);
|
||||||
|
|
||||||
db_begin_transaction(wallet->db);
|
db_begin_transaction(wallet->db);
|
||||||
@@ -162,7 +161,7 @@ static bool wallet_add_utxo(struct wallet *w, struct utxo *utxo,
|
|||||||
static struct utxo *wallet_stmt2output(const tal_t *ctx, struct db_stmt *stmt)
|
static struct utxo *wallet_stmt2output(const tal_t *ctx, struct db_stmt *stmt)
|
||||||
{
|
{
|
||||||
struct utxo *utxo = tal(ctx, struct utxo);
|
struct utxo *utxo = tal(ctx, struct utxo);
|
||||||
u32 *blockheight, *spendheight, *reserved_til;
|
u32 *blockheight, *spendheight;
|
||||||
db_column_txid(stmt, 0, &utxo->txid);
|
db_column_txid(stmt, 0, &utxo->txid);
|
||||||
utxo->outnum = db_column_int(stmt, 1);
|
utxo->outnum = db_column_int(stmt, 1);
|
||||||
db_column_amount_sat(stmt, 2, &utxo->amount);
|
db_column_amount_sat(stmt, 2, &utxo->amount);
|
||||||
@@ -192,7 +191,6 @@ static struct utxo *wallet_stmt2output(const tal_t *ctx, struct db_stmt *stmt)
|
|||||||
|
|
||||||
utxo->blockheight = NULL;
|
utxo->blockheight = NULL;
|
||||||
utxo->spendheight = NULL;
|
utxo->spendheight = NULL;
|
||||||
utxo->reserved_til = NULL;
|
|
||||||
|
|
||||||
if (!db_column_is_null(stmt, 10)) {
|
if (!db_column_is_null(stmt, 10)) {
|
||||||
blockheight = tal(utxo, u32);
|
blockheight = tal(utxo, u32);
|
||||||
@@ -206,11 +204,9 @@ static struct utxo *wallet_stmt2output(const tal_t *ctx, struct db_stmt *stmt)
|
|||||||
utxo->spendheight = spendheight;
|
utxo->spendheight = spendheight;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!db_column_is_null(stmt, 13)) {
|
/* This column can be null if 0.9.1 db or below. */
|
||||||
reserved_til = tal(utxo, u32);
|
utxo->reserved_til = tal(utxo, u32);
|
||||||
*reserved_til = db_column_int(stmt, 13);
|
*utxo->reserved_til = db_column_int_or_default(stmt, 13, 0);
|
||||||
utxo->reserved_til = reserved_til;
|
|
||||||
}
|
|
||||||
|
|
||||||
return utxo;
|
return utxo;
|
||||||
}
|
}
|
||||||
@@ -453,8 +449,6 @@ bool wallet_reserve_utxo(struct wallet *w, struct utxo *utxo, u32 current_height
|
|||||||
|
|
||||||
if (utxo->status == output_state_reserved)
|
if (utxo->status == output_state_reserved)
|
||||||
assert(utxo->reserved_til);
|
assert(utxo->reserved_til);
|
||||||
else
|
|
||||||
assert(!utxo->reserved_til);
|
|
||||||
|
|
||||||
switch (utxo->status) {
|
switch (utxo->status) {
|
||||||
case output_state_spent:
|
case output_state_spent:
|
||||||
@@ -489,8 +483,7 @@ void wallet_unreserve_utxo(struct wallet *w, struct utxo *utxo, u32 current_heig
|
|||||||
if (!utxo->reserved_til)
|
if (!utxo->reserved_til)
|
||||||
utxo->reserved_til = tal_dup(utxo, u32, ¤t_height);
|
utxo->reserved_til = tal_dup(utxo, u32, ¤t_height);
|
||||||
assert(utxo->reserved_til);
|
assert(utxo->reserved_til);
|
||||||
} else
|
}
|
||||||
assert(!utxo->reserved_til);
|
|
||||||
|
|
||||||
if (utxo->status != output_state_reserved)
|
if (utxo->status != output_state_reserved)
|
||||||
fatal("UTXO %s:%u is not reserved",
|
fatal("UTXO %s:%u is not reserved",
|
||||||
@@ -645,175 +638,6 @@ bool wallet_add_onchaind_utxo(struct wallet *w,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct utxo **wallet_select(const tal_t *ctx, struct wallet *w,
|
|
||||||
struct amount_sat sat,
|
|
||||||
const u32 feerate_per_kw,
|
|
||||||
size_t outscriptlen,
|
|
||||||
bool may_have_change,
|
|
||||||
u32 maxheight,
|
|
||||||
struct amount_sat *satoshi_in,
|
|
||||||
struct amount_sat *fee_estimate)
|
|
||||||
{
|
|
||||||
size_t i = 0;
|
|
||||||
struct utxo **available;
|
|
||||||
u64 weight;
|
|
||||||
size_t num_outputs = may_have_change ? 2 : 1;
|
|
||||||
const struct utxo **utxos = tal_arr(ctx, const struct utxo *, 0);
|
|
||||||
tal_add_destructor2(utxos, destroy_utxos, w);
|
|
||||||
|
|
||||||
/* We assume < 253 inputs, and margin is tiny if we're wrong */
|
|
||||||
weight = bitcoin_tx_core_weight(1, num_outputs)
|
|
||||||
+ bitcoin_tx_output_weight(outscriptlen);
|
|
||||||
|
|
||||||
/* Change output will be P2WPKH */
|
|
||||||
if (may_have_change)
|
|
||||||
weight += bitcoin_tx_output_weight(BITCOIN_SCRIPTPUBKEY_P2WPKH_LEN);
|
|
||||||
|
|
||||||
*fee_estimate = AMOUNT_SAT(0);
|
|
||||||
*satoshi_in = AMOUNT_SAT(0);
|
|
||||||
|
|
||||||
available = wallet_get_utxos(ctx, w, output_state_available);
|
|
||||||
|
|
||||||
for (i = 0; i < tal_count(available); i++) {
|
|
||||||
struct amount_sat needed;
|
|
||||||
struct utxo *u = tal_steal(utxos, available[i]);
|
|
||||||
|
|
||||||
/* If we require confirmations check that we have a
|
|
||||||
* confirmation height and that it is below the required
|
|
||||||
* maxheight (current_height - minconf) */
|
|
||||||
if (maxheight != 0 &&
|
|
||||||
(!u->blockheight || *u->blockheight > maxheight)) {
|
|
||||||
tal_free(u);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
tal_arr_expand(&utxos, u);
|
|
||||||
|
|
||||||
if (!wallet_update_output_status(
|
|
||||||
w, &available[i]->txid, available[i]->outnum,
|
|
||||||
output_state_available, output_state_reserved))
|
|
||||||
fatal("Unable to reserve output");
|
|
||||||
|
|
||||||
weight += bitcoin_tx_simple_input_weight(u->is_p2sh);
|
|
||||||
|
|
||||||
if (!amount_sat_add(satoshi_in, *satoshi_in, u->amount))
|
|
||||||
fatal("Overflow in available satoshis %zu/%zu %s + %s",
|
|
||||||
i, tal_count(available),
|
|
||||||
type_to_string(tmpctx, struct amount_sat,
|
|
||||||
satoshi_in),
|
|
||||||
type_to_string(tmpctx, struct amount_sat,
|
|
||||||
&u->amount));
|
|
||||||
|
|
||||||
*fee_estimate = amount_tx_fee(feerate_per_kw, weight);
|
|
||||||
if (!amount_sat_add(&needed, sat, *fee_estimate))
|
|
||||||
fatal("Overflow in fee estimate %zu/%zu %s + %s",
|
|
||||||
i, tal_count(available),
|
|
||||||
type_to_string(tmpctx, struct amount_sat, &sat),
|
|
||||||
type_to_string(tmpctx, struct amount_sat,
|
|
||||||
fee_estimate));
|
|
||||||
if (amount_sat_greater_eq(*satoshi_in, needed))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
tal_free(available);
|
|
||||||
|
|
||||||
return utxos;
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct utxo **wallet_select_coins(const tal_t *ctx, struct wallet *w,
|
|
||||||
bool with_change,
|
|
||||||
struct amount_sat sat,
|
|
||||||
const u32 feerate_per_kw,
|
|
||||||
size_t outscriptlen,
|
|
||||||
u32 maxheight,
|
|
||||||
struct amount_sat *fee_estimate,
|
|
||||||
struct amount_sat *change)
|
|
||||||
{
|
|
||||||
struct amount_sat satoshi_in;
|
|
||||||
const struct utxo **utxo;
|
|
||||||
|
|
||||||
utxo = wallet_select(ctx, w, sat, feerate_per_kw,
|
|
||||||
outscriptlen, with_change, maxheight,
|
|
||||||
&satoshi_in, fee_estimate);
|
|
||||||
|
|
||||||
/* Couldn't afford it? */
|
|
||||||
if (!amount_sat_sub(change, satoshi_in, sat)
|
|
||||||
|| !amount_sat_sub(change, *change, *fee_estimate))
|
|
||||||
return tal_free(utxo);
|
|
||||||
|
|
||||||
if (!with_change)
|
|
||||||
*change = AMOUNT_SAT(0);
|
|
||||||
|
|
||||||
return utxo;
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct utxo **wallet_select_specific(const tal_t *ctx, struct wallet *w,
|
|
||||||
struct bitcoin_txid **txids,
|
|
||||||
u32 **outnums)
|
|
||||||
{
|
|
||||||
size_t i, j;
|
|
||||||
struct utxo **available;
|
|
||||||
const struct utxo **utxos = tal_arr(ctx, const struct utxo*, 0);
|
|
||||||
tal_add_destructor2(utxos, destroy_utxos, w);
|
|
||||||
|
|
||||||
available = wallet_get_utxos(ctx, w, output_state_available);
|
|
||||||
for (i = 0; i < tal_count(txids); i++) {
|
|
||||||
for (j = 0; j < tal_count(available); j++) {
|
|
||||||
|
|
||||||
if (bitcoin_txid_eq(&available[j]->txid, txids[i])
|
|
||||||
&& available[j]->outnum == *outnums[i]) {
|
|
||||||
struct utxo *u = tal_steal(utxos, available[j]);
|
|
||||||
tal_arr_expand(&utxos, u);
|
|
||||||
|
|
||||||
if (!wallet_update_output_status(
|
|
||||||
w, &available[j]->txid, available[j]->outnum,
|
|
||||||
output_state_available, output_state_reserved))
|
|
||||||
fatal("Unable to reserve output");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tal_free(available);
|
|
||||||
|
|
||||||
return utxos;
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct utxo **wallet_select_all(const tal_t *ctx, struct wallet *w,
|
|
||||||
const u32 feerate_per_kw,
|
|
||||||
size_t outscriptlen,
|
|
||||||
u32 maxheight,
|
|
||||||
struct amount_sat *value,
|
|
||||||
struct amount_sat *fee_estimate)
|
|
||||||
{
|
|
||||||
struct amount_sat satoshi_in;
|
|
||||||
const struct utxo **utxo;
|
|
||||||
|
|
||||||
/* Huge value, but won't overflow on addition */
|
|
||||||
utxo = wallet_select(ctx, w, AMOUNT_SAT(1ULL << 56), feerate_per_kw,
|
|
||||||
outscriptlen, false, maxheight,
|
|
||||||
&satoshi_in, fee_estimate);
|
|
||||||
|
|
||||||
/* Can't afford fees? */
|
|
||||||
if (!amount_sat_sub(value, satoshi_in, *fee_estimate))
|
|
||||||
return tal_free(utxo);
|
|
||||||
|
|
||||||
return utxo;
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 *derive_redeem_scriptsig(const tal_t *ctx, struct wallet *w, u32 keyindex)
|
|
||||||
{
|
|
||||||
struct ext_key ext;
|
|
||||||
struct pubkey key;
|
|
||||||
|
|
||||||
if (bip32_key_from_parent(w->bip32_base, keyindex,
|
|
||||||
BIP32_FLAG_KEY_PUBLIC, &ext) != WALLY_OK) {
|
|
||||||
fatal("Unable to derive pubkey");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pubkey_from_der(ext.pub_key, PUBKEY_CMPR_LEN, &key))
|
|
||||||
fatal("Unble to derive pubkey from DER");
|
|
||||||
|
|
||||||
return bitcoin_scriptsig_p2sh_p2wpkh(ctx, &key);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool wallet_can_spend(struct wallet *w, const u8 *script,
|
bool wallet_can_spend(struct wallet *w, const u8 *script,
|
||||||
u32 *index, bool *output_is_p2sh)
|
u32 *index, bool *output_is_p2sh)
|
||||||
{
|
{
|
||||||
@@ -3933,85 +3757,6 @@ const struct forwarding *wallet_forwarded_payments_get(struct wallet *w,
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct unreleased_tx *find_unreleased_tx(struct wallet *w,
|
|
||||||
const struct bitcoin_txid *txid)
|
|
||||||
{
|
|
||||||
struct unreleased_tx *utx;
|
|
||||||
|
|
||||||
list_for_each(&w->unreleased_txs, utx, list) {
|
|
||||||
if (bitcoin_txid_eq(txid, &utx->txid))
|
|
||||||
return utx;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void destroy_unreleased_tx(struct unreleased_tx *utx)
|
|
||||||
{
|
|
||||||
list_del(&utx->list);
|
|
||||||
}
|
|
||||||
|
|
||||||
void remove_unreleased_tx(struct unreleased_tx *utx)
|
|
||||||
{
|
|
||||||
tal_del_destructor(utx, destroy_unreleased_tx);
|
|
||||||
list_del(&utx->list);
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_unreleased_tx(struct wallet *w, struct unreleased_tx *utx)
|
|
||||||
{
|
|
||||||
list_add_tail(&w->unreleased_txs, &utx->list);
|
|
||||||
tal_add_destructor(utx, destroy_unreleased_tx);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* These will touch the db, so need to be explicitly freed. */
|
|
||||||
void free_unreleased_txs(struct wallet *w)
|
|
||||||
{
|
|
||||||
struct unreleased_tx *utx;
|
|
||||||
|
|
||||||
while ((utx = list_top(&w->unreleased_txs, struct unreleased_tx, list)))
|
|
||||||
tal_free(utx);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void process_utxo_result(struct bitcoind *bitcoind,
|
|
||||||
const struct bitcoin_tx_output *txout,
|
|
||||||
void *_utxos)
|
|
||||||
{
|
|
||||||
struct utxo **utxos = _utxos;
|
|
||||||
enum output_status newstate =
|
|
||||||
txout == NULL ? output_state_spent : output_state_available;
|
|
||||||
|
|
||||||
/* Don't unreserve ones which are on timers */
|
|
||||||
if (!utxos[0]->reserved_til || newstate == output_state_spent) {
|
|
||||||
log_unusual(bitcoind->ld->wallet->log,
|
|
||||||
"wallet: reserved output %s/%u reset to %s",
|
|
||||||
type_to_string(tmpctx, struct bitcoin_txid, &utxos[0]->txid),
|
|
||||||
utxos[0]->outnum,
|
|
||||||
newstate == output_state_spent ? "spent" : "available");
|
|
||||||
wallet_update_output_status(bitcoind->ld->wallet,
|
|
||||||
&utxos[0]->txid, utxos[0]->outnum,
|
|
||||||
utxos[0]->status, newstate);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we have more, resolve them too. */
|
|
||||||
tal_arr_remove(&utxos, 0);
|
|
||||||
if (tal_count(utxos) != 0) {
|
|
||||||
bitcoind_getutxout(bitcoind, &utxos[0]->txid, utxos[0]->outnum,
|
|
||||||
process_utxo_result, utxos);
|
|
||||||
} else
|
|
||||||
tal_free(utxos);
|
|
||||||
}
|
|
||||||
|
|
||||||
void wallet_clean_utxos(struct wallet *w, struct bitcoind *bitcoind)
|
|
||||||
{
|
|
||||||
struct utxo **utxos = wallet_get_utxos(NULL, w, output_state_reserved);
|
|
||||||
|
|
||||||
if (tal_count(utxos) != 0) {
|
|
||||||
bitcoind_getutxout(bitcoind, &utxos[0]->txid, utxos[0]->outnum,
|
|
||||||
process_utxo_result,
|
|
||||||
notleak_with_children(utxos));
|
|
||||||
} else
|
|
||||||
tal_free(utxos);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wallet_transaction *wallet_transactions_get(struct wallet *w, const tal_t *ctx)
|
struct wallet_transaction *wallet_transactions_get(struct wallet *w, const tal_t *ctx)
|
||||||
{
|
{
|
||||||
struct db_stmt *stmt;
|
struct db_stmt *stmt;
|
||||||
|
|||||||
@@ -48,26 +48,10 @@ struct wallet {
|
|||||||
* the blockchain. This is currently all P2WSH outputs */
|
* the blockchain. This is currently all P2WSH outputs */
|
||||||
struct outpointfilter *utxoset_outpoints;
|
struct outpointfilter *utxoset_outpoints;
|
||||||
|
|
||||||
/* Unreleased txs, waiting for txdiscard/txsend */
|
|
||||||
struct list_head unreleased_txs;
|
|
||||||
|
|
||||||
/* How many keys should we look ahead at most? */
|
/* How many keys should we look ahead at most? */
|
||||||
u64 keyscan_gap;
|
u64 keyscan_gap;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* A transaction we've txprepared, but haven't signed and released yet */
|
|
||||||
struct unreleased_tx {
|
|
||||||
/* In wallet->unreleased_txs */
|
|
||||||
struct list_node list;
|
|
||||||
/* All the utxos. */
|
|
||||||
struct wallet_tx *wtx;
|
|
||||||
/* Outputs(scriptpubkey and satoshi) this pays to. */
|
|
||||||
struct bitcoin_tx_output **outputs;
|
|
||||||
/* The tx itself (unsigned initially) */
|
|
||||||
struct bitcoin_tx *tx;
|
|
||||||
struct bitcoin_txid txid;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Possible states for tracked outputs in the database. Not sure yet
|
/* Possible states for tracked outputs in the database. Not sure yet
|
||||||
* whether we really want to have reservations reflected in the
|
* whether we really want to have reservations reflected in the
|
||||||
* database, it would simplify queries at the cost of some IO ops */
|
* database, it would simplify queries at the cost of some IO ops */
|
||||||
@@ -440,30 +424,6 @@ struct utxo *wallet_utxo_get(const tal_t *ctx, struct wallet *w,
|
|||||||
const struct bitcoin_txid *txid,
|
const struct bitcoin_txid *txid,
|
||||||
u32 outnum);
|
u32 outnum);
|
||||||
|
|
||||||
const struct utxo **wallet_select_coins(const tal_t *ctx, struct wallet *w,
|
|
||||||
bool with_change,
|
|
||||||
struct amount_sat value,
|
|
||||||
const u32 feerate_per_kw,
|
|
||||||
size_t outscriptlen,
|
|
||||||
u32 maxheight,
|
|
||||||
struct amount_sat *fee_estimate,
|
|
||||||
struct amount_sat *change_satoshi);
|
|
||||||
|
|
||||||
const struct utxo **wallet_select_all(const tal_t *ctx, struct wallet *w,
|
|
||||||
const u32 feerate_per_kw,
|
|
||||||
size_t outscriptlen,
|
|
||||||
u32 maxheight,
|
|
||||||
struct amount_sat *sat,
|
|
||||||
struct amount_sat *fee_estimate);
|
|
||||||
|
|
||||||
/* derive_redeem_scriptsig - Compute the scriptSig for a P2SH-P2WPKH
|
|
||||||
*
|
|
||||||
* @ctx - allocation context
|
|
||||||
* @w - wallet
|
|
||||||
* @keyindex - index of the internal BIP32 key
|
|
||||||
*/
|
|
||||||
u8 *derive_redeem_scriptsig(const tal_t *ctx, struct wallet *w, u32 keyindex);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* wallet_select_specific - Select utxos given an array of txids and an array of outputs index
|
* wallet_select_specific - Select utxos given an array of txids and an array of outputs index
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -15,8 +15,6 @@
|
|||||||
#include <common/status.h>
|
#include <common/status.h>
|
||||||
#include <common/utils.h>
|
#include <common/utils.h>
|
||||||
#include <common/utxo.h>
|
#include <common/utxo.h>
|
||||||
#include <common/wallet_tx.h>
|
|
||||||
#include <common/withdraw_tx.h>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <hsmd/hsmd_wiregen.h>
|
#include <hsmd/hsmd_wiregen.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
@@ -35,283 +33,6 @@
|
|||||||
#include <wally_bip32.h>
|
#include <wally_bip32.h>
|
||||||
#include <wire/wire_sync.h>
|
#include <wire/wire_sync.h>
|
||||||
|
|
||||||
struct tx_broadcast {
|
|
||||||
struct command *cmd;
|
|
||||||
const struct utxo **utxos;
|
|
||||||
const struct wally_tx *wtx;
|
|
||||||
struct amount_sat *expected_change;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct tx_broadcast *unreleased_tx_to_broadcast(const tal_t *ctx,
|
|
||||||
struct command *cmd,
|
|
||||||
struct unreleased_tx *utx)
|
|
||||||
{
|
|
||||||
struct tx_broadcast *txb = tal(ctx, struct tx_broadcast);
|
|
||||||
struct amount_sat *change = tal(txb, struct amount_sat);
|
|
||||||
|
|
||||||
txb->cmd = cmd;
|
|
||||||
txb->utxos = utx->wtx->utxos;
|
|
||||||
txb->wtx = utx->tx->wtx;
|
|
||||||
*change = utx->wtx->change;
|
|
||||||
txb->expected_change = change;
|
|
||||||
return txb;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* wallet_withdrawal_broadcast - The tx has been broadcast (or it failed)
|
|
||||||
*
|
|
||||||
* This is the final step in the withdrawal. We either successfully
|
|
||||||
* broadcast the withdrawal transaction or it failed somehow. So we
|
|
||||||
* report success or a broadcast failure. Upon success we also mark
|
|
||||||
* the used outputs as spent, and add the change output to our pool of
|
|
||||||
* available outputs.
|
|
||||||
*/
|
|
||||||
static void wallet_withdrawal_broadcast(struct bitcoind *bitcoind UNUSED,
|
|
||||||
bool success, const char *msg,
|
|
||||||
struct tx_broadcast *txb)
|
|
||||||
{
|
|
||||||
struct command *cmd = txb->cmd;
|
|
||||||
struct lightningd *ld = cmd->ld;
|
|
||||||
|
|
||||||
/* FIXME: This won't be necessary once we use ccan/json_out! */
|
|
||||||
/* Massage output into shape so it doesn't kill the JSON serialization */
|
|
||||||
char *output = tal_strjoin(cmd, tal_strsplit(cmd, msg, "\n", STR_NO_EMPTY), " ", STR_NO_TRAIL);
|
|
||||||
if (success) {
|
|
||||||
struct bitcoin_txid txid;
|
|
||||||
struct amount_sat change = AMOUNT_SAT(0);
|
|
||||||
|
|
||||||
/* Mark used outputs as spent */
|
|
||||||
wallet_confirm_utxos(ld->wallet, txb->utxos);
|
|
||||||
|
|
||||||
/* Extract the change output and add it to the DB */
|
|
||||||
wallet_extract_owned_outputs(ld->wallet, txb->wtx, NULL, &change);
|
|
||||||
|
|
||||||
/* Note normally, change_satoshi == withdraw->wtx->change, but
|
|
||||||
* not if we're actually making a payment to ourselves! */
|
|
||||||
if (txb->expected_change)
|
|
||||||
assert(amount_sat_greater_eq(change, *txb->expected_change));
|
|
||||||
|
|
||||||
struct json_stream *response = json_stream_success(cmd);
|
|
||||||
wally_txid(txb->wtx, &txid);
|
|
||||||
json_add_hex_talarr(response, "tx",
|
|
||||||
linearize_wtx(tmpctx, txb->wtx));
|
|
||||||
json_add_txid(response, "txid", &txid);
|
|
||||||
was_pending(command_success(cmd, response));
|
|
||||||
} else {
|
|
||||||
was_pending(command_fail(cmd, LIGHTNINGD,
|
|
||||||
"Error broadcasting transaction: %s. Unsent tx discarded %s",
|
|
||||||
output,
|
|
||||||
type_to_string(tmpctx, struct wally_tx, txb->wtx)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Signs the tx, broadcasts it: broadcast calls wallet_withdrawal_broadcast */
|
|
||||||
static struct command_result *broadcast_and_wait(struct command *cmd,
|
|
||||||
struct unreleased_tx *utx)
|
|
||||||
{
|
|
||||||
struct wally_psbt *signed_psbt;
|
|
||||||
struct wally_tx *signed_wtx;
|
|
||||||
struct bitcoin_txid signed_txid;
|
|
||||||
|
|
||||||
/* FIXME: hsm will sign almost anything, but it should really
|
|
||||||
* fail cleanly (not abort!) and let us report the error here. */
|
|
||||||
u8 *msg = towire_hsmd_sign_withdrawal(cmd, utx->wtx->utxos, utx->tx->psbt);
|
|
||||||
|
|
||||||
if (!wire_sync_write(cmd->ld->hsm_fd, take(msg)))
|
|
||||||
fatal("Could not write sign_withdrawal to HSM: %s",
|
|
||||||
strerror(errno));
|
|
||||||
|
|
||||||
msg = wire_sync_read(cmd, cmd->ld->hsm_fd);
|
|
||||||
|
|
||||||
if (!fromwire_hsmd_sign_withdrawal_reply(utx, msg, &signed_psbt))
|
|
||||||
fatal("HSM gave bad sign_withdrawal_reply %s",
|
|
||||||
tal_hex(tmpctx, msg));
|
|
||||||
|
|
||||||
signed_wtx = psbt_finalize(signed_psbt, true);
|
|
||||||
|
|
||||||
if (!signed_wtx) {
|
|
||||||
/* Have the utx persist past this command */
|
|
||||||
tal_steal(cmd->ld->wallet, utx);
|
|
||||||
add_unreleased_tx(cmd->ld->wallet, utx);
|
|
||||||
return command_fail(cmd, LIGHTNINGD,
|
|
||||||
"PSBT is not finalized %s",
|
|
||||||
type_to_string(tmpctx,
|
|
||||||
struct wally_psbt,
|
|
||||||
signed_psbt));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sanity check */
|
|
||||||
wally_txid(signed_wtx, &signed_txid);
|
|
||||||
if (!bitcoin_txid_eq(&signed_txid, &utx->txid))
|
|
||||||
fatal("HSM changed txid: unsigned %s, signed %s",
|
|
||||||
tal_hex(tmpctx, linearize_tx(tmpctx, utx->tx)),
|
|
||||||
tal_hex(tmpctx, linearize_wtx(tmpctx, signed_wtx)));
|
|
||||||
|
|
||||||
/* Replace unsigned tx by signed tx. */
|
|
||||||
wally_tx_free(utx->tx->wtx);
|
|
||||||
utx->tx->wtx = tal_steal(utx->tx, signed_wtx);
|
|
||||||
tal_free(utx->tx->psbt);
|
|
||||||
utx->tx->psbt = tal_steal(utx->tx, signed_psbt);
|
|
||||||
|
|
||||||
/* Now broadcast the transaction */
|
|
||||||
bitcoind_sendrawtx(cmd->ld->topology->bitcoind,
|
|
||||||
tal_hex(tmpctx, linearize_tx(tmpctx, utx->tx)),
|
|
||||||
wallet_withdrawal_broadcast,
|
|
||||||
unreleased_tx_to_broadcast(cmd, cmd, utx));
|
|
||||||
|
|
||||||
return command_still_pending(cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Parsing code for withdraw.
|
|
||||||
*
|
|
||||||
* Returns NULL on success, and fills in wtx, output and
|
|
||||||
* maybe changekey (owned by cmd). Otherwise, cmd has failed, so don't
|
|
||||||
* access it! (It's been freed). */
|
|
||||||
static struct command_result *json_prepare_tx(struct command *cmd,
|
|
||||||
const char *buffer,
|
|
||||||
const jsmntok_t *params,
|
|
||||||
struct unreleased_tx **utx)
|
|
||||||
{
|
|
||||||
u32 *feerate_per_kw = NULL;
|
|
||||||
struct command_result *result;
|
|
||||||
u32 *minconf, maxheight;
|
|
||||||
struct pubkey *changekey;
|
|
||||||
struct bitcoin_tx_output **outputs;
|
|
||||||
const u8 *destination = NULL;
|
|
||||||
size_t out_len;
|
|
||||||
const struct utxo **chosen_utxos = NULL;
|
|
||||||
u32 locktime;
|
|
||||||
|
|
||||||
*utx = tal(cmd, struct unreleased_tx);
|
|
||||||
(*utx)->wtx = tal(*utx, struct wallet_tx);
|
|
||||||
wtx_init(cmd, (*utx)->wtx, AMOUNT_SAT(-1ULL));
|
|
||||||
|
|
||||||
/* *withdraw* command still use 'destination' and 'satoshi' as parameters. */
|
|
||||||
if (!param(cmd, buffer, params,
|
|
||||||
p_req("destination", param_bitcoin_address,
|
|
||||||
&destination),
|
|
||||||
p_req("satoshi", param_wtx, (*utx)->wtx),
|
|
||||||
p_opt("feerate", param_feerate, &feerate_per_kw),
|
|
||||||
p_opt_def("minconf", param_number, &minconf, 1),
|
|
||||||
p_opt("utxos", param_utxos, &chosen_utxos),
|
|
||||||
NULL))
|
|
||||||
return command_param_failed();
|
|
||||||
|
|
||||||
/* Setting the locktime to the next block to be mined has multiple
|
|
||||||
* benefits:
|
|
||||||
* - anti fee-snipping (even if not yet likely)
|
|
||||||
* - less distinguishable transactions (with this we create
|
|
||||||
* general-purpose transactions which looks like bitcoind:
|
|
||||||
* native segwit, nlocktime set to tip, and sequence set to
|
|
||||||
* 0xFFFFFFFE by default. Other wallets are likely to implement
|
|
||||||
* this too).
|
|
||||||
*/
|
|
||||||
locktime = cmd->ld->topology->tip->height;
|
|
||||||
/* Eventually fuzz it too. */
|
|
||||||
if (pseudorand(10) == 0)
|
|
||||||
locktime -= (u32)pseudorand(100);
|
|
||||||
|
|
||||||
if (!feerate_per_kw) {
|
|
||||||
/* We mainly use `txprepare` for opening transactions, and FEERATE_OPENING
|
|
||||||
* is kind of the new FEERATE_NORMAL so it fits well `withdraw` too. */
|
|
||||||
result = param_feerate_estimate(cmd, &feerate_per_kw,
|
|
||||||
FEERATE_OPENING);
|
|
||||||
if (result)
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
maxheight = minconf_to_maxheight(*minconf, cmd->ld);
|
|
||||||
|
|
||||||
outputs = tal_arr(tmpctx, struct bitcoin_tx_output *, 1);
|
|
||||||
outputs[0] = new_tx_output(outputs, (*utx)->wtx->amount,
|
|
||||||
destination);
|
|
||||||
out_len = tal_count(outputs[0]->script);
|
|
||||||
|
|
||||||
if (chosen_utxos)
|
|
||||||
result = wtx_from_utxos((*utx)->wtx, *feerate_per_kw,
|
|
||||||
out_len, maxheight,
|
|
||||||
chosen_utxos);
|
|
||||||
else
|
|
||||||
result = wtx_select_utxos((*utx)->wtx, *feerate_per_kw,
|
|
||||||
out_len, maxheight);
|
|
||||||
|
|
||||||
if (result)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
/* Because of the max limit of AMOUNT_SAT(-1ULL),
|
|
||||||
* `(*utx)->wtx->all_funds` won't change in `wtx_select_utxos()` */
|
|
||||||
if ((*utx)->wtx->all_funds)
|
|
||||||
outputs[0]->amount = (*utx)->wtx->amount;
|
|
||||||
|
|
||||||
/* Add the change as the last output */
|
|
||||||
if (!amount_sat_eq((*utx)->wtx->change, AMOUNT_SAT(0))) {
|
|
||||||
struct bitcoin_tx_output *change_output;
|
|
||||||
|
|
||||||
changekey = tal(tmpctx, struct pubkey);
|
|
||||||
if (!bip32_pubkey(cmd->ld->wallet->bip32_base, changekey,
|
|
||||||
(*utx)->wtx->change_key_index))
|
|
||||||
return command_fail(cmd, LIGHTNINGD, "Keys generation failure");
|
|
||||||
|
|
||||||
change_output = new_tx_output(outputs, (*utx)->wtx->change,
|
|
||||||
scriptpubkey_p2wpkh(tmpctx, changekey));
|
|
||||||
tal_arr_expand(&outputs, change_output);
|
|
||||||
}
|
|
||||||
|
|
||||||
(*utx)->outputs = tal_steal(*utx, outputs);
|
|
||||||
(*utx)->tx = withdraw_tx(*utx, chainparams,
|
|
||||||
(*utx)->wtx->utxos,
|
|
||||||
(*utx)->outputs,
|
|
||||||
cmd->ld->wallet->bip32_base,
|
|
||||||
/* FIXME: Should probably be
|
|
||||||
* struct abs_locktime.
|
|
||||||
*/
|
|
||||||
locktime);
|
|
||||||
|
|
||||||
bitcoin_txid((*utx)->tx, &(*utx)->txid);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* json_withdraw - Entrypoint for the withdrawal flow
|
|
||||||
*
|
|
||||||
* A user has requested a withdrawal over the JSON-RPC, parse the
|
|
||||||
* request, select coins and a change key. Then send the request to
|
|
||||||
* the HSM to generate the signatures.
|
|
||||||
*/
|
|
||||||
static struct command_result *json_withdraw(struct command *cmd,
|
|
||||||
const char *buffer,
|
|
||||||
const jsmntok_t *obj UNNEEDED,
|
|
||||||
const jsmntok_t *params)
|
|
||||||
{
|
|
||||||
struct unreleased_tx *utx;
|
|
||||||
struct command_result *res;
|
|
||||||
|
|
||||||
res = json_prepare_tx(cmd, buffer, params, &utx);
|
|
||||||
if (res)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
/* Store the transaction in the DB and annotate it as a withdrawal */
|
|
||||||
wallet_transaction_add(cmd->ld->wallet, utx->tx->wtx, 0, 0);
|
|
||||||
wallet_transaction_annotate(cmd->ld->wallet, &utx->txid,
|
|
||||||
TX_WALLET_WITHDRAWAL, 0);
|
|
||||||
|
|
||||||
return broadcast_and_wait(cmd, utx);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct json_command withdraw_command = {
|
|
||||||
"withdraw",
|
|
||||||
"bitcoin",
|
|
||||||
json_withdraw,
|
|
||||||
"Send to {destination} address {satoshi} (or 'all') amount via Bitcoin "
|
|
||||||
"transaction, at optional {feerate}",
|
|
||||||
false,
|
|
||||||
"Send funds from the internal wallet to the specified address. Either "
|
|
||||||
"specify a number of satoshis to send or 'all' to sweep all funds in the "
|
|
||||||
"internal wallet to the address. Only use outputs that have at least "
|
|
||||||
"{minconf} confirmations."
|
|
||||||
};
|
|
||||||
AUTODATA(json_command, &withdraw_command);
|
|
||||||
|
|
||||||
/* May return NULL if encoding error occurs. */
|
/* May return NULL if encoding error occurs. */
|
||||||
static char *
|
static char *
|
||||||
encode_pubkey_to_addr(const tal_t *ctx,
|
encode_pubkey_to_addr(const tal_t *ctx,
|
||||||
|
|||||||
Reference in New Issue
Block a user