lightningd/json: make wallet_tx functions take amount_sat.

Using param_tok is generally deprecated, as it doesn't give any sanity checking
for the JSON 'check' command.  So make param_wtx usable directly, and
also make it have a struct amount_sat.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2019-02-21 13:08:42 +10:30
parent 3ba544bfde
commit 2632cc3f34
7 changed files with 96 additions and 79 deletions

View File

@ -4,26 +4,53 @@
#include <inttypes.h>
#include <wallet/wallet.h>
void wtx_init(struct command *cmd, struct wallet_tx * wtx)
void wtx_init(struct command *cmd, struct wallet_tx *wtx, struct amount_sat max)
{
wtx->cmd = cmd;
wtx->amount = 0;
wtx->change_key_index = 0;
wtx->utxos = NULL;
wtx->all_funds = false;
wtx->amount = max;
}
static struct command_result *check_amount(const struct wallet_tx *tx,
u64 amount)
struct command_result *param_wtx(struct command *cmd,
const char *name,
const char *buffer,
const jsmntok_t *tok,
struct wallet_tx *wtx)
{
if (tal_count(tx->utxos) == 0) {
return command_fail(tx->cmd, FUND_CANNOT_AFFORD,
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 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;
}
static struct command_result *check_amount(const struct wallet_tx *wtx,
struct amount_sat amount)
{
if (tal_count(wtx->utxos) == 0) {
return command_fail(wtx->cmd, FUND_CANNOT_AFFORD,
"Cannot afford transaction");
}
if (amount < 546) {
return command_fail(tx->cmd, FUND_OUTPUT_IS_DUST,
"Output %"PRIu64" satoshis would be dust",
amount);
if (amount.satoshis < get_chainparams(wtx->cmd->ld)->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;
}
@ -34,18 +61,21 @@ struct command_result *wtx_select_utxos(struct wallet_tx *tx,
{
struct command_result *res;
u64 fee_estimate;
if (tx->all_funds) {
u64 amount;
struct amount_sat amount;
tx->utxos = wallet_select_all(tx->cmd, tx->cmd->ld->wallet,
fee_rate_per_kw, out_len,
&amount,
&amount.satoshis,
&fee_estimate);
res = check_amount(tx, amount);
if (res)
return res;
if (amount <= tx->amount) {
tx->change = 0;
/* 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;
}
@ -56,15 +86,15 @@ struct command_result *wtx_select_utxos(struct wallet_tx *tx,
}
tx->utxos = wallet_select_coins(tx->cmd, tx->cmd->ld->wallet,
tx->amount,
tx->amount.satoshis,
fee_rate_per_kw, out_len,
&fee_estimate, &tx->change);
&fee_estimate, &tx->change.satoshis);
res = check_amount(tx, tx->amount);
if (res)
return res;
if (tx->change < 546) {
tx->change = 0;
if (tx->change.satoshis < get_chainparams(tx->cmd->ld)->dust_limit) {
tx->change = AMOUNT_SAT(0);
tx->change_key_index = 0;
} else {
tx->change_key_index = wallet_get_newindex(tx->cmd->ld);

View File

@ -2,6 +2,7 @@
#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>
@ -11,15 +12,21 @@
*/
struct wallet_tx {
struct command *cmd;
u64 amount;
u64 change;
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);
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 *wtx_select_utxos(struct wallet_tx *tx,
u32 fee_rate_per_kw,
size_t out_len);

View File

@ -10,6 +10,7 @@
#include <common/memleak.h>
#include <common/param.h>
#include <common/type_to_string.h>
#include <common/wallet_tx.h>
#include <common/wireaddr.h>
#include <gossipd/routing.h>
#include <lightningd/json.h>

View File

@ -157,9 +157,4 @@ enum address_parse_result json_tok_address_scriptpubkey(const tal_t *ctx,
const struct chainparams *chainparams,
const char *buffer,
const jsmntok_t *tok, const u8 **scriptpubkey);
/* Parse the satoshi token in wallet_tx. */
struct command_result *param_wtx(struct wallet_tx * tx, const char * buffer,
const jsmntok_t * sattok, u64 max);
#endif /* LIGHTNING_LIGHTNINGD_JSON_H */

View File

@ -31,7 +31,6 @@
#include <common/param.h>
#include <common/timeout.h>
#include <common/version.h>
#include <common/wallet_tx.h>
#include <common/wireaddr.h>
#include <errno.h>
#include <fcntl.h>
@ -1031,22 +1030,6 @@ json_tok_address_scriptpubkey(const tal_t *cxt,
return ADDRESS_PARSE_UNRECOGNIZED;
}
struct command_result *param_wtx(struct wallet_tx * tx, const char * buffer,
const jsmntok_t *sattok, u64 max)
{
if (json_tok_streq(buffer, sattok, "all")) {
tx->all_funds = true;
tx->amount = max;
} else if (!json_to_u64(buffer, sattok, &tx->amount)) {
return command_fail(tx->cmd, JSONRPC2_INVALID_PARAMS,
"Invalid satoshis");
} else if (tx->amount > max) {
return command_fail(tx->cmd, FUND_MAX_EXCEEDED,
"Amount exceeded %"PRIu64, max);
}
return NULL;
}
static struct command_result *param_command(struct command *cmd,
const char *name,
const char *buffer,

View File

@ -128,7 +128,7 @@ void json_add_uncommitted_channel(struct json_stream *response,
json_array_end(response);
}
msatoshi_total = uc->fc->wtx.amount * 1000;
msatoshi_total = uc->fc->wtx.amount.satoshis * 1000;
our_msatoshi = msatoshi_total - uc->fc->push_msat;
json_add_u64(response, "msatoshi_to_us", our_msatoshi);
json_add_u64(response, "msatoshi_total", msatoshi_total);
@ -317,16 +317,16 @@ static void opening_funder_finished(struct subd *openingd, const u8 *resp,
&channel_info.remote_per_commit));
/* Generate the funding tx. */
if (fc->wtx.change
if (!amount_sat_eq(fc->wtx.change, AMOUNT_SAT(0))
&& !bip32_pubkey(ld->wallet->bip32_base,
&changekey, fc->wtx.change_key_index))
fatal("Error deriving change key %u", fc->wtx.change_key_index);
fundingtx = funding_tx(tmpctx, &funding_outnum,
fc->wtx.utxos, fc->wtx.amount,
fc->wtx.utxos, fc->wtx.amount.satoshis,
&fc->uc->local_funding_pubkey,
&channel_info.remote_fundingkey,
fc->wtx.change, &changekey,
fc->wtx.change.satoshis, &changekey,
ld->wallet->bip32_base);
log_debug(fc->uc->log, "Funding tx has %zi inputs, %zu outputs:",
@ -346,22 +346,29 @@ static void opening_funder_finished(struct subd *openingd, const u8 *resp,
if (!bitcoin_txid_eq(&funding_txid, &expected_txid)) {
log_broken(fc->uc->log,
"Funding txid mismatch:"
" satoshi %"PRIu64" change %"PRIu64
" amount %s change %s"
" changeidx %u"
" localkey %s remotekey %s",
fc->wtx.amount,
fc->wtx.change, fc->wtx.change_key_index,
type_to_string(tmpctx, struct amount_sat,
&fc->wtx.amount),
type_to_string(tmpctx, struct amount_sat,
&fc->wtx.change),
fc->wtx.change_key_index,
type_to_string(fc, struct pubkey,
&fc->uc->local_funding_pubkey),
type_to_string(fc, struct pubkey,
&channel_info.remote_fundingkey));
was_pending(command_fail(fc->cmd, JSONRPC2_INVALID_PARAMS,
"Funding txid mismatch:"
" satoshi %"PRIu64" change %"PRIu64
" amount %s change %s"
" changeidx %u"
" localkey %s remotekey %s",
fc->wtx.amount,
fc->wtx.change,
type_to_string(tmpctx,
struct amount_sat,
&fc->wtx.amount),
type_to_string(tmpctx,
struct amount_sat,
&fc->wtx.change),
fc->wtx.change_key_index,
type_to_string(fc, struct pubkey,
&fc->uc->local_funding_pubkey),
@ -376,7 +383,7 @@ static void opening_funder_finished(struct subd *openingd, const u8 *resp,
&remote_commit_sig,
&funding_txid,
funding_outnum,
fc->wtx.amount,
fc->wtx.amount.satoshis,
fc->push_msat,
fc->channel_flags,
&channel_info,
@ -391,7 +398,8 @@ static void opening_funder_finished(struct subd *openingd, const u8 *resp,
log_debug(channel->log, "Getting HSM to sign funding tx");
msg = towire_hsm_sign_funding(tmpctx, channel->funding_satoshi,
fc->wtx.change, fc->wtx.change_key_index,
fc->wtx.change.satoshis,
fc->wtx.change_key_index,
&fc->uc->local_funding_pubkey,
&channel_info.remote_fundingkey,
fc->wtx.utxos);
@ -804,7 +812,6 @@ static struct command_result *json_fund_channel(struct command *cmd,
const jsmntok_t *params)
{
struct command_result *res;
const jsmntok_t *sattok;
struct funding_channel * fc = tal(cmd, struct funding_channel);
struct pubkey *id;
struct peer *peer;
@ -812,23 +819,21 @@ static struct command_result *json_fund_channel(struct command *cmd,
u32 *feerate_per_kw;
bool *announce_channel;
u8 *msg;
u64 max_funding_satoshi = get_chainparams(cmd->ld)->max_funding_satoshi;
struct amount_sat max_funding_satoshi;
max_funding_satoshi.satoshis = get_chainparams(cmd->ld)->max_funding_satoshi;
fc->cmd = cmd;
fc->uc = NULL;
wtx_init(cmd, &fc->wtx);
wtx_init(cmd, &fc->wtx, max_funding_satoshi);
if (!param(fc->cmd, buffer, params,
p_req("id", param_pubkey, &id),
p_req("satoshi", param_tok, &sattok),
p_req("satoshi", param_wtx, &fc->wtx),
p_opt("feerate", param_feerate, &feerate_per_kw),
p_opt_def("announce", param_bool, &announce_channel, true),
NULL))
return command_param_failed();
res = param_wtx(&fc->wtx, buffer, sattok, max_funding_satoshi);
if (res)
return res;
if (!feerate_per_kw) {
feerate_per_kw = tal(cmd, u32);
*feerate_per_kw = opening_feerate(cmd->ld->topology);
@ -876,16 +881,16 @@ static struct command_result *json_fund_channel(struct command *cmd,
if (res)
return res;
assert(fc->wtx.amount <= max_funding_satoshi);
assert(!amount_sat_greater(fc->wtx.amount, max_funding_satoshi));
peer->uncommitted_channel->fc = tal_steal(peer->uncommitted_channel, fc);
fc->uc = peer->uncommitted_channel;
msg = towire_opening_funder(NULL,
fc->wtx.amount,
fc->wtx.amount.satoshis,
fc->push_msat,
*feerate_per_kw,
fc->wtx.change,
fc->wtx.change.satoshis,
fc->wtx.change_key_index,
fc->channel_flags,
fc->wtx.utxos,

View File

@ -66,7 +66,7 @@ static void wallet_withdrawal_broadcast(struct bitcoind *bitcoind UNUSED,
/* Note normally, change_satoshi == withdraw->wtx.change, but
* not if we're actually making a payment to ourselves! */
assert(change_satoshi >= withdraw->wtx.change);
assert(change_satoshi >= withdraw->wtx.change.satoshis);
struct json_stream *response = json_stream_success(cmd);
json_object_start(response, NULL);
@ -93,7 +93,7 @@ static struct command_result *json_withdraw(struct command *cmd,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
{
const jsmntok_t *desttok, *sattok;
const jsmntok_t *desttok;
struct withdrawal *withdraw = tal(cmd, struct withdrawal);
u32 *feerate_per_kw;
struct bitcoin_tx *tx;
@ -103,19 +103,15 @@ static struct command_result *json_withdraw(struct command *cmd,
struct command_result *res;
withdraw->cmd = cmd;
wtx_init(cmd, &withdraw->wtx);
wtx_init(cmd, &withdraw->wtx, AMOUNT_SAT(-1ULL));
if (!param(cmd, buffer, params,
p_req("destination", param_tok, &desttok),
p_req("satoshi", param_tok, &sattok),
p_req("satoshi", param_wtx, &withdraw->wtx),
p_opt("feerate", param_feerate, &feerate_per_kw),
NULL))
return command_param_failed();
res = param_wtx(&withdraw->wtx, buffer, sattok, -1ULL);
if (res)
return res;
if (!feerate_per_kw) {
res = param_feerate_estimate(cmd, &feerate_per_kw,
FEERATE_NORMAL);
@ -160,8 +156,8 @@ static struct command_result *json_withdraw(struct command *cmd,
txfilter_add_derkey(cmd->ld->owned_txfilter, ext.pub_key);
u8 *msg = towire_hsm_sign_withdrawal(cmd,
withdraw->wtx.amount,
withdraw->wtx.change,
withdraw->wtx.amount.satoshis,
withdraw->wtx.change.satoshis,
withdraw->wtx.change_key_index,
withdraw->destination,
withdraw->wtx.utxos);