diff --git a/CHANGELOG.md b/CHANGELOG.md index 10ec480f7..3c62fc5f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added +- JSON API: New command `check` checks the validity of a JSON API call without running it. - JSON API: `getinfo` now returns `num_peers` `num_pending_channels`, `num_active_channels` and `num_inactive_channels` fields. - JSON API: use `\n\n` to terminate responses, for simplified parsing (pylightning now relies on this) diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index ba5ac1b72..b89c66b2d 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -937,3 +937,71 @@ bool json_tok_wtx(struct wallet_tx * tx, const char * buffer, } return true; } + +static bool json_tok_command(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + const jsmntok_t **out) +{ + cmd->json_cmd = find_cmd(cmd->jcon->ld->jsonrpc, buffer, tok); + if (cmd->json_cmd) + return (*out = tok); + + command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "'%s' of '%.*s' is invalid", + name, tok->end - tok->start, buffer + tok->start); + return false; +} + +static void json_check(struct command *cmd, + const char *buffer, const jsmntok_t *params) +{ + jsmntok_t *mod_params; + const jsmntok_t *name_tok; + bool ok; + struct json_stream *response; + + if (cmd->mode == CMD_USAGE) { + mod_params = NULL; + } else { + mod_params = json_tok_copy(cmd, params); + cmd->allow_unused = true; + } + + if (!param(cmd, buffer, mod_params, + p_req("command_to_check", json_tok_command, &name_tok), + NULL)) + return; + + /* Point name_tok to the name, not the value */ + if (params->type == JSMN_OBJECT) + name_tok--; + + json_tok_remove(&mod_params, (jsmntok_t *)name_tok, 1); + + cmd->mode = CMD_CHECK; + cmd->allow_unused = false; + /* FIXME(wythe): Maybe change "ok" to "failed" since that's really what + * we're after and would be more clear. */ + ok = true; + cmd->ok = &ok; + cmd->json_cmd->dispatch(cmd, buffer, mod_params); + + if (!ok) + return; + + response = json_stream_success(cmd); + json_object_start(response, NULL); + json_add_string(response, "command", cmd->json_cmd->name); + json_add_string(response, "parameters", "ok"); + json_object_end(response); + command_success(cmd, response); +} + +static const struct json_command check_command = { + "check", + json_check, + "Don't run {command_to_check}, just verify parameters.", + .verbose = "check command_to_check [parameters...]\n" +}; + +AUTODATA(json_command, &check_command); diff --git a/lightningd/jsonrpc.h b/lightningd/jsonrpc.h index b461c6a06..da21d36ef 100644 --- a/lightningd/jsonrpc.h +++ b/lightningd/jsonrpc.h @@ -13,7 +13,9 @@ enum command_mode { /* Normal command processing */ CMD_NORMAL, /* Create command usage string, nothing else. */ - CMD_USAGE + CMD_USAGE, + /* Check parameters, nothing else. */ + CMD_CHECK }; /* Context for a command (from JSON, but might outlive the connection!). */ diff --git a/lightningd/param.c b/lightningd/param.c index 1a83d5a3a..bc1397755 100644 --- a/lightningd/param.c +++ b/lightningd/param.c @@ -283,5 +283,9 @@ bool param(struct command *cmd, const char *buffer, return false; } - return param_arr(cmd, buffer, tokens, params); + /* Always return false for CMD_USAGE and CMD_CHECK, signaling the caller + * to return immediately. For CMD_NORMAL, return true if all parameters + * are valid. + */ + return param_arr(cmd, buffer, tokens, params) && cmd->mode == CMD_NORMAL; }