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/version.c \
|
||||
common/wallet.c \
|
||||
common/wallet_tx.c \
|
||||
common/wireaddr.c \
|
||||
common/wire_error.c \
|
||||
common/withdraw_tx.c
|
||||
common/wire_error.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/utils.o \
|
||||
common/utxo.o \
|
||||
common/version.o \
|
||||
common/withdraw_tx.o
|
||||
common/version.o
|
||||
|
||||
|
||||
lightningd/lightning_hsmd: $(HSMD_OBJS) $(HSMD_COMMON_OBJS) $(BITCOIN_OBJS) $(WIRE_OBJS)
|
||||
|
||||
@@ -37,7 +37,6 @@
|
||||
#include <common/type_to_string.h>
|
||||
#include <common/utils.h>
|
||||
#include <common/version.h>
|
||||
#include <common/withdraw_tx.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <hsmd/capabilities.h>
|
||||
|
||||
@@ -110,10 +110,8 @@ LIGHTNINGD_COMMON_OBJS := \
|
||||
common/utxo.o \
|
||||
common/version.o \
|
||||
common/wallet.o \
|
||||
common/wallet_tx.o \
|
||||
common/wire_error.o \
|
||||
common/wireaddr.o \
|
||||
common/withdraw_tx.o
|
||||
|
||||
include wallet/Makefile
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include <common/per_peer_state.h>
|
||||
#include <common/timeout.h>
|
||||
#include <common/utils.h>
|
||||
#include <common/wallet_tx.h>
|
||||
#include <common/wire_error.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
@@ -8,6 +8,7 @@ struct channel;
|
||||
struct crypto_state;
|
||||
struct lightningd;
|
||||
struct per_peer_state;
|
||||
struct peer;
|
||||
|
||||
void peer_start_channeld(struct channel *channel,
|
||||
struct per_peer_state *pps,
|
||||
|
||||
@@ -935,9 +935,6 @@ int main(int argc, char *argv[])
|
||||
min_blockheight, max_blockheight);
|
||||
|
||||
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
|
||||
* 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) */
|
||||
db_begin_transaction(ld->wallet->db);
|
||||
tal_free(ld->jsonrpc);
|
||||
free_unreleased_txs(ld->wallet);
|
||||
db_commit_transaction(ld->wallet->db);
|
||||
|
||||
/* Clean our our HTLC maps, since they use malloc. */
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include <common/penalty_base.h>
|
||||
#include <common/per_peer_state.h>
|
||||
#include <common/utils.h>
|
||||
#include <common/wallet_tx.h>
|
||||
#include <common/wire_error.h>
|
||||
#include <connectd/connectd_wiregen.h>
|
||||
#include <errno.h>
|
||||
|
||||
@@ -521,7 +521,7 @@ static const struct plugin_command commands[] = {
|
||||
json_txsend
|
||||
},
|
||||
{
|
||||
"newwithdraw",
|
||||
"withdraw",
|
||||
"bitcoin",
|
||||
"Send funds to {destination} address",
|
||||
"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')
|
||||
with pytest.raises(RpcError):
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
# 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')
|
||||
|
||||
bitcoind.generate_block(1)
|
||||
|
||||
@@ -12,7 +12,6 @@ from utils import (
|
||||
import os
|
||||
import pytest
|
||||
import subprocess
|
||||
import time
|
||||
import unittest
|
||||
|
||||
|
||||
@@ -42,7 +41,7 @@ def test_withdraw(node_factory, bitcoind):
|
||||
l1.rpc.withdraw(waddr, 'not an amount')
|
||||
with pytest.raises(RpcError):
|
||||
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)
|
||||
|
||||
out = l1.rpc.withdraw(waddr, 2 * amount)
|
||||
@@ -67,15 +66,23 @@ def test_withdraw(node_factory, bitcoind):
|
||||
# lightningd uses P2SH-P2WPKH
|
||||
waddr = l2.rpc.newaddr('bech32')['bech32']
|
||||
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)
|
||||
sync_blockheight(l1.bitcoin, [l1, l2])
|
||||
|
||||
# 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;')
|
||||
assert only_one(outputs)['value'] == 2 * amount
|
||||
|
||||
# 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=1')[0]['c'] == 0
|
||||
|
||||
# Simple test for withdrawal to P2WPKH
|
||||
# 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(waddr, 2 * amount)
|
||||
bitcoind.generate_block(1)
|
||||
sync_blockheight(l1.bitcoin, [l1])
|
||||
# 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
|
||||
|
||||
@@ -102,6 +110,7 @@ def test_withdraw(node_factory, bitcoind):
|
||||
l1.rpc.withdraw('tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qxxxxxx', 2 * amount)
|
||||
l1.rpc.withdraw(waddr, 2 * amount)
|
||||
bitcoind.generate_block(1)
|
||||
sync_blockheight(l1.bitcoin, [l1])
|
||||
# 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
|
||||
|
||||
@@ -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
|
||||
|
||||
# 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')
|
||||
|
||||
# Add some funds to withdraw
|
||||
@@ -225,7 +234,7 @@ def test_addfunds_from_block(node_factory, bitcoind):
|
||||
addr = l1.rpc.newaddr("bech32")['bech32']
|
||||
l1.rpc.withdraw(addr, "all")
|
||||
bitcoind.generate_block(1)
|
||||
time.sleep(1)
|
||||
sync_blockheight(bitcoind, [l1])
|
||||
|
||||
# The address we detect must match what was paid to.
|
||||
output = only_one(l1.rpc.listfunds()['outputs'])
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include <common/json_helpers.h>
|
||||
#include <common/jsonrpc_errors.h>
|
||||
#include <common/key_derive.h>
|
||||
#include <common/wallet_tx.h>
|
||||
#include <lightningd/jsonrpc.h>
|
||||
#include <lightningd/lightningd.h>
|
||||
#include <wallet/wallet.h>
|
||||
@@ -314,6 +313,21 @@ static struct command_result *finish_psbt(struct command *cmd,
|
||||
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,
|
||||
const char *buffer,
|
||||
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 pubkey pk;
|
||||
struct node_id id;
|
||||
struct amount_sat fee_estimate, change_satoshis;
|
||||
struct utxo *one_utxo;
|
||||
const struct utxo **utxos;
|
||||
CHECK(w);
|
||||
|
||||
@@ -941,19 +941,23 @@ static bool test_wallet_outputs(struct lightningd *ld, const tal_t *ctx)
|
||||
"wallet_add_utxo with close_info");
|
||||
|
||||
/* Now select them */
|
||||
utxos = wallet_select_coins(w, w, true, AMOUNT_SAT(2), 0, 21,
|
||||
utxos = tal_arr(w, const struct utxo *, 0);
|
||||
while ((one_utxo = wallet_find_utxo(w, w, 0, NULL, 253,
|
||||
0 /* no confirmations required */,
|
||||
&fee_estimate, &change_satoshis);
|
||||
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];
|
||||
|
||||
CHECK(u.close_info->channel_id == 42 &&
|
||||
pubkey_eq(u.close_info->commitment_point, &pk) &&
|
||||
node_id_eq(&u.close_info->peer_id, &id) &&
|
||||
u.close_info->option_anchor_outputs == false);
|
||||
/* Now un-reserve them for the tests below */
|
||||
tal_free(utxos);
|
||||
|
||||
|
||||
/* Attempt to reserve the utxo */
|
||||
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");
|
||||
|
||||
/* Now select it */
|
||||
utxos = wallet_select_coins(w, w, true, AMOUNT_SAT(5), 0, 21,
|
||||
utxos = tal_arr(w, const struct utxo *, 0);
|
||||
while ((one_utxo = wallet_find_utxo(w, w, 0, NULL, 253,
|
||||
0 /* no confirmations required */,
|
||||
&fee_estimate, &change_satoshis);
|
||||
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];
|
||||
|
||||
CHECK(u.close_info->channel_id == 42 &&
|
||||
u.close_info->commitment_point == NULL &&
|
||||
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->keyscan_gap = 50;
|
||||
list_head_init(&wallet->unstored_payments);
|
||||
list_head_init(&wallet->unreleased_txs);
|
||||
wallet->db = db_setup(wallet, ld, wallet->bip32_base);
|
||||
|
||||
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)
|
||||
{
|
||||
struct utxo *utxo = tal(ctx, struct utxo);
|
||||
u32 *blockheight, *spendheight, *reserved_til;
|
||||
u32 *blockheight, *spendheight;
|
||||
db_column_txid(stmt, 0, &utxo->txid);
|
||||
utxo->outnum = db_column_int(stmt, 1);
|
||||
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->spendheight = NULL;
|
||||
utxo->reserved_til = NULL;
|
||||
|
||||
if (!db_column_is_null(stmt, 10)) {
|
||||
blockheight = tal(utxo, u32);
|
||||
@@ -206,11 +204,9 @@ static struct utxo *wallet_stmt2output(const tal_t *ctx, struct db_stmt *stmt)
|
||||
utxo->spendheight = spendheight;
|
||||
}
|
||||
|
||||
if (!db_column_is_null(stmt, 13)) {
|
||||
reserved_til = tal(utxo, u32);
|
||||
*reserved_til = db_column_int(stmt, 13);
|
||||
utxo->reserved_til = reserved_til;
|
||||
}
|
||||
/* This column can be null if 0.9.1 db or below. */
|
||||
utxo->reserved_til = tal(utxo, u32);
|
||||
*utxo->reserved_til = db_column_int_or_default(stmt, 13, 0);
|
||||
|
||||
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)
|
||||
assert(utxo->reserved_til);
|
||||
else
|
||||
assert(!utxo->reserved_til);
|
||||
|
||||
switch (utxo->status) {
|
||||
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)
|
||||
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",
|
||||
@@ -645,175 +638,6 @@ bool wallet_add_onchaind_utxo(struct wallet *w,
|
||||
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,
|
||||
u32 *index, bool *output_is_p2sh)
|
||||
{
|
||||
@@ -3933,85 +3757,6 @@ const struct forwarding *wallet_forwarded_payments_get(struct wallet *w,
|
||||
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 db_stmt *stmt;
|
||||
|
||||
@@ -48,26 +48,10 @@ struct wallet {
|
||||
* the blockchain. This is currently all P2WSH outputs */
|
||||
struct outpointfilter *utxoset_outpoints;
|
||||
|
||||
/* Unreleased txs, waiting for txdiscard/txsend */
|
||||
struct list_head unreleased_txs;
|
||||
|
||||
/* How many keys should we look ahead at most? */
|
||||
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
|
||||
* whether we really want to have reservations reflected in the
|
||||
* 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,
|
||||
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
|
||||
*
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
#include <common/status.h>
|
||||
#include <common/utils.h>
|
||||
#include <common/utxo.h>
|
||||
#include <common/wallet_tx.h>
|
||||
#include <common/withdraw_tx.h>
|
||||
#include <errno.h>
|
||||
#include <hsmd/hsmd_wiregen.h>
|
||||
#include <inttypes.h>
|
||||
@@ -35,283 +33,6 @@
|
||||
#include <wally_bip32.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. */
|
||||
static char *
|
||||
encode_pubkey_to_addr(const tal_t *ctx,
|
||||
|
||||
Reference in New Issue
Block a user