From 3eccf16f982b9ce8612913e2259a043ca98a3845 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 14:30:19 +0930 Subject: [PATCH] libplugin: datastore helpers. Plugins are supposed to store their data in the datastore, and commando does so: let's make it easier for them by providing convenience APIs. Signed-off-by: Rusty Russell --- plugins/libplugin.c | 123 +++++++++++++++++++++++++++++++++++++++----- plugins/libplugin.h | 58 +++++++++++++++++++++ 2 files changed, 169 insertions(+), 12 deletions(-) diff --git a/plugins/libplugin.c b/plugins/libplugin.c index ee45e8874..6225289bc 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -490,20 +490,18 @@ static struct json_out *start_json_request(const tal_t *ctx, return jout; } -/* Synchronous routine to send command and extract fields from response */ -void rpc_scan(struct plugin *plugin, - const char *method, - const struct json_out *params TAKES, - const char *guide, - ...) +static const char *rpc_scan_core(const tal_t *ctx, + struct plugin *plugin, + const char *method, + const struct json_out *params TAKES, + const char *guide, + va_list ap) { bool error; - const char *err; const jsmntok_t *contents; int reqlen; const char *p; struct json_out *jout; - va_list ap; jout = start_json_request(tmpctx, 0, method, params); finish_and_send_json(plugin->rpc_conn->fd, jout); @@ -514,15 +512,116 @@ void rpc_scan(struct plugin *plugin, method, reqlen, membuf_elems(&plugin->rpc_conn->mb)); p = membuf_consume(&plugin->rpc_conn->mb, reqlen); + return json_scanv(ctx, p, contents, guide, ap); +} + +/* Synchronous routine to send command and extract fields from response */ +void rpc_scan(struct plugin *plugin, + const char *method, + const struct json_out *params TAKES, + const char *guide, + ...) +{ + const char *err; + va_list ap; va_start(ap, guide); - err = json_scanv(tmpctx, p, contents, guide, ap); + err = rpc_scan_core(tmpctx, plugin, method, params, guide, ap); va_end(ap); if (err) - plugin_err(plugin, "Could not parse %s in reply to %s: %s: '%.*s'", - guide, method, err, - reqlen, membuf_elems(&plugin->rpc_conn->mb)); + plugin_err(plugin, "Could not parse %s in reply to %s: %s", + guide, method, err); +} + +static void json_add_keypath(struct json_out *jout, const char *fieldname, const char *path) +{ + char **parts = tal_strsplit(tmpctx, path, "/", STR_EMPTY_OK); + + json_out_start(jout, fieldname, '['); + for (size_t i = 0; parts[i]; parts++) + json_out_addstr(jout, NULL, parts[i]); + json_out_end(jout, ']'); +} + +static bool rpc_scan_datastore(struct plugin *plugin, + const char *path, + const char *hex_or_string, + va_list ap) +{ + const char *guide; + struct json_out *params; + const char *err; + + params = json_out_new(NULL); + json_out_start(params, NULL, '{'); + json_add_keypath(params, "key", path); + json_out_end(params, '}'); + json_out_finished(params); + + guide = tal_fmt(tmpctx, "{datastore:[0:{%s:%%}]}", hex_or_string); + /* FIXME: Could be some other error, but that's probably a caller bug! */ + err = rpc_scan_core(tmpctx, plugin, "listdatastore", take(params), guide, ap); + if (!err) + return true; + plugin_log(plugin, LOG_DBG, "listdatastore error %s: %s", path, err); + return false; +} + +bool rpc_scan_datastore_str(struct plugin *plugin, + const char *path, + ...) +{ + bool ret; + va_list ap; + + va_start(ap, path); + ret = rpc_scan_datastore(plugin, path, "string", ap); + va_end(ap); + return ret; +} + +/* This variant scans the hex encoding, not the string */ +bool rpc_scan_datastore_hex(struct plugin *plugin, + const char *path, + ...) +{ + bool ret; + va_list ap; + + va_start(ap, path); + ret = rpc_scan_datastore(plugin, path, "hex", ap); + va_end(ap); + return ret; +} + +struct command_result *jsonrpc_set_datastore_(struct plugin *plugin, + struct command *cmd, + const char *path, + const void *value, + bool value_is_string, + const char *mode, + struct command_result *(*cb)(struct command *command, + const char *buf, + const jsmntok_t *result, + void *arg), + struct command_result *(*errcb)(struct command *command, + const char *buf, + const jsmntok_t *result, + void *arg), + void *arg) +{ + struct out_req *req; + + req = jsonrpc_request_start(plugin, cmd, "datastore", cb, errcb, arg); + + json_add_keypath(req->js->jout, "key", path); + if (value_is_string) + json_add_string(req->js, "string", value); + else + json_add_hex_talarr(req->js, "hex", value); + json_add_string(req->js, "mode", mode); + return send_outreq(plugin, req); } static void handle_rpc_reply(struct plugin *plugin, const jsmntok_t *toks) diff --git a/plugins/libplugin.h b/plugins/libplugin.h index 6b90ef6e5..6342460cd 100644 --- a/plugins/libplugin.h +++ b/plugins/libplugin.h @@ -160,6 +160,51 @@ struct json_stream *jsonrpc_stream_fail_data(struct command *cmd, int code, const char *err); +/* Helper to jsonrpc_request_start() and send_outreq() to update datastore. */ +struct command_result *jsonrpc_set_datastore_(struct plugin *plugin, + struct command *cmd, + const char *path, + const void *value, + bool value_is_string, + const char *mode, + struct command_result *(*cb)(struct command *command, + const char *buf, + const jsmntok_t *result, + void *arg), + struct command_result *(*errcb)(struct command *command, + const char *buf, + const jsmntok_t *result, + void *arg), + void *arg); + +#define jsonrpc_set_datastore_string(plugin, cmd, path, str, mode, cb, errcb, arg) \ + jsonrpc_set_datastore_((plugin), (cmd), (path), (str), true, (mode), \ + typesafe_cb_preargs(struct command_result *, void *, \ + (cb), (arg), \ + struct command *command, \ + const char *buf, \ + const jsmntok_t *result), \ + typesafe_cb_preargs(struct command_result *, void *, \ + (errcb), (arg), \ + struct command *command, \ + const char *buf, \ + const jsmntok_t *result), \ + (arg)) + +#define jsonrpc_set_datastore_binary(plugin, cmd, path, tal_ptr, mode, cb, errcb, arg) \ + jsonrpc_set_datastore_((plugin), (cmd), (path), (tal_ptr), false, (mode), \ + typesafe_cb_preargs(struct command_result *, void *, \ + (cb), (arg), \ + struct command *command, \ + const char *buf, \ + const jsmntok_t *result), \ + typesafe_cb_preargs(struct command_result *, void *, \ + (errcb), (arg), \ + struct command *command, \ + const char *buf, \ + const jsmntok_t *result), \ + (arg)) + /* This command is finished, here's the response (the content of the * "result" or "error" field) */ WARN_UNUSED_RESULT @@ -220,6 +265,19 @@ void rpc_scan(struct plugin *plugin, const char *guide, ...); +/* Helper to scan datastore: can only be used in init callback. * + Returns false if field does not exist. * path is /-separated. Final + arg is JSON_SCAN or JSON_SCAN_TAL. + */ +bool rpc_scan_datastore_str(struct plugin *plugin, + const char *path, + ...); +/* This variant scans the hex encoding, not the string */ +bool rpc_scan_datastore_hex(struct plugin *plugin, + const char *path, + ...); + + /* Send an async rpc request to lightningd. */ struct command_result *send_outreq(struct plugin *plugin, const struct out_req *req);