From 2914d3adfcb4baea8855139b21ee70933e6df93e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 25 Jan 2024 10:58:56 +1030 Subject: [PATCH] plugins: allow plugins to get per-connection deprecated state. Unfortunately, this is awkward: we just copy through most requests, so we can't easily add a "deprecation" field to each one. So we do a notification if the next command has a different deprecation status than the global one, but that requires opt-in from the plugin. We didn't previously document the subscriptions array, so do that now. Signed-off-by: Rusty Russell Changelog-Added: Plugins: `deprecated_oneshot` notifiction subscription to change deprecated status for a single command. --- .../a-day-in-the-life-of-a-plugin.md | 6 ++++-- .../plugin-development/event-notifications.md | 16 ++++++++++++++++ lightningd/jsonrpc.c | 6 +++--- lightningd/jsonrpc.h | 3 +++ lightningd/notification.c | 12 ++++++++++++ lightningd/notification.h | 6 ++++++ lightningd/plugin.c | 10 ++++++++++ 7 files changed, 54 insertions(+), 5 deletions(-) diff --git a/doc/developers-guide/plugin-development/a-day-in-the-life-of-a-plugin.md b/doc/developers-guide/plugin-development/a-day-in-the-life-of-a-plugin.md index dafceba71..414180e69 100644 --- a/doc/developers-guide/plugin-development/a-day-in-the-life-of-a-plugin.md +++ b/doc/developers-guide/plugin-development/a-day-in-the-life-of-a-plugin.md @@ -63,6 +63,7 @@ The `getmanifest` method is required for all plugins and will be called on start } ], "subscriptions": [ + "deprecated_oneshot", "connect", "disconnect" ], @@ -91,11 +92,12 @@ The `getmanifest` method is required for all plugins and will be called on start During startup the `options` will be added to the list of command line options that `lightningd` accepts. If any `options` "name" is already taken startup will abort. The above will add a `--greeting` option with a default value of `World` and the specified description. _Notice that currently string, integers, bool, and flag options are supported._ If an option specifies `dynamic`: `true`, then it should allow a `setconfig` call for that option after initialization. -The `rpcmethods` are methods that will be exposed via `lightningd`'s JSON-RPC over Unix-Socket interface, just like the builtin commands. Any parameters given to the JSON-RPC calls will be passed through verbatim. Notice that the `name`, `description` and `usage` fields -are mandatory, while the `long_description` can be omitted (it'll be set to `description` if it was not provided). `usage` should surround optional parameter names in `[]`. +The `rpcmethods` are methods that will be exposed via `lightningd`'s JSON-RPC over Unix-Socket interface, just like the builtin commands. Any parameters given to the JSON-RPC calls will be passed through verbatim. Notice that the `name`, `description` and `usage` fields are mandatory, while the `long_description` can be omitted (it'll be set to `description` if it was not provided). `usage` should surround optional parameter names in `[]`. `options` and `rpcmethods` can mark themselves `deprecated: true` if you plan on removing them: this will disable them if the user sets `allow-deprecated-apis` to false, or in `--developer` mode. You can also specify `deprecated` as an array of one or two version numbers, indicating when deprecation starts, and the final version it will be permitted, e.g. `"deprecated": ["v24.02", "v24.02"]`. If only one version number is given, then the final version will be 6 months after the start version. +The `subscriptions` array indicates what [Event Notifications](doc:event-notifications) your plugin wants to receive. You should subscribe to `deprecated_oneshot` if you have any deprecated commands or output, so users can use the `deprecations` API to control it on a per-connection basis. You can specify `*` here to subscribe to all other subscriptions (since *v23.08*). + The `nonnumericids` indicates that the plugin can handle string JSON request `id` fields: prior to v22.11 lightningd used numbers for these, and the change to strings broke some plugins. If not set, then strings will be used once this feature is removed after v23.05. See the [lightningd-rpc](ref:lightningd-rpc) documentation for how to handle JSON `id` fields! The `dynamic` indicates if the plugin can be managed after `lightningd` has been started using the [lightning-plugin](ref:lightning-plugin) JSON-RPC command. Critical plugins that should not be stopped should set it to false. Plugin `options` can be passed to dynamic plugins as argument to the `plugin` command . diff --git a/doc/developers-guide/plugin-development/event-notifications.md b/doc/developers-guide/plugin-development/event-notifications.md index ac2ee1cf5..677dfd05b 100644 --- a/doc/developers-guide/plugin-development/event-notifications.md +++ b/doc/developers-guide/plugin-development/event-notifications.md @@ -17,6 +17,22 @@ Plugins subscribe by returning an array of subscriptions as part of the `getmani > > This is a way of specifying that you want to subscribe to all possible event notifications. It is not recommended, but is useful for plugins which want to provide generic infrastructure for others (in future, we may add the ability to dynamically subscribe/unsubscribe). +### `deprecated_oneshot` + +(Added in *v24.02*) + +This is a special notification, which the plugin will only receive it it set `deprecated_oneshot` to `true` in its getmanifest response. It indicates that the immeditately following command wants a different deprecation status than the global `allow-deprecated-apis` setting. + +This is possible because of the `deprecations` RPC command, where individual connections can change their deprecation settings. + +```json +{ + "deprecated_oneshot": { + "deprecated_ok": false + } +} +``` + ### `channel_opened` A notification for topic `channel_opened` is sent if a peer successfully funded a channel with us. It contains the peer id, the funding amount (in millisatoshis), the funding transaction id, and a boolean indicating if the funding transaction has been included into a block. diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index 60e080753..48d4e1673 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -661,7 +661,7 @@ struct json_filter **command_filter_ptr(struct command *cmd) return &cmd->filter; } -static bool command_deprecated_ok(const struct command *cmd) +bool command_deprecated_ok_flag(const struct command *cmd) { if (cmd->jcon) return cmd->jcon->deprecated_ok; @@ -675,7 +675,7 @@ bool command_deprecated_in_ok(struct command *cmd, { return lightningd_deprecated_in_ok(cmd->ld, command_log(cmd), - command_deprecated_ok(cmd), + command_deprecated_ok_flag(cmd), cmd->json_cmd->name, param, depr_start, depr_end, cmd->id); @@ -687,7 +687,7 @@ bool command_deprecated_out_ok(struct command *cmd, const char *depr_end) { return lightningd_deprecated_out_ok(cmd->ld, - command_deprecated_ok(cmd), + command_deprecated_ok_flag(cmd), cmd->json_cmd->name, fieldname, depr_start, depr_end); diff --git a/lightningd/jsonrpc.h b/lightningd/jsonrpc.h index d37fd06b5..95723928b 100644 --- a/lightningd/jsonrpc.h +++ b/lightningd/jsonrpc.h @@ -176,6 +176,9 @@ void json_notify_fmt(struct command *cmd, /* FIXME: For the few cases where return value is indeterminate */ struct command_result *command_its_complicated(const char *why); +/* command can override ld->deprecated_ok */ +bool command_deprecated_ok_flag(const struct command *cmd); + /** * Create a new jsonrpc to wrap all related information. * diff --git a/lightningd/notification.c b/lightningd/notification.c index c0d1b72bd..c8f2c9c32 100644 --- a/lightningd/notification.c +++ b/lightningd/notification.c @@ -610,3 +610,15 @@ bool notify_plugin_shutdown(struct lightningd *ld, struct plugin *p) jsonrpc_notification_end(n); return plugin_single_notify(p, take(n)); } + +bool notify_deprecated_oneshot(struct lightningd *ld, + struct plugin *p, + bool deprecated_ok) +{ + struct jsonrpc_notification *n = notify_start("deprecated_oneshot"); + json_add_bool(n->stream, "deprecated_ok", deprecated_ok); + json_object_end(n->stream); + jsonrpc_notification_end(n); + return plugin_single_notify(p, take(n)); +} +REGISTER_NOTIFICATION(deprecated_oneshot); diff --git a/lightningd/notification.h b/lightningd/notification.h index d7fbbd683..0fff00c98 100644 --- a/lightningd/notification.h +++ b/lightningd/notification.h @@ -99,6 +99,12 @@ void notify_openchannel_peer_sigs(struct lightningd *ld, void notify_channel_open_failed(struct lightningd *ld, const struct channel_id *cid); +/* Tell this plugin about deprecated flag for next: returns false + * if doesn't subscribe */ +bool notify_deprecated_oneshot(struct lightningd *ld, + struct plugin *p, + bool deprecated_ok); + /* Tell this plugin to shutdown: returns true if it was subscribed. */ bool notify_plugin_shutdown(struct lightningd *ld, struct plugin *p); #endif /* LIGHTNING_LIGHTNINGD_NOTIFICATION_H */ diff --git a/lightningd/plugin.c b/lightningd/plugin.c index 2c80d66f7..8ee9a1a10 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -1307,6 +1307,7 @@ static struct command_result *plugin_rpcmethod_dispatch(struct command *cmd, const jsmntok_t *idtok; struct plugin *plugin; struct jsonrpc_request *req; + bool cmd_ok; if (cmd->mode == CMD_CHECK) return command_param_failed(); @@ -1319,6 +1320,15 @@ static struct command_result *plugin_rpcmethod_dispatch(struct command *cmd, idtok = json_get_member(buffer, toks, "id"); assert(idtok != NULL); + /* If they've changed deprecation status for this cmd, tell plugin */ + cmd_ok = command_deprecated_ok_flag(cmd); + if (cmd_ok != cmd->ld->deprecated_ok) { + if (!notify_deprecated_oneshot(cmd->ld, plugin, cmd_ok)) { + log_debug(plugin->log, + "Plugin does not support deprecation setting for cmd %s (id %s)", + cmd->json_cmd->name, cmd->id); + } + } req = jsonrpc_request_start_raw(plugin, cmd->json_cmd->name, cmd->id, plugin->non_numeric_ids, plugin->log,