mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-03-01 09:40:19 +01:00
fundpsbt: new JSON API to gather UTXOs.
Technically, they could do this themselves, but it's much nicer to have one place to do it (and it makes sure we get the required information into the PSBT, which is actually not entirely accessible through listfunds, as that doesn't want to consult with the HSM for close outputs). Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> Changelog-Added: JSON RPC: new low-level coin selection `fundpsbt` routine.
This commit is contained in:
parent
4ee527a59c
commit
bd19ec2292
3 changed files with 139 additions and 1 deletions
|
@ -12,6 +12,10 @@
|
|||
#include <wally_transaction.h>
|
||||
|
||||
#define BITCOIN_TX_DEFAULT_SEQUENCE 0xFFFFFFFF
|
||||
|
||||
/* BIP 125: Any nsequence < 0xFFFFFFFE is replacable.
|
||||
* And bitcoind uses this value. */
|
||||
#define BITCOIN_TX_RBF_SEQUENCE 0xFFFFFFFD
|
||||
struct wally_psbt;
|
||||
|
||||
struct bitcoin_txid {
|
||||
|
|
|
@ -76,7 +76,6 @@ struct bitcoin_tx *tx_spending_utxos(const tal_t *ctx,
|
|||
struct pubkey key;
|
||||
u8 *scriptSig, *scriptPubkey, *redeemscript;
|
||||
|
||||
assert(num_output);
|
||||
size_t outcount = add_change_output ? 1 + num_output : num_output;
|
||||
struct bitcoin_tx *tx = bitcoin_tx(ctx, chainparams, tal_count(utxos),
|
||||
outcount, nlocktime);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include <common/json_command.h>
|
||||
#include <common/json_helpers.h>
|
||||
#include <common/jsonrpc_errors.h>
|
||||
#include <common/wallet_tx.h>
|
||||
#include <lightningd/jsonrpc.h>
|
||||
#include <lightningd/lightningd.h>
|
||||
#include <wallet/wallet.h>
|
||||
|
@ -164,3 +165,137 @@ static const struct json_command unreserveinputs_command = {
|
|||
false
|
||||
};
|
||||
AUTODATA(json_command, &unreserveinputs_command);
|
||||
|
||||
|
||||
static struct command_result *json_fundpsbt(struct command *cmd,
|
||||
const char *buffer,
|
||||
const jsmntok_t *obj UNNEEDED,
|
||||
const jsmntok_t *params)
|
||||
{
|
||||
struct json_stream *response;
|
||||
const struct utxo **utxos;
|
||||
u32 *feerate_per_kw;
|
||||
u32 *minconf;
|
||||
struct amount_sat *amount, input, needed, excess, total_fee;
|
||||
bool all;
|
||||
u32 locktime, maxheight;
|
||||
struct bitcoin_tx *tx;
|
||||
|
||||
if (!param(cmd, buffer, params,
|
||||
p_req("satoshi", param_sat_or_all, &amount),
|
||||
p_req("feerate", param_feerate_val, &feerate_per_kw),
|
||||
p_opt_def("minconf", param_number, &minconf, 1),
|
||||
NULL))
|
||||
return command_param_failed();
|
||||
|
||||
all = amount_sat_eq(*amount, AMOUNT_SAT(-1ULL));
|
||||
maxheight = minconf_to_maxheight(*minconf, cmd->ld);
|
||||
|
||||
/* We keep adding until we meet their output requirements. */
|
||||
input = AMOUNT_SAT(0);
|
||||
utxos = tal_arr(cmd, const struct utxo *, 0);
|
||||
total_fee = AMOUNT_SAT(0);
|
||||
while (amount_sat_sub(&needed, *amount, input)
|
||||
&& !amount_sat_eq(needed, AMOUNT_SAT(0))) {
|
||||
struct utxo *utxo;
|
||||
|
||||
utxo = wallet_find_utxo(utxos, cmd->ld->wallet,
|
||||
cmd->ld->topology->tip->height,
|
||||
&needed,
|
||||
*feerate_per_kw,
|
||||
maxheight,
|
||||
utxos);
|
||||
if (utxo) {
|
||||
struct amount_sat fee;
|
||||
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 increase amount needed, to pay for new input */
|
||||
fee = amount_tx_fee(*feerate_per_kw,
|
||||
utxo_spend_weight(utxo));
|
||||
if (!amount_sat_add(amount, *amount, fee))
|
||||
/* Either they specified "all", or we
|
||||
* will fail anyway. */
|
||||
*amount = AMOUNT_SAT(-1ULL);
|
||||
if (!amount_sat_add(&total_fee, total_fee, fee))
|
||||
return command_fail(cmd, LIGHTNINGD,
|
||||
"impossible fee value");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If they said "all", we expect to run out of utxos. */
|
||||
if (all) {
|
||||
/* If we have none at all though, fail */
|
||||
if (!tal_count(utxos))
|
||||
return command_fail(cmd, FUND_CANNOT_AFFORD,
|
||||
"No available UTXOs");
|
||||
break;
|
||||
}
|
||||
|
||||
return command_fail(cmd, FUND_CANNOT_AFFORD,
|
||||
"Could not afford %s using all %zu available UTXOs: %s short",
|
||||
type_to_string(tmpctx,
|
||||
struct amount_sat,
|
||||
amount),
|
||||
tal_count(utxos),
|
||||
type_to_string(tmpctx,
|
||||
struct amount_sat,
|
||||
&needed));
|
||||
}
|
||||
|
||||
/* 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
|
||||
* 0xFFFFFFFD by default. Other wallets are likely to implement
|
||||
* this too).
|
||||
*/
|
||||
locktime = cmd->ld->topology->tip->height;
|
||||
|
||||
/* Eventually fuzz it too. */
|
||||
if (locktime > 100 && pseudorand(10) == 0)
|
||||
locktime -= pseudorand(100);
|
||||
|
||||
/* FIXME: tx_spending_utxos does more than we need, but there
|
||||
* are other users right now. */
|
||||
tx = tx_spending_utxos(cmd, chainparams, utxos,
|
||||
cmd->ld->wallet->bip32_base,
|
||||
false, 0, locktime,
|
||||
BITCOIN_TX_RBF_SEQUENCE);
|
||||
|
||||
if (all) {
|
||||
/* Count everything not going towards fees as excess. */
|
||||
if (!amount_sat_sub(&excess, input, total_fee))
|
||||
return command_fail(cmd, FUND_CANNOT_AFFORD,
|
||||
"All %zu inputs could not afford"
|
||||
" %s fees",
|
||||
tal_count(utxos),
|
||||
type_to_string(tmpctx,
|
||||
struct amount_sat,
|
||||
&total_fee));
|
||||
} else {
|
||||
/* This was the condition of exiting the loop above! */
|
||||
if (!amount_sat_sub(&excess, input, *amount))
|
||||
abort();
|
||||
}
|
||||
|
||||
response = json_stream_success(cmd);
|
||||
json_add_psbt(response, "psbt", tx->psbt);
|
||||
json_add_amount_sat_only(response, "excess_msat", excess);
|
||||
return command_success(cmd, response);
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
Loading…
Add table
Reference in a new issue