This PR: - adds all the guides (in markdown format) that is published at https://docs.corelightning.org/docs - adds a github workflow to sync any future changes made to files inside the guides folder - does not include API reference (json-rpc commands). Those will be handled in a separate PR since they're used as manpages and will require a different github workflow Note that the guides do not exactly map to their related files in doc/, since we reorganized the overall documentation structure on readme for better readability and developer experience. For example, doc/FUZZING.md and doc/HACKING.md#Testing are merged into testing.md in the new docs. As on the creation date of this PR, content from each of the legacy documents has been synced with the new docs. Until this PR gets merged, I will continue to push any updates made to the legacy documents into the new docs. If this looks reasonable, I will add a separate PR to clean up the legacy documents from doc/ (or mark them deprecated) to avoid redundant upkeep and maintenance. Changelog-None
13 KiB
title, slug, hidden, createdAt, updatedAt
| title | slug | hidden | createdAt | updatedAt |
|---|---|---|---|---|
| A day in the life of a plugin | a-day-in-the-life-of-a-plugin | false | 2023-02-03T08:32:53.431Z | 2023-02-21T14:57:10.491Z |
A plugin may be written in any language, and communicates with lightningd through the plugin's stdin and stdout. JSON-RPCv2 is used as protocol on top of the two streams, with the plugin acting as server and lightningd acting as client. The plugin file needs to be executable (e.g. use chmod a+x plugin_name).
🚧
As noted,
lightningdusesstdinas an intake mechanism. This can cause unexpected behavior if one is not careful. To wit, care should be taken to ensure that debug/logging statements must be routed tostderror directly to a file. Activities that are benign in other contexts (println!,dbg!, etc) will cause the plugin to be killed with an error along the lines of:
UNUSUAL plugin-cln-plugin-startup: Killing plugin: JSON-RPC message does not contain "jsonrpc" field
During startup of lightningd you can use the --plugin= option to register one or more plugins that should be started. In case you wish to start several plugins you have to use the --plugin= argument once for each plugin (or --plugin-dir or place them in the default
plugin dirs, usually /usr/local/libexec/c-lightning/plugins and ~/.lightning/plugins). An example call might look like:
lightningd --plugin=/path/to/plugin1 --plugin=path/to/plugin2
lightningd will run your plugins from the --lightning-dir/networkname as working directory and env variables "LIGHTNINGD_PLUGIN" and "LIGHTNINGD_VERSION" set, then will write JSON-RPC requests to the plugin's stdin and will read replies from its stdout. To initialise the plugin two RPC methods are required:
getmanifestasks the plugin for command line options and JSON-RPC commands that should be passed through. This can be run beforelightningdchecks that it is the sole user of thelightning-dirdirectory (for--help) so your plugin should not touch files at this point.initis called after the command line options have been parsed and passes them through with the real values (if specified). This is also the signal thatlightningd's JSON-RPC over Unix Socket is now up and ready to receive incoming requests from the plugin.
Once those two methods were called lightningd will start passing through incoming JSON-RPC commands that were registered and the plugin may interact with lightningd using the JSON-RPC over Unix-Socket interface.
Above is generally valid for plugins that start when lightningd starts. For dynamic plugins that start via the lightning-plugin JSON-RPC command there is some difference, mainly in options passthrough (see note in Types of Options).
shutdown(optional): if subscribed to "shutdown" notification, a plugin can exit cleanly whenlightningdis shutting down or when stopped viaplugin stop.
The getmanifest method
The getmanifest method is required for all plugins and will be called on startup with optional parameters (in particular, it may have allow-deprecated-apis: false, but you should accept, and ignore, other parameters). It MUST return a JSON object similar to this example:
{
"options": [
{
"name": "greeting",
"type": "string",
"default": "World",
"description": "What name should I call you?",
"deprecated": false
}
],
"rpcmethods": [
{
"name": "hello",
"usage": "[name]",
"description": "Returns a personalized greeting for {greeting} (set via options)."
},
{
"name": "gettime",
"usage": "",
"description": "Returns the current time in {timezone}",
"long_description": "Returns the current time in the timezone that is given as the only parameter.\nThis description may be quite long and is allowed to span multiple lines.",
"deprecated": false
}
],
"subscriptions": [
"connect",
"disconnect"
],
"hooks": [
{ "name": "openchannel", "before": ["another_plugin"] },
{ "name": "htlc_accepted" }
],
"featurebits": {
"node": "D0000000",
"channel": "D0000000",
"init": "0E000000",
"invoice": "00AD0000"
},
"notifications": [
{
"method": "mycustomnotification"
}
],
"nonnumericids": true,
"dynamic": true
}
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.
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 (which every developer should do, right?).
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 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 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 .
If a disable member exists, the plugin will be disabled and the contents of this member is the reason why. This allows plugins to disable themselves if they are not supported in this configuration.
The featurebits object allows the plugin to register featurebits that should be announced in a number of places in the protocol. They can be used to signal support for custom protocol extensions to direct peers, remote nodes and in invoices. Custom protocol extensions can be implemented for example using the sendcustommsg method and the custommsg hook, or the sendonion method and the htlc_accepted hook. The keys in the featurebits object are node for features that should be announced via the node_announcement to all nodes in the network, init for features that should be announced to direct peers during the connection setup, channel for features which should apply to channel_announcement, and invoice for features that should be announced to a potential sender of a payment in the invoice. The low range of featurebits is reserved for standardize features, so please pick random, high position bits for experiments. If you'd like to standardize your extension please reach out to the [specification repository][spec] to get a featurebit assigned.
The notifications array allows plugins to announce which custom notifications they intend to send to lightningd. These custom notifications can then be subscribed to by other plugins, allowing them to communicate with each other via the existing publish-subscribe mechanism and react to events that happen in other plugins, or collect information based on the notification topics.
Plugins are free to register any name for their rpcmethod as long as the name was not previously registered. This includes both built-in methods, such as help and getinfo, as well as methods registered by other plugins. If there is a conflict then lightningd will report
an error and kill the plugin, this aborts startup if the plugin is important.
Types of Options
There are currently four supported option 'types':
- string: a string
- bool: a boolean
- int: parsed as a signed integer (64-bit)
- flag: no-arg flag option. Is boolean under the hood. Defaults to false.
In addition, string and int types can specify "multi": true to indicate they can be specified multiple times. These will always be represented in init as a (possibly empty) JSON array.
Nota bene: if a flag type option is not set, it will not appear in the options set that is passed to the plugin.
Here's an example option set, as sent in response to getmanifest
"options": [
{
"name": "greeting",
"type": "string",
"default": "World",
"description": "What name should I call you?"
},
{
"name": "run-hot",
"type": "flag",
"default": None, // defaults to false
"description": "If set, overclocks plugin"
},
{
"name": "is_online",
"type": "bool",
"default": false,
"description": "Set to true if plugin can use network"
},
{
"name": "service-port",
"type": "int",
"default": 6666,
"description": "Port to use to connect to 3rd-party service"
},
{
"name": "number",
"type": "int",
"default": 0,
"description": "Another number to add",
"multi": true
}
],
Note: lightningd command line options are only parsed during startup and their values are not remembered when the plugin is stopped or killed. For dynamic plugins started with plugin start, options can be passed as extra arguments to the command lightning-plugin.
Custom notifications
The plugins may emit custom notifications for topics they have announced during startup. The list of notification topics declared during startup must include all topics that may be emitted, in order to verify that all topics plugins subscribe to are also emitted by some other plugin, and warn if a plugin subscribes to a non-existent topic. In case a plugin emits notifications it has not announced the notification will be ignored and not forwarded to subscribers.
When forwarding a custom notification lightningd will wrap the payload of the notification in an object that contains metadata about the notification. The following is an example of this transformation. The first listing is the original notification emitted by the sender plugin, while the second is the the notification as received by the receiver plugin (both listings show the full JSON-RPC notification to illustrate the wrapping).
{
"jsonrpc": "2.0",
"method": "mycustomnotification",
"params": {
"key": "value",
"message": "Hello fellow plugin!"
}
}
is delivered as
{
"jsonrpc": "2.0",
"method": "mycustomnotification",
"params": {
"origin": "sender",
"payload": {
"key": "value",
"message": "Hello fellow plugin!"
}
}
}
The notification topic (method in the JSON-RPC message) must not match one of the internal events in order to prevent breaking subscribers that expect the existing notification format. Multiple plugins are allowed to emit notifications for the same topics, allowing things like metric aggregators where the aggregator subscribes to a common topic and other plugins publish metrics as notifications.
The init method
The init method is required so that lightningd can pass back the filled command line options and notify the plugin that lightningd is now ready to receive JSON-RPC commands. The params of the call are a simple JSON object containing the options:
{
"options": {
"greeting": "World",
"number": [0]
},
"configuration": {
"lightning-dir": "/home/user/.lightning/testnet",
"rpc-file": "lightning-rpc",
"startup": true,
"network": "testnet",
"feature_set": {
"init": "02aaa2",
"node": "8000000002aaa2",
"channel": "",
"invoice": "028200"
},
"proxy": {
"type": "ipv4",
"address": "127.0.0.1",
"port": 9050
},
"torv3-enabled": true,
"always_use_proxy": false
}
}
The plugin must respond to init calls. The response should be a valid JSON-RPC response to the init, but this is not currently enforced. If the response is an object containing result which contains disable then the plugin will be disabled and the contents
of this member is the reason why.
The startup field allows a plugin to detect if it was started at lightningd startup (true), or at runtime (false).
Timeouts
During startup ("startup" is true), the plugin has 60 seconds to return getmanifest and another 60 seconds to return init, or gets killed.
When started dynamically via the lightning-plugin JSON-RPC command, both getmanifest and init should be completed within 60 seconds.