rgb-cln/doc/PLUGINS.md

951 lines
34 KiB
Markdown
Raw Normal View History

# Plugins
Plugins are a simple yet powerful way to extend the functionality
provided by c-lightning. They are subprocesses that are started by the
main `lightningd` daemon and can interact with `lightningd` in a
variety of ways:
- **Command line option passthrough** allows plugins to register their
own command line options that are exposed through `lightningd` so
that only the main process needs to be configured.
- **JSON-RPC command passthrough** adds a way for plugins to add their
own commands to the JSON-RPC interface.
- **Event stream subscriptions** provide plugins with a push-based
notification mechanism about events from the `lightningd`.
- **Hooks** are a primitive that allows plugins to be notified about
internal events in `lightningd` and alter its behavior or inject
custom behaviors.
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`)
## A day in the life of a plugin
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
2019-09-21 01:52:10 +01:00
plugin dirs, usually `/usr/local/libexec/c-lightning/plugins` and
`~/.lightningd/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, then
will write JSON-RPC requests to the plugin's `stdin` and
will read replies from its `stdout`. To initialize the plugin two RPC
methods are required:
- `getmanifest` asks the plugin for command line options and JSON-RPC
commands that should be passed through. This can be run before
`lightningd` checks that it is the sole user of the `lightning-dir`
directory (for `--help`) so your plugin should not touch files at this
point.
- `init` is called after the command line options have been
parsed and passes them through with the real values (if specified). This is also
the signal that `lightningd`'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.
### The `getmanifest` method
The `getmanifest` method is required for all plugins and will be called on
startup without any params. It MUST return a JSON object similar to
this example:
```json
{
2019-09-21 01:52:10 +01:00
"options": [
{
"name": "greeting",
"type": "string",
"default": "World",
"description": "What name should I call you?"
}
],
"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."
}
],
"subscriptions": [
"connect",
"disconnect"
],
"hooks": [
"openchannel",
"htlc_accepted"
],
"features": {
"node": "D0000000",
"channel": "D0000000",
"init": "0E000000",
"invoice": "00AD0000"
},
2019-09-21 01:52:10 +01:00
"dynamic": true
}
```
The `options` will be added to the list of command line options that
`lightningd` accepts. 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 `[]`.
The `dynamic` indicates if the plugin can be managed after `lightningd`
has been started. Critical plugins that should not be stopped should set it
to false.
The `features` object allows the plugin to register featurebits that should be
announced in a number of places in [the protocol][bolt9]. 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 `features` 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.
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 exit.
#### 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.
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`
```json
"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"
}
],
```
### 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:
```json
{
2019-09-21 01:52:10 +01:00
"options": {
"greeting": "World"
},
"configuration": {
"lightning-dir": "/home/user/.lightning/testnet",
2019-09-21 01:52:10 +01:00
"rpc-file": "lightning-rpc",
"startup": true
}
}
```
The plugin must respond to `init` calls, however the response can be
arbitrary and will currently be discarded by `lightningd`. JSON-RPC
commands were chosen over notifications in order not to force plugins
to implement notifications which are not that well supported.
The `startup` field allows a plugin to detect if it was started at
`lightningd` startup (true), or at runtime (false).
## JSON-RPC passthrough
Plugins may register their own JSON-RPC methods that are exposed
through the JSON-RPC provided by `lightningd`. This provides users
with a single interface to interact with, while allowing the addition
of custom methods without having to modify the daemon itself.
JSON-RPC methods are registered as part of the `getmanifest`
result. Each registered method must provide a `name` and a
`description`. An optional `long_description` may also be
provided. This information is then added to the internal dispatch
table, and used to return the help text when using `lightning-cli
help`, and the methods can be called using the `name`.
For example the above `getmanifest` result will register two methods,
called `hello` and `gettime`:
```json
2019-09-21 01:52:10 +01:00
...
"rpcmethods": [
{
"name": "hello",
"usage": "[name]",
"description": "Returns a personalized greeting for {greeting} (set via options)."
},
{
"name": "gettime",
"description": "Returns the current time in {timezone}",
"usage": "",
"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."
}
],
...
```
The RPC call will be passed through unmodified, with the exception of
the JSON-RPC call `id`, which is internally remapped to a unique
integer instead, in order to avoid collisions. When passing the result
back the `id` field is restored to its original value.
Note that if your `result` for an RPC call includes `"format-hint":
"simple"`, then `lightning-cli` will default to printing your output
in "human-readable" flat form.
## Event notifications
Event notifications allow a plugin to subscribe to events in
`lightningd`. `lightningd` will then send a push notification if an
event matching the subscription occurred. A notification is defined in
the JSON-RPC [specification][jsonrpc-spec] as an RPC call that does
not include an `id` parameter:
> A Notification is a Request object without an "id" member. A Request
> object that is a Notification signifies the Client's lack of
> interest in the corresponding Response object, and as such no
> Response object needs to be returned to the client. The Server MUST
> NOT reply to a Notification, including those that are within a batch
> request.
>
> Notifications are not confirmable by definition, since they do not
> have a Response object to be returned. As such, the Client would not
> be aware of any errors (like e.g. "Invalid params","Internal
> error").
Plugins subscribe by returning an array of subscriptions as part of
the `getmanifest` response. The result for the `getmanifest` call
above for example subscribes to the two topics `connect` and
`disconnect`. The topics that are currently defined and the
corresponding payloads are listed below.
### `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.
2019-09-21 01:52:10 +01:00
```json
{
2019-09-21 01:52:10 +01:00
"channel_opened": {
"id": "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f",
"funding_satoshis": "100000000msat",
"funding_txid": "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b",
"funding_locked": false
}
}
```
### `connect`
A notification for topic `connect` is sent every time a new connection
to a peer is established.
```json
{
2019-09-21 01:52:10 +01:00
"id": "02f6725f9c1c40333b67faea92fd211c183050f28df32cac3f9d69685fe9665432",
"address": "1.2.3.4"
}
```
### `disconnect`
A notification for topic `disconnect` is sent every time a connection
to a peer was lost.
```json
{
2019-09-21 01:52:10 +01:00
"id": "02f6725f9c1c40333b67faea92fd211c183050f28df32cac3f9d69685fe9665432"
}
```
### `invoice_payment`
A notification for topic `invoice_payment` is sent every time an invoie is paid.
```json
{
2019-09-21 01:52:10 +01:00
"invoice_payment": {
"label": "unique-label-for-invoice",
"preimage": "0000000000000000000000000000000000000000000000000000000000000000",
"msat": "10000msat"
}
}
```
### `warning`
A notification for topic `warning` is sent every time a new `BROKEN`
/`UNUSUAL` level(in plugins, we use `error`/`warn`) log generated,
which means an unusual/borken thing happens, such as channel failed,
message resolving failed...
```json
{
2019-09-21 01:52:10 +01:00
"warning": {
"level": "warn",
"time": "1559743608.565342521",
"source": "lightningd(17652): 0821f80652fb840239df8dc99205792bba2e559a05469915804c08420230e23c7c chan #7854:",
"log": "Peer permanent failure in CHANNELD_NORMAL: lightning_channeld: sent ERROR bad reestablish dataloss msg"
}
}
```
1. `level` is `warn` or `error`: `warn` means something seems bad happened
and it's under control, but we'd better check it; `error` means something
extremely bad is out of control, and it may lead to crash;
2. `time` is the second since epoch;
3. `source` means where the event happened, it may have the following
2019-09-21 01:52:10 +01:00
forms:
`<node_id> chan #<db_id_of_channel>:`,`lightningd(<lightningd_pid>):`,
`plugin-<plugin_name>:`, `<daemon_name>(<daemon_pid>):`, `jsonrpc:`,
`jcon fd <error_fd_to_jsonrpc>:`, `plugin-manager`;
4. `log` is the context of the original log entry.
### `forward_event`
A notification for topic `forward_event` is sent every time the status
of a forward payment is set. The json format is same as the API
`listforwards`.
```json
{
"forward_event": {
2019-09-21 01:52:10 +01:00
"payment_hash": "f5a6a059a25d1e329d9b094aeeec8c2191ca037d3f5b0662e21ae850debe8ea2",
"in_channel": "103x2x1",
"out_channel": "103x1x1",
"in_msatoshi": 100001001,
"in_msat": "100001001msat",
"out_msatoshi": 100000000,
"out_msat": "100000000msat",
"fee": 1001,
"fee_msat": "1001msat",
"status": "settled",
"received_time": 1560696342.368,
"resolved_time": 1560696342.556
}
}
```
or
```json
{
"forward_event": {
2019-09-21 01:52:10 +01:00
"payment_hash": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"in_channel": "103x2x1",
"out_channel": "110x1x0",
"in_msatoshi": 100001001,
"in_msat": "100001001msat",
"out_msatoshi": 100000000,
"out_msat": "100000000msat",
"fee": 1001,
"fee_msat": "1001msat",
"status": "local_failed",
"failcode": 16392,
"failreason": "WIRE_PERMANENT_CHANNEL_FAILURE",
"received_time": 1560696343.052
}
}
2019-09-21 01:52:10 +01:00
```
- The status includes `offered`, `settled`, `failed` and `local_failed`,
and they are all string type in json.
- When the forward payment is valid for us, we'll set `offered`
and send the forward payment to next hop to resolve;
- When the payment forwarded by us gets paid eventually, the forward
payment will change the status from `offered` to `settled`;
- If payment fails locally(like failing to resolve locally) or the
corresponding htlc with next hop fails(like htlc timeout), we will
set the status as `local_failed`. `local_failed` may be set before
setting `offered` or after setting `offered`. In fact, from the
time we receive the htlc of the previous hop, all we can know the
cause of the failure is treated as `local_failed`. `local_failed`
only occuors locally or happens in the htlc between us and next hop;
- If `local_failed` is set before `offered`, this
means we just received htlc from the previous hop and haven't
generate htlc for next hop. In this case, the json of `forward_event`
sets the fields of `out_msatoshi`, `out_msat`,`fee` and `out_channel`
as 0;
- Note: In fact, for this case we may be not sure if this incoming
htlc represents a pay to us or a payment we need to forward.
2019-09-21 01:52:10 +01:00
We just simply treat all incoming failed to resolve as
`local_failed`.
- Only in `local_failed` case, json includes `failcode` and
`failreason` fields;
- `failed` means the payment forwarded by us fails in the
latter hops, and the failure isn't related to us, so we aren't
accessed to the fail reason. `failed` must be set after
`offered`.
- `failed` case doesn't include `failcode` and `failreason`
fields;
- `received_time` means when we received the htlc of this payment from
the previous peer. It will be contained into all status case;
- `resolved_time` means when the htlc of this payment between us and the
next peer was resolved. The resolved result may success or fail, so
only `settled` and `failed` case contain `resolved_time`;
- The `failcode` and `failreason` are defined in [BOLT 4][bolt4-failure-codes].
### `sendpay_success`
A notification for topic `sendpay_success` is sent every time a sendpay
succeeds (with `complete` status). The json is the same as the return value of
the commands `sendpay`/`waitsendpay` when these commands succeed.
```json
{
2019-09-21 01:52:10 +01:00
"sendpay_success": {
"id": 1,
"payment_hash": "5c85bf402b87d4860f4a728e2e58a2418bda92cd7aea0ce494f11670cfbfb206",
"destination": "035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d",
"msatoshi": 100000000,
"amount_msat": "100000000msat",
"msatoshi_sent": 100001001,
"amount_sent_msat": "100001001msat",
"created_at": 1561390572,
"status": "complete",
"payment_preimage": "9540d98095fd7f37687ebb7759e733934234d4f934e34433d4998a37de3733ee"
}
}
```
`sendpay` doesn't wait for the result of sendpay and `waitsendpay`
returns the result of sendpay in specified time or timeout, but
`sendpay_success` will always return the result anytime when sendpay
successes if is was subscribed.
### `sendpay_failure`
A notification for topic `sendpay_failure` is sent every time a sendpay
completes with `failed` status. The JSON is same as the return value of
the commands `sendpay`/`waitsendpay` when these commands fail.
```json
{
"sendpay_failure": {
2019-09-21 01:52:10 +01:00
"code": 204,
"message": "failed: WIRE_UNKNOWN_NEXT_PEER (reply from remote)",
"data": {
"id": 2,
"payment_hash": "9036e3bdbd2515f1e653cb9f22f8e4c49b73aa2c36e937c926f43e33b8db8851",
"destination": "035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d",
"msatoshi": 100000000,
"amount_msat": "100000000msat",
"msatoshi_sent": 100001001,
"amount_sent_msat": "100001001msat",
"created_at": 1561395134,
"status": "failed",
"erring_index": 1,
"failcode": 16394,
"failcodename": "WIRE_UNKNOWN_NEXT_PEER",
"erring_node": "022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59",
"erring_channel": "103x2x1",
"erring_direction": 0
}
}
}
```
`sendpay` doesn't wait for the result of sendpay and `waitsendpay`
returns the result of sendpay in specified time or timeout, but
`sendpay_failure` will always return the result anytime when sendpay
fails if is was subscribed.
## Hooks
Hooks allow a plugin to define custom behavior for `lightningd`
without having to modify the c-lightning source code itself. A plugin
declares that it'd like to be consulted on what to do next for certain
events in the daemon. A hook can then decide how `lightningd` should
react to the given event.
The call semantics of the hooks, i.e., when and how hooks are called, depend
on the hook type. Most hooks are currently set to `single`-mode. In this mode
only a single plugin can register the hook, and that plugin will get called
for each event of that type. If a second plugin attempts to register the hook
it gets killed and a corresponding log entry will be added to the logs. In
`chain`-mode multiple plugins can register for the hook type and they are
called sequentially if a matching event is triggered. Each plugin can then
handle the event or defer by returning a `continue` result like the following:
```json
{
"result": "continue"
}
```
The remainder of the response is ignored and if there are any more plugins
that have registered the hook the next one gets called. If there are no more
plugins then the internal handling is resumed as if no hook had been
called. Any other result returned by a plugin is considered an exit from the
chain. Upon exit no more plugin hooks are called for the current event, and
the result is executed. Unless otherwise stated all hooks are `single`-mode.
Hooks and notifications are very similar, however there are a few
key differences:
- Notifications are asynchronous, i.e., `lightningd` will send the
notifications but not wait for the plugin to process them. Hooks on
the other hand are synchronous, `lightningd` cannot finish
processing the event until the plugin has returned.
- Any number of plugins can subscribe to a notification topic and get
notified in parallel, however only one plugin may register for
`single`-mode hook types, and in all cases only one plugin may return a
non-`continue` response. This avoids having multiple contradictory
responses.
Hooks are considered to be an advanced feature due to the fact that
`lightningd` relies on the plugin to tell it what to do next. Use them
carefully, and make sure your plugins always return a valid response
to any hook invocation.
As a convention, for all hooks, returning the object
`{ "result" : "continue" }` results in `lightningd` behaving exactly as if
no plugin is registered on the hook.
### `peer_connected`
This hook is called whenever a peer has connected and successfully completed
the cryptographic handshake. The parameters have the following structure if there is a channel with the peer:
```json
{
"peer": {
2019-09-21 01:52:10 +01:00
"id": "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f",
"addr": "34.239.230.56:9735",
"features": ""
}
}
```
The hook is sparse on purpose, since the plugin can use the JSON-RPC
`listpeers` command to get additional details should they be required. The
`addr` field shows the address that we are connected to ourselves, not the
gossiped list of known addresses. In particular this means that the port for
incoming connections is an ephemeral port, that may not be available for
reconnections.
The returned result must contain a `result` member which is either
the string `disconnect` or `continue`. If `disconnect` and
there's a member `error_message`, that member is sent to the peer
before disconnection.
### `db_write`
This hook is called whenever a change is about to be committed to the database.
It is currently extremely restricted:
1. a plugin registering for this hook should not perform anything that may cause
a db operation in response (pretty much, anything but logging).
2. a plugin registering for this hook should not register for other hooks or
commands, as these may become intermingled and break rule #1.
3. the hook will be called before your plugin is initialized!
This hook, unlike all the other hooks, is also strongly synchronous:
`lightningd` will stop almost all the other processing until this
hook responds.
```json
{
"data_version": 42,
2019-09-21 01:52:10 +01:00
"writes": [
"PRAGMA foreign_keys = ON"
]
}
```
This hook is intended for creating continuous backups.
The intent is that your backup plugin maintains three
pieces of information (possibly in separate files):
(1) a snapshot of the database, (2) a log of database queries
that will bring that snapshot up-to-date, and (3) the previous
`data_version`.
`data_version` is an unsigned 32-bit number that will always
increment by 1 each time `db_write` is called.
Note that this will wrap around on the limit of 32-bit numbers.
`writes` is an array of strings, each string being a database query
that modifies the database.
If the `data_version` above is validated correctly, then you can
simply append this to the log of database queries.
Your plugin **MUST** validate the `data_version`.
It **MUST** keep track of the previous `data_version` it got,
and:
1. If the new `data_version` is ***exactly*** one higher than
the previous, then this is the ideal case and nothing bad
happened and we should save this and continue.
2. If the new `data_version` is ***exactly*** the same value
as the previous, then the previous set of queries was not
committed.
Your plugin **MAY** overwrite the previous set of queries with
the current set, or it **MAY** overwrite its entire backup
with a new snapshot of the database and the current `writes`
array (treating this case as if `data_version` were two or
more higher than the previous).
3. If the new `data_version` is ***less than*** the previous,
your plugin **MUST** halt and catch fire, and have the
operator inspect what exactly happend here.
4. Otherwise, some queries were lost and your plugin **SHOULD**
recover by creating a new snapshot of the database: copy the
database file, back up the given `writes` array, then delete
(or atomically `rename` if in a POSIX filesystem) the previous
backups of the database and SQL statements, or you **MAY**
fail the hook to abort `lightningd`.
The "rolling up" of the database could be done periodically as well
if the log of SQL statements has grown large.
Any response other than `{"result": "continue"}` will cause lightningd
to error without
committing to the database!
This is the expected way to halt and catch fire.
### `invoice_payment`
This hook is called whenever a valid payment for an unpaid invoice has arrived.
```json
{
"payment": {
2019-09-21 01:52:10 +01:00
"label": "unique-label-for-invoice",
"preimage": "0000000000000000000000000000000000000000000000000000000000000000",
"msat": "10000msat"
}
}
```
The hook is sparse on purpose, since the plugin can use the JSON-RPC
`listinvoices` command to get additional details about this invoice.
It can return a `failure_message` field as defined for final
nodes in [BOLT 4][bolt4-failure-messages], a `result` field with the string
`reject` to fail it with `incorrect_or_unknown_payment_details`, or a
`result` field with the string `continue` to accept the payment.
### `openchannel`
This hook is called whenever a remote peer tries to fund a channel to us,
and it has passed basic sanity checks:
```json
{
"openchannel": {
2019-09-21 01:52:10 +01:00
"id": "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f",
"funding_satoshis": "100000000msat",
"push_msat": "0msat",
"dust_limit_satoshis": "546000msat",
"max_htlc_value_in_flight_msat": "18446744073709551615msat",
"channel_reserve_satoshis": "1000000msat",
"htlc_minimum_msat": "0msat",
"feerate_per_kw": 7500,
"to_self_delay": 5,
"max_accepted_htlcs": 483,
"channel_flags": 1
}
}
```
There may be additional fields, including `shutdown_scriptpubkey` and
a hex-string. You can see the definitions of these fields in [BOLT 2's description of the open_channel message][bolt2-open-channel].
The returned result must contain a `result` member which is either
the string `reject` or `continue`. If `reject` and
there's a member `error_message`, that member is sent to the peer
before disconnection.
For a 'continue'd result, you can also include a `close_to` address,
which will be used as the output address for a mutual close transaction.
e.g.
```json
{
"result": "continue",
"close_to": "bc1qlq8srqnz64wgklmqvurv7qnr4rvtq2u96hhfg2"
}
```
Note that `close_to` must be a valid address for the current chain; an invalid address will cause the node to exit with an error.
### `htlc_accepted`
The `htlc_accepted` hook is called whenever an incoming HTLC is accepted, and
its result determines how `lightningd` should treat that HTLC.
The payload of the hook call has the following format:
```json
{
"onion": {
"payload": "",
"type": "legacy",
"short_channel_id": "1x2x3",
"forward_amount": "42msat",
"outgoing_cltv_value": 500014
},
"next_onion": "[1365bytes of serialized onion]",
"shared_secret": "0000000000000000000000000000000000000000000000000000000000000000",
"htlc": {
"amount": "43msat",
"cltv_expiry": 500028,
"cltv_expiry_relative": 10,
"payment_hash": "0000000000000000000000000000000000000000000000000000000000000000"
}
}
```
For detailed information about each field please refer to [BOLT 04 of the specification][bolt4], the following is just a brief summary:
- `onion.payload` contains the unparsed payload that was sent to us from the
sender of the payment.
- `onion.type` is `legacy` for realm 0 payments, `tlv` for realm > 1.
- `short_channel_id` determines the channel that the sender is hinting
should be used next. Not present if we're the final destination.
- `forward_amount` is the amount we should be forwarding to the next hop,
and should match the incoming funds in case we are the recipient.
- `outgoing_cltv_value` determines what the CLTV value for the HTLC that we
forward to the next hop should be.
- `total_msat` specifies the total amount to pay, if present.
- `payment_secret` specifies the payment secret (which the payer should have obtained from the invoice), if present.
- `next_onion` is the fully processed onion that we should be sending to the
next hop as part of the outgoing HTLC. Processed in this case means that we
took the incoming onion, decrypted it, extracted the payload destined for
us, and serialized the resulting onion again.
- `shared_secret` is the shared secret we used to decrypt the incoming
onion. It is shared with the sender that constructed the onion.
- `htlc`:
- `amount` is the amount that we received with the HTLC. This amount minus
the `forward_amount` is the fee that will stay with us.
- `cltv_expiry` determines when the HTLC reverts back to the
sender. `cltv_expiry` minus `outgoing_cltv_expiry` should be equal or
larger than our `cltv_delta` setting.
- `cltv_expiry_relative` hints how much time we still have to claim the
HTLC. It is the `cltv_expiry` minus the current `blockheight` and is
passed along mainly to avoid the plugin having to look up the current
blockheight.
- `payment_hash` is the hash whose `payment_preimage` will unlock the funds
and allow us to claim the HTLC.
The hook response must have one of the following formats:
```json
{
"result": "continue"
}
```
This means that the plugin does not want to do anything special and
`lightningd` should continue processing it normally, i.e., resolve the payment
if we're the recipient, or attempt to forward it otherwise. Notice that the
usual checks such as sufficient fees and CLTV deltas are still enforced.
```json
{
"result": "fail",
"failure_message": "2002"
}
```
`fail` will tell `lightningd` to fail the HTLC with a given hex-encoded
`failure_message` (please refer to the [spec][bolt4-failure-messages] for details: `incorrect_or_unknown_payment_details` is the most common).
```json
{
"result": "resolve",
"payment_key": "0000000000000000000000000000000000000000000000000000000000000000"
}
```
`resolve` instructs `lightningd` to claim the HTLC by providing the preimage
matching the `payment_hash` presented in the call. Notice that the plugin must
ensure that the `payment_key` really matches the `payment_hash` since
`lightningd` will not check and the wrong value could result in the channel
being closed.
Warning: `lightningd` will replay the HTLCs for which it doesn't have a final
verdict during startup. This means that, if the plugin response wasn't
processed before the HTLC was forwarded, failed, or resolved, then the plugin
may see the same HTLC again during startup. It is therefore paramount that the
plugin is idempotent if it talks to an external system.
The `htlc_accepted` hook is a chained hook, i.e., multiple plugins can
register it, and they will be called in the order they were registered in
until the first plugin return a result that is not `{"result": "continue"}`,
after which the event is considered to be handled. After the event has been
handled the remaining plugins will be skipped.
2019-11-03 12:28:43 +00:00
### `rpc_command`
2019-11-03 12:28:43 +00:00
The `rpc_command` hook allows a plugin to take over any RPC command. It sends
the received JSON-RPC request to the registered plugin,
```json
{
"rpc_command": {
"id": 3,
2019-11-03 12:28:43 +00:00
"method": "method_name",
"params": {
"param_1": [],
"param_2": {},
"param_n": "",
}
}
}
```
which can in turn:
Let `lightningd` execute the command with
```json
{
"result" : "continue"
2019-11-03 12:28:43 +00:00
}
```
Replace the request made to `lightningd`:
```json
{
"replace": {
"id": 3,
2019-11-03 12:28:43 +00:00
"method": "method_name",
"params": {
"param_1": [],
"param_2": {},
"param_n": "",
}
}
}
```
Return a custom response to the request sender:
```json
{
"return": {
"result": {
}
}
}
```
Return a custom error to the request sender:
```json
{
"return": {
"error": {
}
}
}
```
### `custommsg`
The `custommsg` plugin hook is the receiving counterpart to the
[`dev-sendcustommsg`][sendcustommsg] RPC method and allows plugins to handle
messages that are not handled internally. The goal of these two components is
to allow the implementation of custom protocols or prototypes on top of a
c-lightning node, without having to change the node's implementation itself.
The payload for a call follows this format:
```json
{
"peer_id": "02df5ffe895c778e10f7742a6c5b8a0cefbe9465df58b92fadeb883752c8107c8f",
"message": "1337ffffffff"
}
```
This payload would have been sent by the peer with the `node_id` matching
`peer_id`, and the message has type `0x1337` and contents `ffffffff`. Notice
that the messages are currently limited to odd-numbered types and must not
match a type that is handled internally by c-lightning. These limitations are
in place in order to avoid conflicts with the internal state tracking, and
avoiding disconnections or channel closures, since odd-numbered message can be
ignored by nodes (see ["it's ok to be odd" in the specification][oddok] for
details). The plugin must implement the parsing of the message, including the
type prefix, since c-lightning does not know how to parse the message.
The result for this hook is currently being discarded. For future uses of the
result we suggest just returning `{'result': 'continue'}`.
This will ensure backward
compatibility should the semantics be changed in future.
[jsonrpc-spec]: https://www.jsonrpc.org/specification
[jsonrpc-notification-spec]: https://www.jsonrpc.org/specification#notification
[bolt4]: https://github.com/lightningnetwork/lightning-rfc/blob/master/04-onion-routing.md
[bolt4-failure-messages]: https://github.com/lightningnetwork/lightning-rfc/blob/master/04-onion-routing.md#failure-messages
[bolt2-open-channel]: https://github.com/lightningnetwork/lightning-rfc/blob/master/02-peer-protocol.md#the-open_channel-message
[sendcustommsg]: lightning-dev-sendcustommsg.7.html
[oddok]: https://github.com/lightningnetwork/lightning-rfc/blob/master/00-introduction.md#its-ok-to-be-odd
[spec]: [https://github.com/lightningnetwork/lightning-rfc]
[bolt9]: https://github.com/lightningnetwork/lightning-rfc/blob/master/09-features.md