diff --git a/common/wallet_tx.c b/common/wallet_tx.c index 57f8bfb56..4dfedc0fe 100644 --- a/common/wallet_tx.c +++ b/common/wallet_tx.c @@ -1,4 +1,7 @@ +#include +#include #include +#include #include #include #include @@ -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); +} diff --git a/common/wallet_tx.h b/common/wallet_tx.h index 1a748b0eb..1b8e95229 100644 --- a/common/wallet_tx.h +++ b/common/wallet_tx.h @@ -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 diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 4965ea79d..7321de46e 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -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; diff --git a/wallet/wallet.c b/wallet/wallet.c index 280d9769f..01391463c 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -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, diff --git a/wallet/wallet.h b/wallet/wallet.h index 85d64f1f1..dc8483f64 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -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. *