rgb-cln/wallet/reservation.c

948 lines
28 KiB
C

/* Dealing with reserving UTXOs */
#include "config.h"
#include <bitcoin/psbt.h>
#include <bitcoin/script.h>
#include <ccan/cast/cast.h>
#include <ccan/mem/mem.h>
#include <common/configdir.h>
#include <common/json_command.h>
#include <common/json_param.h>
#include <common/key_derive.h>
#include <common/type_to_string.h>
#include <lightningd/chaintopology.h>
#include <lightningd/channel.h>
#include <lightningd/hsm_control.h>
#include <lightningd/jsonrpc.h>
#include <lightningd/lightningd.h>
#include <wallet/txfilter.h>
#include <wally_psbt.h>
/* 12 hours is usually enough reservation time */
#define RESERVATION_DEFAULT (6 * 12)
static bool was_reserved(enum output_status oldstatus,
u32 reserved_til,
u32 current_height)
{
if (oldstatus != OUTPUT_STATE_RESERVED)
return false;
return reserved_til > current_height;
}
static void json_add_reservestatus(struct json_stream *response,
const struct utxo *utxo,
enum output_status oldstatus,
u32 old_res,
u32 current_height)
{
json_object_start(response, NULL);
json_add_txid(response, "txid", &utxo->outpoint.txid);
json_add_u32(response, "vout", utxo->outpoint.n);
json_add_bool(response, "was_reserved",
was_reserved(oldstatus, old_res, current_height));
json_add_bool(response, "reserved",
utxo_is_reserved(utxo, current_height));
if (utxo_is_reserved(utxo, current_height))
json_add_u32(response, "reserved_to_block",
utxo->reserved_til);
json_object_end(response);
}
/* Reserve these UTXOs and print to JSON */
static void reserve_and_report(struct json_stream *response,
struct wallet *wallet,
u32 current_height,
u32 reserve,
struct utxo **utxos)
{
json_array_start(response, "reservations");
for (size_t i = 0; i < tal_count(utxos); i++) {
enum output_status oldstatus;
u32 old_res;
oldstatus = utxos[i]->status;
old_res = utxos[i]->reserved_til;
if (!wallet_reserve_utxo(wallet,
utxos[i],
current_height,
reserve)) {
fatal("Unable to reserve %s!",
type_to_string(tmpctx,
struct bitcoin_outpoint,
&utxos[i]->outpoint));
}
json_add_reservestatus(response, utxos[i], oldstatus, old_res,
current_height);
}
json_array_end(response);
}
static struct command_result *json_reserveinputs(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
{
struct json_stream *response;
struct wally_psbt *psbt;
struct utxo **utxos = tal_arr(cmd, struct utxo *, 0);
bool *exclusive;
u32 *reserve, current_height;
if (!param_check(cmd, buffer, params,
p_req("psbt", param_psbt, &psbt),
p_opt_def("exclusive", param_bool, &exclusive, true),
p_opt_def("reserve", param_number, &reserve,
RESERVATION_DEFAULT),
NULL))
return command_param_failed();
/* We only deal with V2 internally */
if (!psbt_set_version(psbt, 2)) {
return command_fail(cmd, LIGHTNINGD,
"Failed to set version for PSBT: %s",
type_to_string(tmpctx,
struct wally_psbt,
psbt));
}
current_height = get_block_height(cmd->ld->topology);
for (size_t i = 0; i < psbt->num_inputs; i++) {
struct bitcoin_outpoint outpoint;
struct utxo *utxo;
wally_psbt_input_get_outpoint(&psbt->inputs[i], &outpoint);
utxo = wallet_utxo_get(cmd, cmd->ld->wallet, &outpoint);
if (!utxo)
continue;
if (*exclusive && utxo_is_reserved(utxo, current_height)) {
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"%s already reserved",
type_to_string(tmpctx,
struct bitcoin_outpoint,
&utxo->outpoint));
}
if (utxo->status == OUTPUT_STATE_SPENT)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"%s already spent",
type_to_string(tmpctx,
struct bitcoin_outpoint,
&utxo->outpoint));
tal_arr_expand(&utxos, utxo);
}
if (command_check_only(cmd))
return command_check_done(cmd);
response = json_stream_success(cmd);
reserve_and_report(response, cmd->ld->wallet, current_height, *reserve, utxos);
return command_success(cmd, response);
}
static const struct json_command reserveinputs_command = {
"reserveinputs",
"bitcoin",
json_reserveinputs,
"Reserve utxos (or increase their reservation)",
false
};
AUTODATA(json_command, &reserveinputs_command);
static struct command_result *json_unreserveinputs(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
{
struct json_stream *response;
struct wally_psbt *psbt;
u32 *reserve;
if (!param_check(cmd, buffer, params,
p_req("psbt", param_psbt, &psbt),
p_opt_def("reserve", param_number, &reserve,
RESERVATION_DEFAULT),
NULL))
return command_param_failed();
/* We only deal with V2 internally */
if (!psbt_set_version(psbt, 2)) {
log_broken(cmd->ld->log,
"Unable to set version for PSBT: %s",
type_to_string(tmpctx, struct wally_psbt,
psbt));
}
/* We should also add the utxo info for these inputs!
* (absolutely required for using this psbt in a dual-funded
* round) */
for (size_t i = 0; i < psbt->num_inputs; i++) {
struct bitcoin_tx *utxo_tx;
struct bitcoin_txid txid;
wally_psbt_input_get_txid(&psbt->inputs[i], &txid);
utxo_tx = wallet_transaction_get(psbt, cmd->ld->wallet,
&txid);
if (utxo_tx) {
tal_wally_start();
wally_psbt_input_set_utxo(&psbt->inputs[i],
utxo_tx->wtx);
tal_wally_end(psbt);
} else
log_broken(cmd->ld->log,
"No transaction found for UTXO %s",
type_to_string(tmpctx, struct bitcoin_txid,
&txid));
}
if (command_check_only(cmd))
return command_check_done(cmd);
response = json_stream_success(cmd);
json_array_start(response, "reservations");
for (size_t i = 0; i < psbt->num_inputs; i++) {
struct bitcoin_outpoint outpoint;
struct utxo *utxo;
enum output_status oldstatus;
u32 old_res;
wally_psbt_input_get_outpoint(&psbt->inputs[i], &outpoint);
utxo = wallet_utxo_get(cmd, cmd->ld->wallet, &outpoint);
if (!utxo || utxo->status != OUTPUT_STATE_RESERVED)
continue;
oldstatus = utxo->status;
old_res = utxo->reserved_til;
wallet_unreserve_utxo(cmd->ld->wallet,
utxo,
get_block_height(cmd->ld->topology),
*reserve);
json_add_reservestatus(response, utxo, oldstatus, old_res,
get_block_height(cmd->ld->topology));
}
json_array_end(response);
return command_success(cmd, response);
}
static const struct json_command unreserveinputs_command = {
"unreserveinputs",
"bitcoin",
json_unreserveinputs,
"Unreserve utxos (or at least, reduce their reservation)",
false
};
AUTODATA(json_command, &unreserveinputs_command);
/**
* inputs_sufficient - are we there yet?
* @input: total input amount
* @amount: required output amount
* @feerate_per_kw: feerate we have to pay
* @weight: weight of transaction so far.
* @diff: (output) set to amount over or under requirements.
*
* Returns true if inputs >= fees + amount, otherwise false. diff is
* the amount over (if returns true) or under (if returns false)
*/
static bool inputs_sufficient(struct amount_sat input,
struct amount_sat amount,
u32 feerate_per_kw,
size_t weight,
struct amount_sat *diff)
{
struct amount_sat fee;
fee = amount_tx_fee(feerate_per_kw, weight);
/* If we can't add fees, amount is huge (e.g. "all") */
if (!amount_sat_add(&amount, amount, fee))
return false;
/* One of these must work! */
if (amount_sat_sub(diff, input, amount))
return true;
if (!amount_sat_sub(diff, amount, input))
abort();
return false;
}
struct wally_psbt *psbt_using_utxos(const tal_t *ctx,
struct wallet *wallet,
struct utxo **utxos,
u32 nlocktime,
u32 nsequence,
struct wally_psbt *base)
{
struct pubkey key;
u8 *scriptSig, *scriptPubkey, *redeemscript;
struct wally_psbt *psbt;
if (base)
psbt = base;
else
psbt = create_psbt(ctx, tal_count(utxos), 0, nlocktime);
for (size_t i = 0; i < tal_count(utxos); i++) {
u32 this_nsequence;
struct bitcoin_tx *tx;
if (utxos[i]->is_p2sh) {
bip32_pubkey(wallet->ld, &key, utxos[i]->keyindex);
scriptSig = bitcoin_scriptsig_p2sh_p2wpkh(tmpctx, &key);
redeemscript = bitcoin_redeem_p2sh_p2wpkh(tmpctx, &key);
scriptPubkey = scriptpubkey_p2sh(tmpctx, redeemscript);
/* Make sure we've got the right info! */
if (utxos[i]->scriptPubkey)
assert(memeq(utxos[i]->scriptPubkey,
tal_bytelen(utxos[i]->scriptPubkey),
scriptPubkey, tal_bytelen(scriptPubkey)));
} else {
scriptSig = NULL;
redeemscript = NULL;
scriptPubkey = utxos[i]->scriptPubkey;
}
/* BOLT #3:
* #### `to_remote` Output
* ...
* The output is spent by an input with `nSequence` field
* set to `1` and witness:
*/
if (utxos[i]->close_info
&& utxos[i]->close_info->option_anchors)
this_nsequence = utxos[i]->close_info->csv;
else
this_nsequence = nsequence;
psbt_append_input(psbt, &utxos[i]->outpoint,
this_nsequence, scriptSig,
NULL, redeemscript);
psbt_input_set_wit_utxo(psbt, psbt->num_inputs-1,
scriptPubkey, utxos[i]->amount);
if (is_elements(chainparams)) {
/* FIXME: persist asset tags */
amount_sat_to_asset(&utxos[i]->amount,
chainparams->fee_asset_tag);
/* FIXME: persist nonces */
}
/* FIXME: as of 17 sept 2020, elementsd is *at most* at par
* with v0.18.0 of bitcoind, which doesn't support setting
* non-witness and witness utxo data for an input; remove this
* check once elementsd can be updated */
if (!is_elements(chainparams)) {
/* If we have the transaction for this utxo,
* add it to the PSBT as the non-witness-utxo field.
* Dual-funded channels and some hardware wallets
* require this */
tx = wallet_transaction_get(ctx, wallet,
&utxos[i]->outpoint.txid);
if (tx)
psbt_input_set_utxo(psbt, psbt->num_inputs-1, tx->wtx);
}
}
return psbt;
}
static struct command_result *finish_psbt(struct command *cmd,
struct utxo **utxos,
u32 feerate_per_kw,
size_t weight,
struct amount_sat excess,
u32 reserve,
u32 *locktime,
struct amount_sat change)
{
struct json_stream *response;
struct wally_psbt *psbt;
ssize_t change_outnum;
u32 current_height = get_block_height(cmd->ld->topology);
if (!locktime) {
locktime = tal(cmd, u32);
*locktime = default_locktime(cmd->ld->topology);
}
psbt = psbt_using_utxos(cmd, cmd->ld->wallet, utxos,
*locktime, BITCOIN_TX_RBF_SEQUENCE,
NULL);
assert(psbt->version == 2);
/* Should we add a change output? (Iff it can pay for itself!) */
change = change_amount(change, feerate_per_kw, weight);
if (amount_sat_greater(change, AMOUNT_SAT(0))) {
struct pubkey pubkey;
s64 keyidx;
u8 *b32script;
/* Get a change adddress */
keyidx = wallet_get_newindex(cmd->ld);
if (keyidx < 0)
return command_fail(cmd, LIGHTNINGD,
"Failed to generate change address."
" Keys exhausted.");
if (chainparams->is_elements) {
bip32_pubkey(cmd->ld, &pubkey, keyidx);
b32script = scriptpubkey_p2wpkh(tmpctx, &pubkey);
} else {
b32script = p2tr_for_keyidx(tmpctx, cmd->ld, keyidx);
}
if (!b32script) {
return command_fail(cmd, LIGHTNINGD,
"Failed to generate change address."
" Keys generation failure");
}
txfilter_add_scriptpubkey(cmd->ld->owned_txfilter, b32script);
change_outnum = psbt->num_outputs;
psbt_append_output(psbt, b32script, change);
/* Add additional weight of output */
weight += bitcoin_tx_output_weight(
chainparams->is_elements ? BITCOIN_SCRIPTPUBKEY_P2WPKH_LEN : BITCOIN_SCRIPTPUBKEY_P2TR_LEN);
} else {
change_outnum = -1;
}
/* Add a fee output if this is elements */
if (is_elements(chainparams)) {
struct amount_sat est_fee =
amount_tx_fee(feerate_per_kw, weight);
psbt_append_output(psbt, NULL, est_fee);
/* Add additional weight of fee output */
weight += bitcoin_tx_output_weight(0);
} else {
/* PSETv0 doesn't exist */
if (!psbt_set_version(psbt, 0)) {
return command_fail(cmd, LIGHTNINGD,
"Failed to set PSBT version number back to 0.");
}
}
response = json_stream_success(cmd);
json_add_psbt(response, "psbt", psbt);
json_add_num(response, "feerate_per_kw", feerate_per_kw);
json_add_num(response, "estimated_final_weight", weight);
json_add_amount_sat_msat(response, "excess_msat", excess);
if (change_outnum != -1)
json_add_num(response, "change_outnum", change_outnum);
if (reserve)
reserve_and_report(response, cmd->ld->wallet, current_height,
reserve, utxos);
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;
}
/* Returns false if it needed to create change, but couldn't afford. */
static bool change_for_emergency(struct lightningd *ld,
bool have_anchor_channel,
struct utxo **utxos,
u32 feerate_per_kw,
u32 weight,
struct amount_sat *excess,
struct amount_sat *change)
{
struct amount_sat fee;
/* Only needed for anchor channels */
if (!have_anchor_channel)
return true;
/* Fine if rest of wallet has funds. */
if (wallet_has_funds(ld->wallet,
cast_const2(const struct utxo **, utxos),
get_block_height(ld->topology),
ld->emergency_sat))
return true;
/* If we can afford with existing change output, great (or
* ld->emergency_sat is 0) */
if (amount_sat_greater_eq(change_amount(*change,
feerate_per_kw, weight),
ld->emergency_sat))
return true;
/* Try splitting excess to add to change. */
fee = change_fee(feerate_per_kw, weight);
if (!amount_sat_sub(excess, *excess, fee)
|| !amount_sat_sub(excess, *excess, ld->emergency_sat))
return false;
if (!amount_sat_add(change, *change, fee)
|| !amount_sat_add(change, *change, ld->emergency_sat))
abort();
/* We *will* get a change output now! */
assert(amount_sat_eq(change_amount(*change, feerate_per_kw,
weight),
ld->emergency_sat));
return true;
}
static struct command_result *json_fundpsbt(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
{
struct utxo **utxos;
const struct utxo **excluded;
u32 *feerate_per_kw;
u32 *minconf, *weight, *min_witness_weight;
struct amount_sat *amount, input, diff, change;
bool all, *excess_as_change, *nonwrapped, *keep_emergency_funds;
u32 *locktime, *reserve, maxheight;
u32 current_height;
if (!param_check(cmd, buffer, params,
p_req("satoshi", param_sat_or_all, &amount),
p_req("feerate", param_feerate, &feerate_per_kw),
p_req("startweight", param_number, &weight),
p_opt_def("minconf", param_number, &minconf, 1),
p_opt_def("reserve", param_number, &reserve,
RESERVATION_DEFAULT),
p_opt("locktime", param_number, &locktime),
p_opt_def("min_witness_weight", param_number,
&min_witness_weight, 0),
p_opt_def("excess_as_change", param_bool,
&excess_as_change, false),
p_opt_def("nonwrapped", param_bool,
&nonwrapped, false),
p_opt_def("opening_anchor_channel", param_bool,
&keep_emergency_funds, false),
NULL))
return command_param_failed();
/* If we have anchor channels, we definitely need to keep
* emergency funds. */
if (have_anchor_channel(cmd->ld))
*keep_emergency_funds = true;
all = amount_sat_eq(*amount, AMOUNT_SAT(-1ULL));
maxheight = minconf_to_maxheight(*minconf, cmd->ld);
current_height = get_block_height(cmd->ld->topology);
/* We keep adding until we meet their output requirements. */
utxos = tal_arr(cmd, struct utxo *, 0);
/* Either uneconomical at this feerate, or already included. */
excluded = tal_arr(cmd, const struct utxo *, 0);
input = AMOUNT_SAT(0);
while (!inputs_sufficient(input, *amount, *feerate_per_kw, *weight,
&diff)) {
struct utxo *utxo;
struct amount_sat fee;
u32 utxo_weight;
utxo = wallet_find_utxo(utxos, cmd->ld->wallet,
current_height,
&diff,
*feerate_per_kw,
maxheight,
*nonwrapped,
excluded);
if (utxo) {
tal_arr_expand(&excluded, utxo);
utxo_weight = utxo_spend_weight(utxo,
*min_witness_weight);
fee = amount_tx_fee(*feerate_per_kw, utxo_weight);
/* Uneconomic to add this utxo, skip it */
if (!all && amount_sat_greater_eq(fee, utxo->amount))
continue;
tal_arr_expand(&utxos, utxo);
/* It supplies more input. */
if (!amount_sat_add(&input, input, utxo->amount))
return command_fail(cmd, LIGHTNINGD,
"impossible UTXO value");
/* But also adds weight */
*weight += utxo_weight;
continue;
}
/* If they said "all", we expect to run out of utxos. */
if (all && tal_count(utxos))
break;
/* 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(cmd->ld->topology))
return command_fail(cmd,
FUNDING_STILL_SYNCING_BITCOIN,
"Cannot afford: still syncing with bitcoin network...");
return command_fail(cmd, FUND_CANNOT_AFFORD,
"Could not afford %s using all %zu available UTXOs: %s short",
all ? "all"
: type_to_string(tmpctx,
struct amount_sat,
amount),
tal_count(utxos),
all ? "all"
: type_to_string(tmpctx,
struct amount_sat,
&diff));
}
tal_free(excluded);
if (all) {
/* We need to afford one non-dust output, at least. */
if (!inputs_sufficient(input, AMOUNT_SAT(0),
*feerate_per_kw, *weight,
&diff)
|| amount_sat_less(diff, chainparams->dust_limit)) {
if (!topology_synced(cmd->ld->topology))
return command_fail(cmd,
FUNDING_STILL_SYNCING_BITCOIN,
"Cannot afford: still syncing with bitcoin network...");
return command_fail(cmd, FUND_CANNOT_AFFORD,
"All %zu inputs could not afford"
" fees",
tal_count(utxos));
}
*excess_as_change = false;
}
/* Turn excess into change. */
if (*excess_as_change) {
change = diff;
diff = AMOUNT_SAT(0);
} else {
change = AMOUNT_SAT(0);
}
/* If needed, add change output for emergency_sat */
if (!change_for_emergency(cmd->ld,
*keep_emergency_funds,
utxos, *feerate_per_kw, *weight,
&diff, &change)) {
return command_fail(cmd, FUND_CANNOT_AFFORD_WITH_EMERGENCY,
"We would not have enough left for min-emergency-msat %s",
fmt_amount_sat(tmpctx,
cmd->ld->emergency_sat));
}
if (command_check_only(cmd))
return command_check_done(cmd);
return finish_psbt(cmd, utxos, *feerate_per_kw, *weight, diff, *reserve,
locktime, change);
}
static const struct json_command fundpsbt_command = {
"fundpsbt",
"bitcoin",
json_fundpsbt,
"Create PSBT using enough utxos to allow an output of {satoshi} at {feerate}",
false
};
AUTODATA(json_command, &fundpsbt_command);
static struct command_result *json_addpsbtoutput(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
{
struct json_stream *response;
struct amount_sat *amount;
struct wally_psbt *psbt;
u32 *locktime;
ssize_t outnum;
u32 weight;
struct pubkey pubkey;
s64 keyidx;
const u8 *b32script;
if (!param_check(cmd, buffer, params,
p_req("satoshi", param_sat, &amount),
p_opt("initialpsbt", param_psbt, &psbt),
p_opt("locktime", param_number, &locktime),
p_opt("destination", param_bitcoin_address,
&b32script),
NULL))
return command_param_failed();
if (!psbt) {
if (!locktime) {
locktime = tal(cmd, u32);
*locktime = default_locktime(cmd->ld->topology);
}
psbt = create_psbt(cmd, 0, 0, *locktime);
} else if (locktime) {
return command_fail(cmd, FUNDING_PSBT_INVALID,
"Can't set locktime of an existing {initialpsbt}");
}
if (!validate_psbt(psbt))
return command_fail(cmd,
FUNDING_PSBT_INVALID,
"PSBT failed to validate.");
if (amount_sat_less(*amount, chainparams->dust_limit))
return command_fail(cmd, FUND_OUTPUT_IS_DUST,
"Receive amount is below dust limit (%s)",
type_to_string(tmpctx,
struct amount_sat,
&chainparams->dust_limit));
if (command_check_only(cmd))
return command_check_done(cmd);
/* Get a change adddress */
if (!b32script) {
keyidx = wallet_get_newindex(cmd->ld);
if (keyidx < 0)
return command_fail(cmd, LIGHTNINGD,
"Failed to generate change address."
" Keys exhausted.");
if (chainparams->is_elements) {
bip32_pubkey(cmd->ld, &pubkey, keyidx);
b32script = scriptpubkey_p2wpkh(tmpctx, &pubkey);
} else {
b32script = p2tr_for_keyidx(tmpctx, cmd->ld, keyidx);
}
if (!b32script) {
return command_fail(cmd, LIGHTNINGD,
"Failed to generate change address."
" Keys generation failure");
}
txfilter_add_scriptpubkey(cmd->ld->owned_txfilter, b32script);
}
outnum = psbt->num_outputs;
psbt_append_output(psbt, b32script, *amount);
/* Add additional weight of output */
weight = bitcoin_tx_output_weight(
chainparams->is_elements ? BITCOIN_SCRIPTPUBKEY_P2WPKH_LEN : BITCOIN_SCRIPTPUBKEY_P2TR_LEN);
response = json_stream_success(cmd);
json_add_psbt(response, "psbt", psbt);
json_add_num(response, "estimated_added_weight", weight);
json_add_num(response, "outnum", outnum);
return command_success(cmd, response);
}
static const struct json_command addpsbtoutput_command = {
"addpsbtoutput",
"bitcoin",
json_addpsbtoutput,
"Create a PSBT (or modify existing {initialpsbt}) with an output receiving {satoshi} amount.",
false
};
AUTODATA(json_command, &addpsbtoutput_command);
static struct command_result *param_txout(struct command *cmd,
const char *name,
const char *buffer,
const jsmntok_t *tok,
struct utxo ***utxos)
{
size_t i;
const jsmntok_t *curr;
*utxos = tal_arr(cmd, struct utxo *, tok->size);
json_for_each_arr(i, curr, tok) {
struct utxo *utxo;
jsmntok_t txid_tok, outnum_tok;
struct bitcoin_outpoint outpoint;
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));
if (!json_to_txid(buffer, &txid_tok, &outpoint.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, &outnum_tok, &outpoint.n)) {
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Could not get a vout out of \"%s\"",
json_strdup(tmpctx, buffer, &outnum_tok));
}
utxo = wallet_utxo_get(*utxos, cmd->ld->wallet, &outpoint);
if (!utxo) {
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Unknown UTXO %s",
type_to_string(tmpctx,
struct bitcoin_outpoint,
&outpoint));
}
if (utxo->status == OUTPUT_STATE_SPENT) {
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Already spent UTXO %s",
type_to_string(tmpctx,
struct bitcoin_outpoint,
&outpoint));
}
(*utxos)[i] = utxo;
}
if (i == 0)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Please specify an array of 'txid:output_index',"
" not \"%.*s\"",
tok->end - tok->start,
buffer + tok->start);
return NULL;
}
static struct command_result *json_utxopsbt(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
{
struct utxo **utxos;
u32 *feerate_per_kw, *weight, *min_witness_weight;
bool all, *reserved_ok, *excess_as_change, *keep_emergency_funds;
struct amount_sat *amount, input, excess, change;
u32 current_height, *locktime, *reserve;
if (!param_check(cmd, buffer, params,
p_req("satoshi", param_sat_or_all, &amount),
p_req("feerate", param_feerate, &feerate_per_kw),
p_req("startweight", param_number, &weight),
p_req("utxos", param_txout, &utxos),
p_opt_def("reserve", param_number, &reserve,
RESERVATION_DEFAULT),
p_opt_def("reservedok", param_bool, &reserved_ok, false),
p_opt("locktime", param_number, &locktime),
p_opt_def("min_witness_weight", param_number,
&min_witness_weight, 0),
p_opt_def("excess_as_change", param_bool,
&excess_as_change, false),
p_opt_def("opening_anchor_channel", param_bool,
&keep_emergency_funds, false),
NULL))
return command_param_failed();
/* If we have anchor channels, we definitely need to keep
* emergency funds. */
if (have_anchor_channel(cmd->ld))
*keep_emergency_funds = true;
all = amount_sat_eq(*amount, AMOUNT_SAT(-1ULL));
input = AMOUNT_SAT(0);
current_height = get_block_height(cmd->ld->topology);
for (size_t i = 0; i < tal_count(utxos); i++) {
const struct utxo *utxo = utxos[i];
if (!*reserved_ok && utxo_is_reserved(utxo, current_height))
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"UTXO %s already reserved",
type_to_string(tmpctx,
struct bitcoin_outpoint,
&utxo->outpoint));
if (utxo_is_csv_locked(utxo, current_height))
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"UTXO %s is csv locked (%u)",
type_to_string(tmpctx,
struct bitcoin_outpoint,
&utxo->outpoint),
utxo->close_info->csv);
/* It supplies more input. */
if (!amount_sat_add(&input, input, utxo->amount))
return command_fail(cmd, LIGHTNINGD,
"impossible UTXO value");
/* But also adds weight */
*weight += utxo_spend_weight(utxo, *min_witness_weight);
}
if (all) {
/* We need to afford one non-dust output, at least. */
if (!inputs_sufficient(input, AMOUNT_SAT(0),
*feerate_per_kw, *weight,
&excess)
|| amount_sat_less(excess, chainparams->dust_limit)) {
return command_fail(cmd, FUND_CANNOT_AFFORD,
"Could not afford anything using UTXOs totalling %s with weight %u at feerate %u",
type_to_string(tmpctx,
struct amount_sat,
&input),
*weight, *feerate_per_kw);
}
*excess_as_change = false;
} else {
if (!inputs_sufficient(input, *amount,
*feerate_per_kw, *weight, &excess)) {
return command_fail(cmd, FUND_CANNOT_AFFORD,
"Could not afford %s using UTXOs totalling %s with weight %u at feerate %u",
type_to_string(tmpctx,
struct amount_sat,
amount),
type_to_string(tmpctx,
struct amount_sat,
&input),
*weight, *feerate_per_kw);
}
}
if (*excess_as_change) {
change = excess;
excess = AMOUNT_SAT(0);
} else {
change = AMOUNT_SAT(0);
}
/* If needed, add change output for emergency_sat */
if (!change_for_emergency(cmd->ld,
*keep_emergency_funds,
utxos, *feerate_per_kw, *weight,
&excess, &change)) {
return command_fail(cmd, FUND_CANNOT_AFFORD_WITH_EMERGENCY,
"We would not have enough left for min-emergency-msat %s",
fmt_amount_sat(tmpctx,
cmd->ld->emergency_sat));
}
if (command_check_only(cmd))
return command_check_done(cmd);
return finish_psbt(cmd, utxos, *feerate_per_kw, *weight, excess,
*reserve, locktime, change);
}
static const struct json_command utxopsbt_command = {
"utxopsbt",
"bitcoin",
json_utxopsbt,
"Create PSBT using these utxos",
false
};
AUTODATA(json_command, &utxopsbt_command);