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

View File

@ -2,6 +2,7 @@
#define LIGHTNING_COMMON_WALLET_TX_H #define LIGHTNING_COMMON_WALLET_TX_H
#include "config.h" #include "config.h"
#include <ccan/short_types/short_types.h> #include <ccan/short_types/short_types.h>
#include <common/amount.h>
#include <lightningd/json.h> #include <lightningd/json.h>
#include <lightningd/jsonrpc.h> #include <lightningd/jsonrpc.h>
#include <lightningd/lightningd.h> #include <lightningd/lightningd.h>
@ -11,15 +12,21 @@
*/ */
struct wallet_tx { struct wallet_tx {
struct command *cmd; struct command *cmd;
u64 amount; struct amount_sat amount, change;
u64 change;
u32 change_key_index; u32 change_key_index;
const struct utxo **utxos; const struct utxo **utxos;
bool all_funds; /* In this case, amount is a maximum. */ 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, struct command_result *wtx_select_utxos(struct wallet_tx *tx,
u32 fee_rate_per_kw, u32 fee_rate_per_kw,
size_t out_len); size_t out_len);

View File

@ -10,6 +10,7 @@
#include <common/memleak.h> #include <common/memleak.h>
#include <common/param.h> #include <common/param.h>
#include <common/type_to_string.h> #include <common/type_to_string.h>
#include <common/wallet_tx.h>
#include <common/wireaddr.h> #include <common/wireaddr.h>
#include <gossipd/routing.h> #include <gossipd/routing.h>
#include <lightningd/json.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 struct chainparams *chainparams,
const char *buffer, const char *buffer,
const jsmntok_t *tok, const u8 **scriptpubkey); 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 */ #endif /* LIGHTNING_LIGHTNINGD_JSON_H */

View File

