diff --git a/common/json.c b/common/json.c index c89d8af00..072d397b1 100644 --- a/common/json.c +++ b/common/json.c @@ -76,6 +76,21 @@ bool json_tok_double(const char *buffer, const jsmntok_t *tok, double *num) return true; } +bool json_tok_percent(const char *buffer, const jsmntok_t *tok, double *num) +{ + if (!json_tok_double(buffer, tok, num)) + return false; + + /* Ensure it is in the range [0.0, 100.0] */ + if (!(0.0 <= *num)) + return false; + + if (!(*num <= 100.0)) + return false; + + return true; +} + bool json_tok_number(const char *buffer, const jsmntok_t *tok, unsigned int *num) { diff --git a/common/json.h b/common/json.h index 0c1cb12f3..2c4fa4f3c 100644 --- a/common/json.h +++ b/common/json.h @@ -39,6 +39,9 @@ bool json_tok_double(const char *buffer, const jsmntok_t *tok, double *num); bool json_tok_bitcoin_amount(const char *buffer, const jsmntok_t *tok, uint64_t *satoshi); +/* Extract double in range [0.0, 100.0] */ +bool json_tok_percent(const char *buffer, const jsmntok_t *tok, double *num); + /* Extract boolean this (must be a true or false) */ bool json_tok_bool(const char *buffer, const jsmntok_t *tok, bool *b); diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index 54547b955..cd0b3caf3 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -24,6 +24,7 @@ #include <lightningd/lightningd.h> #include <lightningd/log.h> #include <lightningd/options.h> +#include <lightningd/param.h> #include <stdio.h> #include <sys/socket.h> #include <sys/stat.h> @@ -95,14 +96,13 @@ static void json_rhash(struct command *cmd, const char *buffer, const jsmntok_t *params) { struct json_result *response = new_json_result(cmd); - jsmntok_t *secrettok; + const jsmntok_t *secrettok; struct sha256 secret; - if (!json_get_params(cmd, buffer, params, - "secret", &secrettok, - NULL)) { + if (!param(cmd, buffer, params, + p_req("secret", json_tok_tok, &secrettok), + NULL)) return; - } if (!hex_decode(buffer + secrettok->start, secrettok->end - secrettok->start, @@ -200,11 +200,12 @@ static void json_help(struct command *cmd, unsigned int i; struct json_result *response = new_json_result(cmd); struct json_command **cmdlist = get_cmdlist(); - jsmntok_t *cmdtok; + const jsmntok_t *cmdtok; - if (!json_get_params(cmd, buffer, params, "?command", &cmdtok, NULL)) { + if (!param(cmd, buffer, params, + p_opt_tok("command", &cmdtok), + NULL)) return; - } json_object_start(response, NULL); if (cmdtok) { @@ -493,104 +494,6 @@ static void parse_request(struct json_connection *jcon, const jsmntok_t tok[]) assert(c->pending); } -bool json_get_params(struct command *cmd, - const char *buffer, const jsmntok_t param[], ...) -{ - va_list ap; - const char **names; - size_t num_names; - /* Uninitialized warnings on p and end */ - const jsmntok_t *p = NULL, *end = NULL; - - if (param->type == JSMN_ARRAY) { - if (param->size == 0) - p = NULL; - else - p = param + 1; - end = json_next(param); - } else if (param->type != JSMN_OBJECT) { - command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "Expected array or object for params"); - return false; - } - - num_names = 0; - names = tal_arr(cmd, const char *, num_names + 1); - va_start(ap, param); - while ((names[num_names] = va_arg(ap, const char *)) != NULL) { - const jsmntok_t **tokptr = va_arg(ap, const jsmntok_t **); - bool compulsory = true; - if (names[num_names][0] == '?') { - names[num_names]++; - compulsory = false; - } - if (param->type == JSMN_ARRAY) { - *tokptr = p; - if (p) { - p = json_next(p); - if (p == end) - p = NULL; - } - } else { - *tokptr = json_get_member(buffer, param, - names[num_names]); - } - /* Convert 'null' to NULL */ - if (*tokptr - && (*tokptr)->type == JSMN_PRIMITIVE - && buffer[(*tokptr)->start] == 'n') { - *tokptr = NULL; - } - if (compulsory && !*tokptr) { - va_end(ap); - command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "Missing '%s' parameter", - names[num_names]); - return false; - } - num_names++; - tal_resize(&names, num_names + 1); - } - - va_end(ap); - - /* Now make sure there aren't any params which aren't valid */ - if (param->type == JSMN_ARRAY) { - if (param->size > num_names) { - tal_free(names); - command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "Too many parameters:" - " got %u, expected %zu", - param->size, num_names); - return false; - } - } else { - const jsmntok_t *t; - - end = json_next(param); - - /* Find each parameter among the valid names */ - for (t = param + 1; t < end; t = json_next(t+1)) { - bool found = false; - for (size_t i = 0; i < num_names; i++) { - if (json_tok_streq(buffer, t, names[i])) - found = true; - } - if (!found) { - tal_free(names); - command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "Unknown parameter '%.*s'", - t->end - t->start, - buffer + t->start); - return false; - } - } - } - - tal_free(names); - return true; -} - static struct io_plan *write_json(struct io_conn *conn, struct json_connection *jcon) { @@ -864,6 +767,17 @@ json_tok_address_scriptpubkey(const tal_t *cxt, return ADDRESS_PARSE_UNRECOGNIZED; } +bool json_tok_newaddr(const char *buffer, const jsmntok_t *tok, bool *is_p2wpkh) +{ + if (json_tok_streq(buffer, tok, "p2sh-segwit")) + *is_p2wpkh = false; + else if (json_tok_streq(buffer, tok, "bech32")) + *is_p2wpkh = true; + else + return false; + return true; +} + bool json_tok_wtx(struct wallet_tx * tx, const char * buffer, const jsmntok_t *sattok) { diff --git a/lightningd/jsonrpc.h b/lightningd/jsonrpc.h index bb7715ef5..01ece3c3b 100644 --- a/lightningd/jsonrpc.h +++ b/lightningd/jsonrpc.h @@ -60,16 +60,6 @@ struct json_command { const char *verbose; }; -/* Get the parameters (by position or name). Followed by triples of - * of const char *name, const jsmntok_t **ret_ptr, then NULL. - * - * If name starts with '?' it is optional (and will be set to NULL - * if it's a literal 'null' or not present). - * Otherwise false is returned, and command_fail already called. - */ -bool json_get_params(struct command *cmd, - const char *buffer, const jsmntok_t param[], ...); - struct json_result *null_response(const tal_t *ctx); void command_success(struct command *cmd, struct json_result *response); void PRINTF_FMT(3, 4) command_fail(struct command *cmd, int code, @@ -103,6 +93,8 @@ json_tok_address_scriptpubkey(const tal_t *ctx, const char *buffer, const jsmntok_t *tok, const u8 **scriptpubkey); +bool json_tok_newaddr(const char *buffer, const jsmntok_t *tok, bool *is_p2wpkh); + /* Parse the satoshi token in wallet_tx. */ bool json_tok_wtx(struct wallet_tx * tx, const char * buffer, const jsmntok_t * sattok); diff --git a/lightningd/log.c b/lightningd/log.c index 66c1adc4a..8b7dbc52e 100644 --- a/lightningd/log.c +++ b/lightningd/log.c @@ -18,6 +18,7 @@ #include <lightningd/jsonrpc_errors.h> #include <lightningd/lightningd.h> #include <lightningd/options.h> +#include <lightningd/param.h> #include <signal.h> #include <stdio.h> #include <sys/stat.h> @@ -656,18 +657,11 @@ static void json_getlog(struct command *cmd, struct json_result *response = new_json_result(cmd); enum log_level minlevel; struct log_book *lr = cmd->ld->log_book; - jsmntok_t *level; - if (!json_get_params(cmd, buffer, params, "?level", &level, NULL)) { + if (!param(cmd, buffer, params, + p_opt_def("level", json_tok_loglevel, &minlevel, LOG_INFORM), + NULL)) return; - } - - if (!level) - minlevel = LOG_INFORM; - else if (!json_tok_loglevel(buffer, level, &minlevel)) { - command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Invalid level param"); - return; - } json_object_start(response, NULL); if (deprecated_apis) diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 587df7779..8f7c8a2c3 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -19,6 +19,7 @@ #include <lightningd/lightningd.h> #include <lightningd/log.h> #include <lightningd/opening_control.h> +#include <lightningd/param.h> #include <lightningd/peer_control.h> #include <lightningd/subd.h> #include <openingd/gen_opening_wire.h> @@ -892,7 +893,7 @@ bool handle_opening_channel(struct lightningd *ld, static void json_fund_channel(struct command *cmd, const char *buffer, const jsmntok_t *params) { - jsmntok_t *desttok, *sattok; + const jsmntok_t *desttok, *sattok; struct funding_channel * fc = tal(cmd, struct funding_channel); u32 feerate_per_kw = get_feerate(cmd->ld->topology, FEERATE_NORMAL); u8 *msg; @@ -900,10 +901,12 @@ static void json_fund_channel(struct command *cmd, fc->cmd = cmd; fc->uc = NULL; wtx_init(cmd, &fc->wtx); - if (!json_get_params(fc->cmd, buffer, params, - "id", &desttok, - "satoshi", &sattok, NULL)) + if (!param(fc->cmd, buffer, params, + p_req("id", json_tok_tok, &desttok), + p_req("satoshi", json_tok_tok, &sattok), + NULL)) return; + if (!json_tok_wtx(&fc->wtx, buffer, sattok)) return; if (!pubkey_from_hexstr(buffer + desttok->start, diff --git a/lightningd/options.c b/lightningd/options.c index 6852cf536..13f8fc8c5 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -24,6 +24,7 @@ #include <lightningd/lightningd.h> #include <lightningd/log.h> #include <lightningd/options.h> +#include <lightningd/param.h> #include <lightningd/subd.h> #include <stdio.h> #include <string.h> @@ -1059,12 +1060,13 @@ static void json_listconfigs(struct command *cmd, { size_t i; struct json_result *response = new_json_result(cmd); - jsmntok_t *configtok; + const jsmntok_t *configtok; bool found = false; - if (!json_get_params(cmd, buffer, params, "?config", &configtok, NULL)) { + if (!param(cmd, buffer, params, + p_opt_tok("config", &configtok), + NULL)) return; - } json_object_start(response, NULL); if (!configtok) diff --git a/lightningd/param.c b/lightningd/param.c index e11e2d21d..7f685a5a1 100644 --- a/lightningd/param.c +++ b/lightningd/param.c @@ -49,7 +49,10 @@ struct fail_format { static struct fail_format fail_formats[] = { {json_tok_bool, "'%s' should be 'true' or 'false', not '%.*s'"}, {json_tok_double, "'%s' should be a double, not '%.*s'"}, + {json_tok_percent, + "'%s' should be a double in range [0.0, 100.0], not '%.*s'"}, {json_tok_u64, "'%s' should be an unsigned 64 bit integer, not '%.*s'"}, + {json_tok_newaddr, "'%s' should be 'bech32' or 'p2sh-segwit', not '%.*s'"}, {json_tok_number, "'%s' should be an integer, not '%.*s'"}, {json_tok_wtx, "'%s' should be 'all' or a positive integer greater than " diff --git a/lightningd/pay.c b/lightningd/pay.c index 9c1309efd..4caccfaff 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -11,6 +11,7 @@ #include <lightningd/lightningd.h> #include <lightningd/log.h> #include <lightningd/options.h> +#include <lightningd/param.h> #include <lightningd/peer_control.h> #include <lightningd/peer_htlcs.h> #include <lightningd/subd.h> @@ -912,21 +913,19 @@ static void json_sendpay_on_resolve(const struct sendpay_result* r, static void json_sendpay(struct command *cmd, const char *buffer, const jsmntok_t *params) { - jsmntok_t *routetok, *rhashtok; - jsmntok_t *msatoshitok; + const jsmntok_t *routetok, *rhashtok; const jsmntok_t *t, *end; size_t n_hops; struct sha256 rhash; struct route_hop *route; - u64 msatoshi; + u64 *msatoshi; - if (!json_get_params(cmd, buffer, params, - "route", &routetok, - "payment_hash", &rhashtok, - "?msatoshi", &msatoshitok, - NULL)) { + if (!param(cmd, buffer, params, + p_req("route", json_tok_tok, &routetok), + p_req("payment_hash", json_tok_tok, &rhashtok), + p_opt("msatoshi", json_tok_u64, &msatoshi), + NULL)) return; - } if (!hex_decode(buffer + rhashtok->start, rhashtok->end - rhashtok->start, @@ -1006,32 +1005,24 @@ static void json_sendpay(struct command *cmd, return; } - if (msatoshitok) { - if (!json_tok_u64(buffer, msatoshitok, &msatoshi)) { - command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "'%.*s' is not a number", - msatoshitok->end - msatoshitok->start, - buffer + msatoshitok->start); - return; - } - /* The given msatoshi is the actual payment that - * the payee is requesting. The final hop amount, is - * what we actually give, which can be from the - * msatoshi to twice msatoshi. */ - /* if not: msatoshi <= finalhop.amount <= 2 * msatoshi, - * fail. */ - if (!(msatoshi <= route[n_hops-1].amount && - route[n_hops-1].amount <= 2 * msatoshi)) { + /* The given msatoshi is the actual payment that the payee is + * requesting. The final hop amount is what we actually give, which can + * be from the msatoshi to twice msatoshi. */ + + /* if not: msatoshi <= finalhop.amount <= 2 * msatoshi, fail. */ + if (msatoshi) { + if (!(*msatoshi <= route[n_hops-1].amount && + route[n_hops-1].amount <= 2 * *msatoshi)) { command_fail(cmd, JSONRPC2_INVALID_PARAMS, "msatoshi %"PRIu64" out of range", - msatoshi); + *msatoshi); return; } - } else - msatoshi = route[n_hops-1].amount; + } - if (send_payment(cmd, cmd->ld, &rhash, route, msatoshi, - &json_sendpay_on_resolve, cmd)) + if (send_payment(cmd, cmd->ld, &rhash, route, + msatoshi ? *msatoshi : route[n_hops-1].amount, + &json_sendpay_on_resolve, cmd)) command_still_pending(cmd); } @@ -1050,15 +1041,14 @@ static void waitsendpay_timeout(struct command *cmd) static void json_waitsendpay(struct command *cmd, const char *buffer, const jsmntok_t *params) { - jsmntok_t *rhashtok; - jsmntok_t *timeouttok; + const jsmntok_t *rhashtok; struct sha256 rhash; - unsigned int timeout; + unsigned int *timeout; - if (!json_get_params(cmd, buffer, params, - "payment_hash", &rhashtok, - "?timeout", &timeouttok, - NULL)) + if (!param(cmd, buffer, params, + p_req("payment_hash", json_tok_tok, &rhashtok), + p_opt("timeout", json_tok_number, &timeout), + NULL)) return; if (!hex_decode(buffer + rhashtok->start, @@ -1071,21 +1061,12 @@ static void json_waitsendpay(struct command *cmd, const char *buffer, return; } - if (timeouttok && !json_tok_number(buffer, timeouttok, &timeout)) { - command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "'%.*s' is not a valid number", - timeouttok->end - timeouttok->start, - buffer + timeouttok->start); - return; - } - if (!wait_payment(cmd, cmd->ld, &rhash, &json_waitsendpay_on_resolve, cmd)) return; - if (timeouttok) - new_reltimer(&cmd->ld->timers, cmd, time_from_sec(timeout), + if (timeout) + new_reltimer(&cmd->ld->timers, cmd, time_from_sec(*timeout), &waitsendpay_timeout, cmd); - command_still_pending(cmd); } @@ -1105,12 +1086,11 @@ static void json_listpayments(struct command *cmd, const char *buffer, jsmntok_t *bolt11tok, *rhashtok; struct sha256 *rhash = NULL; - if (!json_get_params(cmd, buffer, params, - "?bolt11", &bolt11tok, - "?payment_hash", &rhashtok, - NULL)) { + if (!param(cmd, buffer, params, + p_opt_tok("bolt11", &bolt11tok), + p_opt_tok("payment_hash", &rhashtok), + NULL)) return; - } if (bolt11tok) { struct bolt11 *b11; diff --git a/lightningd/payalgo.c b/lightningd/payalgo.c index 076fa7868..8b7ca115f 100644 --- a/lightningd/payalgo.c +++ b/lightningd/payalgo.c @@ -15,6 +15,7 @@ #include <lightningd/jsonrpc_errors.h> #include <lightningd/lightningd.h> #include <lightningd/log.h> +#include <lightningd/param.h> #include <lightningd/subd.h> #include <sodium/randombytes.h> #include <wallet/wallet.h> @@ -592,29 +593,27 @@ static void json_pay_stop_retrying(struct pay *pay) static void json_pay(struct command *cmd, const char *buffer, const jsmntok_t *params) { - jsmntok_t *bolt11tok, *msatoshitok, *desctok, *riskfactortok, *maxfeetok; - jsmntok_t *retryfortok; - jsmntok_t *maxdelaytok; - double riskfactor = 1.0; - double maxfeepercent = 0.5; - u64 msatoshi; + const jsmntok_t *bolt11tok, *desctok; + double riskfactor; + double maxfeepercent; + u64 *msatoshi; struct pay *pay = tal(cmd, struct pay); struct bolt11 *b11; char *fail, *b11str, *desc; - unsigned int retryfor = 60; - unsigned int maxdelay = cmd->ld->config.locktime_max; - - if (!json_get_params(cmd, buffer, params, - "bolt11", &bolt11tok, - "?msatoshi", &msatoshitok, - "?description", &desctok, - "?riskfactor", &riskfactortok, - "?maxfeepercent", &maxfeetok, - "?retry_for", &retryfortok, - "?maxdelay", &maxdelaytok, - NULL)) { + unsigned int retryfor; + unsigned int maxdelay; + + if (!param(cmd, buffer, params, + p_req("bolt11", json_tok_tok, &bolt11tok), + p_opt("msatoshi", json_tok_u64, &msatoshi), + p_opt_tok("description", &desctok), + p_opt_def("riskfactor", json_tok_double, &riskfactor, 1.0), + p_opt_def("maxfeepercent", json_tok_percent, &maxfeepercent, 0.5), + p_opt_def("retry_for", json_tok_number, &retryfor, 60), + p_opt_def("maxdelay", json_tok_number, &maxdelay, + cmd->ld->config.locktime_max), + NULL)) return; - } b11str = tal_strndup(cmd, buffer + bolt11tok->start, bolt11tok->end - bolt11tok->start); @@ -638,78 +637,24 @@ static void json_pay(struct command *cmd, pay->expiry.ts.tv_sec = b11->timestamp + b11->expiry; pay->min_final_cltv_expiry = b11->min_final_cltv_expiry; - if (retryfortok && !json_tok_number(buffer, retryfortok, &retryfor)) { - command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "'%.*s' is not an integer", - retryfortok->end - retryfortok->start, - buffer + retryfortok->start); - return; - } - if (b11->msatoshi) { - msatoshi = *b11->msatoshi; - if (msatoshitok) { + if (msatoshi) { command_fail(cmd, JSONRPC2_INVALID_PARAMS, "msatoshi parameter unnecessary"); return; } + msatoshi = b11->msatoshi; } else { - if (!msatoshitok) { + if (!msatoshi) { command_fail(cmd, JSONRPC2_INVALID_PARAMS, "msatoshi parameter required"); return; } - if (!json_tok_u64(buffer, msatoshitok, &msatoshi)) { - command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "msatoshi '%.*s' is not a valid number", - msatoshitok->end-msatoshitok->start, - buffer + msatoshitok->start); - return; - } - } - pay->msatoshi = msatoshi; - - if (riskfactortok - && !json_tok_double(buffer, riskfactortok, &riskfactor)) { - command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "'%.*s' is not a valid double", - riskfactortok->end - riskfactortok->start, - buffer + riskfactortok->start); - return; } + pay->msatoshi = *msatoshi; pay->riskfactor = riskfactor * 1000; - - if (maxfeetok - && !json_tok_double(buffer, maxfeetok, &maxfeepercent)) { - command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "'%.*s' is not a valid double", - maxfeetok->end - maxfeetok->start, - buffer + maxfeetok->start); - return; - } - /* Ensure it is in range 0.0 <= maxfeepercent <= 100.0 */ - if (!(0.0 <= maxfeepercent)) { - command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%f maxfeepercent must be non-negative", - maxfeepercent); - return; - } - if (!(maxfeepercent <= 100.0)) { - command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%f maxfeepercent must be <= 100.0", - maxfeepercent); - return; - } pay->maxfeepercent = maxfeepercent; - if (maxdelaytok - && !json_tok_number(buffer, maxdelaytok, &maxdelay)) { - command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "'%.*s' is not a valid integer", - maxdelaytok->end - maxdelaytok->start, - buffer + maxdelaytok->start); - return; - } if (maxdelay < pay->min_final_cltv_expiry) { command_fail(cmd, JSONRPC2_INVALID_PARAMS, "maxdelay (%u) must be greater than " diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index eb9f49ec9..bf4e17d27 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -39,6 +39,7 @@ #include <lightningd/onchain_control.h> #include <lightningd/opening_control.h> #include <lightningd/options.h> +#include <lightningd/param.h> #include <lightningd/peer_htlcs.h> #include <unistd.h> #include <wally_bip32.h> @@ -143,7 +144,7 @@ struct peer *peer_by_id(struct lightningd *ld, const struct pubkey *id) struct peer *peer_from_json(struct lightningd *ld, const char *buffer, - jsmntok_t *peeridtok) + const jsmntok_t *peeridtok) { struct pubkey peerid; @@ -940,38 +941,14 @@ static void gossipd_getpeers_complete(struct subd *gossip, const u8 *msg, static void json_listpeers(struct command *cmd, const char *buffer, const jsmntok_t *params) { - jsmntok_t *leveltok; struct getpeers_args *gpa = tal(cmd, struct getpeers_args); - jsmntok_t *idtok; gpa->cmd = cmd; - gpa->specific_id = NULL; - if (!json_get_params(cmd, buffer, params, - "?id", &idtok, - "?level", &leveltok, - NULL)) { + if (!param(cmd, buffer, params, + p_opt("id", json_tok_pubkey, &gpa->specific_id), + p_opt("level", json_tok_loglevel, &gpa->ll), + NULL)) return; - } - - if (idtok) { - gpa->specific_id = tal_arr(cmd, struct pubkey, 1); - if (!json_tok_pubkey(buffer, idtok, gpa->specific_id)) { - command_fail(cmd, LIGHTNINGD, - "id %.*s not valid", - idtok->end - idtok->start, - buffer + idtok->start); - return; - } - } - if (leveltok) { - gpa->ll = tal(gpa, enum log_level); - if (!json_tok_loglevel(buffer, leveltok, gpa->ll)) { - command_fail(cmd, LIGHTNINGD, - "Invalid level param"); - return; - } - } else - gpa->ll = NULL; /* Get peers from gossipd. */ subd_req(cmd, cmd->ld->gossip, @@ -1040,36 +1017,18 @@ command_find_channel(struct command *cmd, static void json_close(struct command *cmd, const char *buffer, const jsmntok_t *params) { - jsmntok_t *idtok; - jsmntok_t *timeouttok; - jsmntok_t *forcetok; + const jsmntok_t *idtok; struct peer *peer; struct channel *channel; - unsigned int timeout = 30; - bool force = false; - - if (!json_get_params(cmd, buffer, params, - "id", &idtok, - "?force", &forcetok, - "?timeout", &timeouttok, - NULL)) { - return; - } + unsigned int timeout; + bool force; - if (forcetok && !json_tok_bool(buffer, forcetok, &force)) { - command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "Force '%.*s' must be true or false", - forcetok->end - forcetok->start, - buffer + forcetok->start); + if (!param(cmd, buffer, params, + p_req("id", json_tok_tok, &idtok), + p_opt_def("force", json_tok_bool, &force, false), + p_opt_def("timeout", json_tok_number, &timeout, 30), + NULL)) return; - } - if (timeouttok && !json_tok_number(buffer, timeouttok, &timeout)) { - command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "Timeout '%.*s' is not a number", - timeouttok->end - timeouttok->start, - buffer + timeouttok->start); - return; - } peer = peer_from_json(cmd->ld, buffer, idtok); if (peer) @@ -1194,22 +1153,13 @@ static void gossip_peer_disconnected (struct subd *gossip, static void json_disconnect(struct command *cmd, const char *buffer, const jsmntok_t *params) { - jsmntok_t *idtok; struct pubkey id; u8 *msg; - if (!json_get_params(cmd, buffer, params, - "id", &idtok, - NULL)) { + if (!param(cmd, buffer, params, + p_req("id", json_tok_pubkey, &id), + NULL)) return; - } - - if (!json_tok_pubkey(buffer, idtok, &id)) { - command_fail(cmd, LIGHTNINGD, "id %.*s not valid", - idtok->end - idtok->start, - buffer + idtok->start); - return; - } msg = towire_gossipctl_peer_disconnect(cmd, &id); subd_req(cmd, cmd->ld->gossip, msg, -1, 0, gossip_peer_disconnected, cmd); @@ -1227,19 +1177,18 @@ AUTODATA(json_command, &disconnect_command); static void json_sign_last_tx(struct command *cmd, const char *buffer, const jsmntok_t *params) { - jsmntok_t *peertok; + struct pubkey peerid; struct peer *peer; struct json_result *response = new_json_result(cmd); u8 *linear; struct channel *channel; - if (!json_get_params(cmd, buffer, params, - "id", &peertok, - NULL)) { + if (!param(cmd, buffer, params, + p_req("id", json_tok_pubkey, &peerid), + NULL)) return; - } - peer = peer_from_json(cmd->ld, buffer, peertok); + peer = peer_by_id(cmd->ld, &peerid); if (!peer) { command_fail(cmd, LIGHTNINGD, "Could not find peer with that id"); @@ -1274,17 +1223,16 @@ AUTODATA(json_command, &dev_sign_last_tx); static void json_dev_fail(struct command *cmd, const char *buffer, const jsmntok_t *params) { - jsmntok_t *peertok; + struct pubkey peerid; struct peer *peer; struct channel *channel; - if (!json_get_params(cmd, buffer, params, - "id", &peertok, - NULL)) { + if (!param(cmd, buffer, params, + p_req("id", json_tok_pubkey, &peerid), + NULL)) return; - } - peer = peer_from_json(cmd->ld, buffer, peertok); + peer = peer_by_id(cmd->ld, &peerid); if (!peer) { command_fail(cmd, LIGHTNINGD, "Could not find peer with that id"); @@ -1320,18 +1268,17 @@ static void dev_reenable_commit_finished(struct subd *channeld UNUSED, static void json_dev_reenable_commit(struct command *cmd, const char *buffer, const jsmntok_t *params) { - jsmntok_t *peertok; + struct pubkey peerid; struct peer *peer; u8 *msg; struct channel *channel; - if (!json_get_params(cmd, buffer, params, - "id", &peertok, - NULL)) { + if (!param(cmd, buffer, params, + p_req("id", json_tok_pubkey, &peerid), + NULL)) return; - } - peer = peer_from_json(cmd->ld, buffer, peertok); + peer = peer_by_id(cmd->ld, &peerid); if (!peer) { command_fail(cmd, LIGHTNINGD, "Could not find peer with that id"); @@ -1408,33 +1355,30 @@ static void process_dev_forget_channel(struct bitcoind *bitcoind UNUSED, static void json_dev_forget_channel(struct command *cmd, const char *buffer, const jsmntok_t *params) { - jsmntok_t *nodeidtok, *forcetok, *scidtok; + struct pubkey peerid; struct peer *peer; struct channel *channel; - struct short_channel_id scid; + struct short_channel_id *scid; struct dev_forget_channel_cmd *forget = tal(cmd, struct dev_forget_channel_cmd); forget->cmd = cmd; - if (!json_get_params(cmd, buffer, params, - "id", &nodeidtok, - "?short_channel_id", &scidtok, - "?force", &forcetok, - NULL)) { - return; - } - if (scidtok && !json_tok_short_channel_id(buffer, scidtok, &scid)) { - command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "Invalid short_channel_id '%.*s'", - scidtok->end - scidtok->start, - buffer + scidtok->start); + /* If &forget->force is used directly in p_opt_def() below then + * gcc 7.3.0 fails with: + * 'operation on ‘forget->force’ may be undefined [-Werror=sequence-point]' + * + * See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86584 + * + * Hence this indirection. + */ + bool *force = &forget->force; + if (!param(cmd, buffer, params, + p_req("id", json_tok_pubkey, &peerid), + p_opt("short_channel_id", json_tok_short_channel_id, &scid), + p_opt_def("force", json_tok_bool, force, false), + NULL)) return; - } - - forget->force = false; - if (forcetok) - json_tok_bool(buffer, forcetok, &forget->force); - peer = peer_from_json(cmd->ld, buffer, nodeidtok); + peer = peer_by_id(cmd->ld, &peerid); if (!peer) { command_fail(cmd, LIGHTNINGD, "Could not find channel with that peer"); @@ -1443,10 +1387,10 @@ static void json_dev_forget_channel(struct command *cmd, const char *buffer, forget->channel = NULL; list_for_each(&peer->channels, channel, list) { - if (scidtok) { + if (scid) { if (!channel->scid) continue; - if (!short_channel_id_eq(channel->scid, &scid)) + if (!short_channel_id_eq(channel->scid, scid)) continue; } if (forget->channel) { diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h index 642fbc02b..a84009b09 100644 --- a/lightningd/peer_control.h +++ b/lightningd/peer_control.h @@ -62,7 +62,7 @@ void delete_peer(struct peer *peer); struct peer *peer_by_id(struct lightningd *ld, const struct pubkey *id); struct peer *peer_from_json(struct lightningd *ld, const char *buffer, - jsmntok_t *peeridtok); + const jsmntok_t *peeridtok); /* The three ways peers enter from the network: * diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index 23e9b5d1c..94d76033e 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -10,10 +10,12 @@ #include <gossipd/gen_gossip_wire.h> #include <lightningd/chaintopology.h> #include <lightningd/htlc_end.h> +#include <lightningd/json.h> #include <lightningd/jsonrpc.h> #include <lightningd/jsonrpc_errors.h> #include <lightningd/lightningd.h> #include <lightningd/log.h> +#include <lightningd/param.h> #include <lightningd/pay.h> #include <lightningd/peer_control.h> #include <lightningd/peer_htlcs.h> @@ -1651,30 +1653,24 @@ void notify_feerate_change(struct lightningd *ld) static void json_dev_ignore_htlcs(struct command *cmd, const char *buffer, const jsmntok_t *params) { - jsmntok_t *nodeidtok, *ignoretok; + struct pubkey peerid; struct peer *peer; + bool ignore; - if (!json_get_params(cmd, buffer, params, - "id", &nodeidtok, - "ignore", &ignoretok, - NULL)) { + if (!param(cmd, buffer, params, + p_req("id", json_tok_pubkey, &peerid), + p_req("ignore", json_tok_bool, &ignore), + NULL)) return; - } - peer = peer_from_json(cmd->ld, buffer, nodeidtok); + peer = peer_by_id(cmd->ld, &peerid); if (!peer) { command_fail(cmd, LIGHTNINGD, "Could not find channel with that peer"); return; } + peer->ignore_htlcs = ignore; - if (!json_tok_bool(buffer, ignoretok, &peer->ignore_htlcs)) { - command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "Invalid boolean '%.*s'", - ignoretok->end - ignoretok->start, - buffer + ignoretok->start); - return; - } command_success(cmd, null_response(cmd)); } diff --git a/lightningd/test/run-param.c b/lightningd/test/run-param.c index de69af0c5..746fdbccd 100644 --- a/lightningd/test/run-param.c +++ b/lightningd/test/run-param.c @@ -37,13 +37,13 @@ void command_fail_detailed(struct command *cmd, int code, } /* AUTOGENERATED MOCKS START */ +/* Generated stub for json_tok_newaddr */ +bool json_tok_newaddr(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, bool *is_p2wpkh UNNEEDED) +{ fprintf(stderr, "json_tok_newaddr called!\n"); abort(); } /* Generated stub for json_tok_wtx */ -bool json_tok_wtx(struct wallet_tx *tx UNNEEDED, const char *buffer UNNEEDED, +bool json_tok_wtx(struct wallet_tx * tx UNNEEDED, const char * buffer UNNEEDED, const jsmntok_t * sattok UNNEEDED) -{ - abort(); -} - +{ fprintf(stderr, "json_tok_wtx called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ struct json { diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index f9b25af53..338ea031c 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -242,10 +242,6 @@ struct json_escaped *json_escape(const tal_t *ctx UNNEEDED, const char *str TAKE struct json_escaped *json_escaped_string_(const tal_t *ctx UNNEEDED, const void *bytes UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "json_escaped_string_ called!\n"); abort(); } -/* Generated stub for json_get_params */ -bool json_get_params(struct command *cmd UNNEEDED, - const char *buffer UNNEEDED, const jsmntok_t param[] UNNEEDED, ...) -{ fprintf(stderr, "json_get_params called!\n"); abort(); } /* Generated stub for json_object_end */ void json_object_end(struct json_result *ptr UNNEEDED) { fprintf(stderr, "json_object_end called!\n"); abort(); } @@ -275,6 +271,10 @@ bool json_tok_pubkey(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, bool json_tok_short_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, struct short_channel_id *scid UNNEEDED) { fprintf(stderr, "json_tok_short_channel_id called!\n"); abort(); } +/* Generated stub for json_tok_tok */ +bool json_tok_tok(const char *buffer UNNEEDED, const jsmntok_t * tok UNNEEDED, + const jsmntok_t **out UNNEEDED) +{ fprintf(stderr, "json_tok_tok called!\n"); abort(); } /* Generated stub for kill_uncommitted_channel */ void kill_uncommitted_channel(struct uncommitted_channel *uc UNNEEDED, const char *why UNNEEDED) @@ -312,6 +312,10 @@ struct outpointfilter *outpointfilter_new(tal_t *ctx UNNEEDED) void outpointfilter_remove(struct outpointfilter *of UNNEEDED, const struct bitcoin_txid *txid UNNEEDED, const u32 outnum UNNEEDED) { fprintf(stderr, "outpointfilter_remove called!\n"); abort(); } +/* Generated stub for param */ +bool param(struct command *cmd UNNEEDED, const char *buffer UNNEEDED, + const jsmntok_t params[] UNNEEDED, ...) +{ fprintf(stderr, "param called!\n"); abort(); } /* Generated stub for peer_accept_channel */ u8 *peer_accept_channel(const tal_t *ctx UNNEEDED, struct lightningd *ld UNNEEDED, diff --git a/wallet/walletrpc.c b/wallet/walletrpc.c index 28ce72f3c..72dec9a46 100644 --- a/wallet/walletrpc.c +++ b/wallet/walletrpc.c @@ -19,6 +19,7 @@ #include <lightningd/jsonrpc_errors.h> #include <lightningd/lightningd.h> #include <lightningd/log.h> +#include <lightningd/param.h> #include <lightningd/peer_control.h> #include <lightningd/subd.h> #include <wally_bip32.h> @@ -86,7 +87,7 @@ static void wallet_withdrawal_broadcast(struct bitcoind *bitcoind UNUSED, static void json_withdraw(struct command *cmd, const char *buffer, const jsmntok_t *params) { - jsmntok_t *desttok, *sattok; + const jsmntok_t *desttok, *sattok; struct withdrawal *withdraw = tal(cmd, struct withdrawal); u32 feerate_per_kw = get_feerate(cmd->ld->topology, FEERATE_NORMAL); @@ -96,10 +97,13 @@ static void json_withdraw(struct command *cmd, withdraw->cmd = cmd; wtx_init(cmd, &withdraw->wtx); - if (!json_get_params(withdraw->cmd, buffer, params, - "destination", &desttok, - "satoshi", &sattok, NULL)) + + if (!param(cmd, buffer, params, + p_req("destination", json_tok_tok, &desttok), + p_req("satoshi", json_tok_tok, &sattok), + NULL)) return; + if (!json_tok_wtx(&withdraw->wtx, buffer, sattok)) return; @@ -217,26 +221,14 @@ static void json_newaddr(struct command *cmd, const char *buffer UNUSED, struct json_result *response = new_json_result(cmd); struct ext_key ext; struct pubkey pubkey; - jsmntok_t *addrtype; bool is_p2wpkh; s64 keyidx; char *out; - if (!json_get_params(cmd, buffer, params, - "?addresstype", &addrtype, NULL)) { - return; - } - - if (!addrtype || json_tok_streq(buffer, addrtype, "p2sh-segwit")) - is_p2wpkh = false; - else if (json_tok_streq(buffer, addrtype, "bech32")) - is_p2wpkh = true; - else { - command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "Invalid address type " - "(expected bech32 or p2sh-segwit)"); + if (!param(cmd, buffer, params, + p_opt_def("addresstype", json_tok_newaddr, &is_p2wpkh, true), + NULL)) return; - } keyidx = wallet_get_newindex(cmd->ld); if (keyidx < 0) { @@ -287,18 +279,15 @@ static void json_listaddrs(struct command *cmd, struct json_result *response = new_json_result(cmd); struct ext_key ext; struct pubkey pubkey; - jsmntok_t *bip32tok; u64 bip32_max_index; - if (!json_get_params(cmd, buffer, params, - "?bip32_max_index", &bip32tok, - NULL)) { + if (!param(cmd, buffer, params, + p_opt_def("bip32_max_index", json_tok_u64, &bip32_max_index, + db_get_intvar(cmd->ld->wallet->db, + "bip32_max_index", 0)), + NULL)) return; - } - if (!bip32tok || !json_tok_u64(buffer, bip32tok, &bip32_max_index)) { - bip32_max_index = db_get_intvar(cmd->ld->wallet->db, "bip32_max_index", 0); - } json_object_start(response, NULL); json_array_start(response, "addresses");