mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-02-21 14:24:09 +01:00
fundchannel: Add a new 'utxo' parameter
This new parameter takes a list of outpoints (as txid:vout) and fund a channel from the corresponding utxos. Example : fundchannel <id> 10000 normal 1 [10767f0db0e568127fffd7f70a154d4599f42d62babf63230a7c3378bfce3cb0:0, c9e040e0b5fc8c59d5e7834108fbc5583001f414dd83faf0a05cff9d1a92d32c:0]
This commit is contained in:
parent
ca4b07c529
commit
ac038b340b
5 changed files with 188 additions and 6 deletions
|
@ -1,4 +1,7 @@
|
|||
#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/wallet_tx.h>
|
||||
#include <inttypes.h>
|
||||
|
@ -23,13 +26,14 @@ struct command_result *param_wtx(struct command *cmd,
|
|||
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 satoshis or 'all', not '%.*s'",
|
||||
name,
|
||||
tok->end - tok->start,
|
||||
buffer + tok->start);
|
||||
"'%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,
|
||||
|
@ -39,6 +43,66 @@ struct command_result *param_wtx(struct command *cmd,
|
|||
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)
|
||||
{
|
||||
|
@ -104,3 +168,64 @@ struct command_result *wtx_select_utxos(struct wallet_tx *tx,
|
|||
}
|
||||
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) */
|
||||
weight = 4 * (4 + 1 + 1 + 4) + 4 * (8 + 1 + out_len);
|
||||
for (size_t i = 0; i < tal_count(utxos); i++) {
|
||||
if (!*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 = 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, get_chainparams(tx->cmd->ld)->dust_limit)) {
|
||||
tx->change_key_index = wallet_get_newindex(tx->cmd->ld);
|
||||
/* 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",
|
||||
type_to_string(tmpctx, struct amount_sat,
|
||||
&fee_estimate));
|
||||
} else {
|
||||
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",
|
||||
type_to_string(tmpctx, struct amount_sat,
|
||||
&fee_estimate));
|
||||
}
|
||||
return check_amount(tx, tx->amount);
|
||||
}
|
||||
|
|
|
@ -27,11 +27,23 @@ struct command_result *param_wtx(struct command *cmd,
|
|||
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
|
||||
|
|
|
@ -996,6 +996,7 @@ static struct command_result *json_fund_channel(struct command *cmd,
|
|||
bool *announce_channel;
|
||||
u8 *msg;
|
||||
struct amount_sat max_funding_satoshi;
|
||||
const struct utxo **chosen_utxos;
|
||||
|
||||
max_funding_satoshi = get_chainparams(cmd->ld)->max_funding;
|
||||
|
||||
|
@ -1009,6 +1010,7 @@ static struct command_result *json_fund_channel(struct command *cmd,
|
|||
p_opt("feerate", param_feerate, &feerate_per_kw),
|
||||
p_opt_def("announce", param_bool, &announce_channel, true),
|
||||
p_opt_def("minconf", param_number, &minconf, 1),
|
||||
p_opt("utxos", param_utxos, &chosen_utxos),
|
||||
NULL))
|
||||
return command_param_failed();
|
||||
|
||||
|
@ -1055,8 +1057,12 @@ static struct command_result *json_fund_channel(struct command *cmd,
|
|||
}
|
||||
|
||||
maxheight = minconf_to_maxheight(*minconf, cmd->ld);
|
||||
res = wtx_select_utxos(fc->wtx, *feerate_per_kw,
|
||||
BITCOIN_SCRIPTPUBKEY_P2WSH_LEN, maxheight);
|
||||
if (chosen_utxos)
|
||||
res = wtx_from_utxos(fc->wtx, *feerate_per_kw,
|
||||
BITCOIN_SCRIPTPUBKEY_P2WSH_LEN, maxheight, chosen_utxos);
|
||||
else
|
||||
res = wtx_select_utxos(fc->wtx, *feerate_per_kw,
|
||||
BITCOIN_SCRIPTPUBKEY_P2WSH_LEN, maxheight);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
|
|
|
@ -380,6 +380,36 @@ const struct utxo **wallet_select_coins(const tal_t *ctx, struct wallet *w,
|
|||
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,
|
||||
|
|
|
@ -371,6 +371,15 @@ const struct utxo **wallet_select_all(const tal_t *ctx, struct wallet *w,
|
|||
struct amount_sat *sat,
|
||||
struct amount_sat *fee_estimate);
|
||||
|
||||
/**
|
||||
* wallet_select_specific - Select utxos given an array of txids and an array of outputs index
|
||||
*
|
||||
* Returns an array of `utxo` structs.
|
||||
*/
|
||||
const struct utxo **wallet_select_specific(const tal_t *ctx, struct wallet *w,
|
||||
struct bitcoin_txid **txids,
|
||||
u32 **outnums);
|
||||
|
||||
/**
|
||||
* wallet_confirm_utxos - Once we've spent a set of utxos, mark them confirmed.
|
||||
*
|
||||
|
|
Loading…
Add table
Reference in a new issue