@ -31,7 +31,6 @@
#include <common/param.h> #include <common/param.h>
#include <common/timeout.h> #include <common/timeout.h>
#include <common/version.h> #include <common/version.h>
#include <common/wallet_tx.h>
#include <common/wireaddr.h> #include <common/wireaddr.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
@ -1031,22 +1030,6 @@ json_tok_address_scriptpubkey(const tal_t *cxt,
return ADDRESS_PARSE_UNRECOGNIZED; 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, static struct command_result *param_command(struct command *cmd,
const char *name, const char *name,
const char *buffer, const char *buffer,

View File

@ -128,7 +128,7 @@ void json_add_uncommitted_channel(struct json_stream *response,
json_array_end(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; our_msatoshi = msatoshi_total - uc->fc->push_msat;
json_add_u64(response, "msatoshi_to_us", our_msatoshi); json_add_u64(response, "msatoshi_to_us", our_msatoshi);
json_add_u64(response, "msatoshi_total", msatoshi_total); 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)); &channel_info.remote_per_commit));
/* Generate the funding tx. */ /* Generate the funding tx. */
if (fc->wtx.change if (!amount_sat_eq(fc->wtx.change, AMOUNT_SAT(0))
&& !bip32_pubkey(ld->wallet->bip32_base, && !bip32_pubkey(ld->wallet->bip32_base,
&changekey, fc->wtx.change_key_index)) &changekey, fc->wtx.change_key_index))
fatal("Error deriving change key %u", fc->wtx.change_key_index); fatal("Error deriving change key %u", fc->wtx.change_key_index);
fundingtx = funding_tx(tmpctx, &funding_outnum, fundingtx = funding_tx(tmpctx, &funding_outnum,
fc->wtx.utxos, fc->wtx.amount, fc->wtx.utxos, fc->wtx.amount.satoshis,
&fc->uc->local_funding_pubkey, &fc->uc->local_funding_pubkey,
&channel_info.remote_fundingkey, &channel_info.remote_fundingkey,
fc->wtx.change, &changekey, fc->wtx.change.satoshis, &changekey,
ld->wallet->bip32_base); ld->wallet->bip32_base);
log_debug(fc->uc->log, "Funding tx has %zi inputs, %zu outputs:", 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)) { if (!bitcoin_txid_eq(&funding_txid, &expected_txid)) {
log_broken(fc->uc->log, log_broken(fc->uc->log,
"Funding txid mismatch:" "Funding txid mismatch:"
" satoshi %"PRIu64" change %"PRIu64 " amount %s change %s"
" changeidx %u" " changeidx %u"
" localkey %s remotekey %s", " localkey %s remotekey %s",
fc->wtx.amount, type_to_string(tmpctx, struct amount_sat,
fc->wtx.change, fc->wtx.change_key_index, &fc->wtx.amount),
type_to_string(tmpctx, struct amount_sat,
&fc->wtx.change),
fc->wtx.change_key_index,
type_to_string(fc, struct pubkey, type_to_string(fc, struct pubkey,
&fc->uc->local_funding_pubkey), &fc->uc->local_funding_pubkey),
type_to_string(fc, struct pubkey, type_to_string(fc, struct pubkey,
&channel_info.remote_fundingkey)); &channel_info.remote_fundingkey));
was_pending(command_fail(fc->cmd, JSONRPC2_INVALID_PARAMS, was_pending(command_fail(fc->cmd, JSONRPC2_INVALID_PARAMS,
"Funding txid mismatch:" "Funding txid mismatch:"
" satoshi %"PRIu64" change %"PRIu64 " amount %s change %s"
" changeidx %u" " changeidx %u"
" localkey %s remotekey %s", " localkey %s remotekey %s",
fc->wtx.amount, type_to_string(tmpctx,
fc->wtx.change, struct amount_sat,
&fc->wtx.amount),
type_to_string(tmpctx,
struct amount_sat,
&fc->wtx.change),
fc->wtx.change_key_index, fc->wtx.change_key_index,
type_to_string(fc, struct pubkey, type_to_string(fc, struct pubkey,
&fc->uc->local_funding_pubkey), &fc->uc->local_funding_pubkey),
@ -376,7 +383,7 @@ static void opening_funder_finished(struct subd *openingd, const u8 *resp,
&remote_commit_sig, &remote_commit_sig,
&funding_txid, &funding_txid,
funding_outnum, funding_outnum,
fc->wtx.amount, fc->wtx.amount.satoshis,
fc->push_msat, fc->push_msat,
fc->channel_flags, fc->channel_flags,
&channel_info, &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"); log_debug(channel->log, "Getting HSM to sign funding tx");
msg = towire_hsm_sign_funding(tmpctx, channel->funding_satoshi, 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, &fc->uc->local_funding_pubkey,
&channel_info.remote_fundingkey, &channel_info.remote_fundingkey,
fc->wtx.utxos); fc->wtx.utxos);
@ -804,7 +812,6 @@ static struct command_result *json_fund_channel(struct command *cmd,
const jsmntok_t *params) const jsmntok_t *params)
{ {
struct command_result *res; struct command_result *res;
const jsmntok_t *sattok;
struct funding_channel * fc = tal(cmd, struct funding_channel); struct funding_channel * fc = tal(cmd, struct funding_channel);
struct pubkey *id; struct pubkey *id;
struct peer *peer; struct peer *peer;
@ -812,23 +819,21 @@ static struct command_result *json_fund_channel(struct command *cmd,
u32 *feerate_per_kw; u32 *feerate_per_kw;
bool *announce_channel; bool *announce_channel;
u8 *msg; 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->cmd = cmd;
fc->uc = NULL; fc->uc = NULL;
wtx_init(cmd, &fc->wtx); wtx_init(cmd, &fc->wtx, max_funding_satoshi);
if (!param(fc->cmd, buffer, params, if (!param(fc->cmd, buffer, params,
p_req("id", param_pubkey, &id), 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("feerate", param_feerate, &feerate_per_kw),
p_opt_def("announce", param_bool, &announce_channel, true), p_opt_def("announce", param_bool, &announce_channel, true),
NULL)) NULL))
return command_param_failed(); return command_param_failed();
res = param_wtx(&fc->wtx, buffer, sattok, max_funding_satoshi);
if (res)
return res;
if (!feerate_per_kw) { if (!feerate_per_kw) {
feerate_per_kw = tal(cmd, u32); feerate_per_kw = tal(cmd, u32);
*feerate_per_kw = opening_feerate(cmd->ld->topology); *feerate_per_kw = opening_feerate(cmd->ld->topology);
@ -876,16 +881,16 @@ static struct command_result *json_fund_channel(struct command *cmd,
if (res) if (res)
return 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); peer->uncommitted_channel->fc = tal_steal(peer->uncommitted_channel, fc);
fc->uc = peer->uncommitted_channel; fc->uc = peer->uncommitted_channel;
msg = towire_opening_funder(NULL, msg = towire_opening_funder(NULL,
fc->wtx.amount, fc->wtx.amount.satoshis,
fc->push_msat, fc->push_msat,
*feerate_per_kw, *feerate_per_kw,
fc->wtx.change, fc->wtx.change.satoshis,
fc->wtx.change_key_index, fc->wtx.change_key_index,
fc->channel_flags, fc->channel_flags,
fc->wtx.utxos, 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 /* Note normally, change_satoshi == withdraw->wtx.change, but
* not if we're actually making a payment to ourselves! */ * 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); struct json_stream *response = json_stream_success(cmd);
json_object_start(response, NULL); 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 *obj UNNEEDED,
const jsmntok_t *params) const jsmntok_t *params)
{ {
const jsmntok_t *desttok, *sattok; const jsmntok_t *desttok;
struct withdrawal *withdraw = tal(cmd, struct withdrawal); struct withdrawal *withdraw = tal(cmd, struct withdrawal);
u32 *feerate_per_kw; u32 *feerate_per_kw;
struct bitcoin_tx *tx; struct bitcoin_tx *tx;
@ -103,19 +103,15 @@ static struct command_result *json_withdraw(struct command *cmd,
struct command_result *res; struct command_result *res;
withdraw->cmd = cmd; withdraw->cmd = cmd;
wtx_init(cmd, &withdraw->wtx); wtx_init(cmd, &withdraw->wtx, AMOUNT_SAT(-1ULL));
if (!param(cmd, buffer, params, if (!param(cmd, buffer, params,
p_req("destination", param_tok, &desttok), 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), p_opt("feerate", param_feerate, &feerate_per_kw),
NULL)) NULL))
return command_param_failed(); return command_param_failed();
res = param_wtx(&withdraw->wtx, buffer, sattok, -1ULL);
if (res)
return res;
if (!feerate_per_kw) { if (!feerate_per_kw) {
res = param_feerate_estimate(cmd, &feerate_per_kw, res = param_feerate_estimate(cmd, &feerate_per_kw,
FEERATE_NORMAL); 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); txfilter_add_derkey(cmd->ld->owned_txfilter, ext.pub_key);
u8 *msg = towire_hsm_sign_withdrawal(cmd, u8 *msg = towire_hsm_sign_withdrawal(cmd,
withdraw->wtx.amount, withdraw->wtx.amount.satoshis,
withdraw->wtx.change, withdraw->wtx.change.satoshis,
withdraw->wtx.change_key_index, withdraw->wtx.change_key_index,
withdraw->destination, withdraw->destination,
withdraw->wtx.utxos); withdraw->wtx.utxos);