diff --git a/common/json_command.h b/common/json_command.h index e4a2dbd67..8586ba72d 100644 --- a/common/json_command.h +++ b/common/json_command.h @@ -15,6 +15,9 @@ struct command_result *command_fail(struct command *cmd, enum jsonrpc_errcode co const char *fmt, ...) PRINTF_FMT(3, 4) WARN_UNUSED_RESULT RETURNS_NONNULL; +/* Caller supplies this too: must provide this to reach into cmd */ +struct json_filter **command_filter_ptr(struct command *cmd); + /* Convenient wrapper for "paramname: msg: invalid token '.*%s'" */ static inline struct command_result * command_fail_badparam(struct command *cmd, diff --git a/common/json_filter.c b/common/json_filter.c index ed422f32d..bda4b91eb 100644 --- a/common/json_filter.c +++ b/common/json_filter.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -190,3 +191,61 @@ const char *json_filter_misused(const tal_t *ctx, const struct json_filter *f) return tal_steal(ctx, ret); } } + +/* Recursively populate filter. NULL on success. + * + * Example for listtransactions to include output type, amount_msat, + * {"transactions": [{"outputs": [{"amount_msat": true, "type": true}]}]} + */ +static struct command_result * +build_filter(struct command *cmd, + const char *name, + const char *buffer, + const jsmntok_t *tok, + struct json_filter *filter) +{ + struct command_result *ret; + size_t i; + const jsmntok_t *t; + struct json_filter *subf; + + if (tok->type == JSMN_ARRAY) { + if (tok->size != 1) + return command_fail_badparam(cmd, name, buffer, tok, + "Arrays can only have one element"); + subf = json_filter_subarr(filter); + return build_filter(cmd, name, buffer, tok + 1, subf); + } + + json_for_each_obj(i, t, tok) { + bool is_true; + const jsmntok_t *val = t + 1; + + if (t->type != JSMN_STRING) + return command_fail_badparam(cmd, name, buffer, t, + "expected string key"); + subf = json_filter_subobj(filter, buffer + t->start, t->end - t->start); + if (val->type == JSMN_OBJECT || val->type == JSMN_ARRAY) { + ret = build_filter(cmd, name, buffer, val, subf); + if (ret) + return ret; + } else if (!json_to_bool(buffer, val, &is_true) || !is_true) + return command_fail_badparam(cmd, name, buffer, val, "value must be true"); + } + return NULL; +} + +struct command_result *parse_filter(struct command *cmd, + const char *name, + const char *buffer, + const jsmntok_t *tok) +{ + struct json_filter **filter = command_filter_ptr(cmd); + + if (tok->type != JSMN_OBJECT) + return command_fail_badparam(cmd, name, buffer, tok, + "Expected object"); + + *filter = json_filter_new(cmd); + return build_filter(cmd, name, buffer, tok, *filter); +} diff --git a/common/json_filter.h b/common/json_filter.h index 0bf298687..dee177aeb 100644 --- a/common/json_filter.h +++ b/common/json_filter.h @@ -5,8 +5,10 @@ #define LIGHTNING_COMMON_JSON_FILTER_H #include "config.h" #include +#include #include +struct command; struct json_filter; /* Print this? */ @@ -31,4 +33,9 @@ struct json_filter *json_filter_subobj(struct json_filter *filter, size_t fieldnamelen); struct json_filter *json_filter_subarr(struct json_filter *filter); +/* Turn this "filter" field into cmd->filter and return NULL, or fail command */ +struct command_result *parse_filter(struct command *cmd, + const char *name, + const char *buffer, + const jsmntok_t *tok); #endif /* LIGHTNING_COMMON_JSON_FILTER_H */ diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index 5c799e4ea..56cbea077 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -493,6 +494,11 @@ struct command_result *command_fail(struct command *cmd, enum jsonrpc_errcode co return command_failed(cmd, r); } +struct json_filter **command_filter_ptr(struct command *cmd) +{ + return &cmd->filter; +} + struct command_result *command_still_pending(struct command *cmd) { notleak_with_children(cmd); @@ -909,6 +915,7 @@ parse_request(struct json_connection *jcon, const jsmntok_t tok[]) c->id_is_string = (id->type == JSMN_STRING); c->id = json_strdup(c, jcon->buffer, id); c->mode = CMD_NORMAL; + c->filter = NULL; list_add_tail(&jcon->commands, &c->list); tal_add_destructor(c, destroy_command); diff --git a/lightningd/jsonrpc.h b/lightningd/jsonrpc.h index 38bff3cae..2310b079b 100644 --- a/lightningd/jsonrpc.h +++ b/lightningd/jsonrpc.h @@ -41,6 +41,8 @@ struct command { enum command_mode mode; /* Have we started a json stream already? For debugging. */ struct json_stream *json_stream; + /* Optional output field filter. */ + struct json_filter *filter; }; /** diff --git a/plugins/libplugin.c b/plugins/libplugin.c index ef68530eb..8f2f0b4b0 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -132,6 +132,12 @@ struct command_result *command_done(void) return &complete; } +/* Don't ask for _filter, we will crash! */ +struct json_filter **command_filter_ptr(struct command *cmd) +{ + return NULL; +} + static void ld_send(struct plugin *plugin, struct json_stream *stream) { struct jstream *jstr = tal(plugin, struct jstream);