From 1eef0e59b3e005dcf9e65a2c902c5bb71834aa33 Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Fri, 13 Dec 2024 14:32:30 +0100 Subject: [PATCH] Add bLIP 50: LSP Spec Transport Layer (#52) The LSPS0 specification defines a protocol that is used for communcations between LSP nodes and their clients. To this end it utilizes a JSON-RPC format spoken over BOLT8 peer-to-peer messages. The given protocol has been previously stabilized by the LSP Spec group and is live in production with several LSP and client implementations today. As previously discussed on multiple occasions, the LSP Spec group is however moving to a bLIP-centric process, which is why we 'upstream' these previously-stabilized specifications here. --- README.md | 24 +- blip-0002.md | 16 +- blip-0050.md | 1091 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1112 insertions(+), 19 deletions(-) create mode 100644 blip-0050.md diff --git a/README.md b/README.md index a8cc99e..9d10726 100644 --- a/README.md +++ b/README.md @@ -17,15 +17,15 @@ published here. For more detail on the process, please read [bLIP-0001](./blip-0001.md) and [bLIP-0002](./blip-0002.md). -| Number | Title | Author | Status | -|--------------------------|--------------------------------|-----------------------------|--------| -| [1](./blip-0001.md) | bLIP Process | Ryan Gentry | Active | -| [2](./blip-0002.md) | Reserved Values | Bastien Teinturier | Active | -| [3](./blip-0003.md) | Keysend | Valentine Wallace | Active | -| [4](./blip-0004.md) | Experimental Endorsement | Carla Kirk-Cohen | Active | -| [10](./blip-0010.md) | Podcasting 2.0 | Satoshis Stream | Active | -| [11](./blip-0011.md) | NameDesc | Hampus Sjöberg | Active | -| [17](./blip-0017.md) | Hosted Channels | Anton Kumaigorodskiy | Active | -| [25](./blip-0025.md) | Forward less than onion value | Valentine Wallace | Active | -| [32](./blip-0032.md) | Onion Message DNS Resolution | Matt Corallo | Active | - +| Number | Title | Author | Status | +|--------------------------|---------------------------------|-----------------------------|--------| +| [1](./blip-0001.md) | bLIP Process | Ryan Gentry | Active | +| [2](./blip-0002.md) | Reserved Values | Bastien Teinturier | Active | +| [3](./blip-0003.md) | Keysend | Valentine Wallace | Active | +| [4](./blip-0004.md) | Experimental Endorsement | Carla Kirk-Cohen | Active | +| [10](./blip-0010.md) | Podcasting 2.0 | Satoshis Stream | Active | +| [11](./blip-0011.md) | NameDesc | Hampus Sjöberg | Active | +| [17](./blip-0017.md) | Hosted Channels | Anton Kumaigorodskiy | Active | +| [25](./blip-0025.md) | Forward less than onion value | Valentine Wallace | Active | +| [32](./blip-0032.md) | Onion Message DNS Resolution | Matt Corallo | Active | +| [50](./blip-0050.md) | LSPS0: LSP Spec Transport Layer | ZmnSCPxj jxPCSnmZ | Active | diff --git a/blip-0002.md b/blip-0002.md index 1db70e4..f0e2937 100644 --- a/blip-0002.md +++ b/blip-0002.md @@ -45,13 +45,14 @@ Custom feature bits used in the `I` [Bolt 11](https://github.com/lightning/bolts bLIPs may reserve feature bits by adding them to the following table: -| Bits | Name | Description | Context | Dependencies | Link | -|---------|-----------------------|------------------------------------------------------------|---------|-------------------------|---------------------------| -| 54/55 | `keysend` | A form of spontaneous payment | N | `var_onion_optin` | [bLIP 3](./blip-0003.md) | -| 256/257 | `hosted_channels` | This node accepts requests for hosted channels | IN | | [bLIP 17](./blip-0017.md) | -| 258/259 | `dns_resolver` | This node accepts DNSSEC proof requests | N | | [bLIP 32](./blip-0032.md) | -| 260/261 | `htlc_endorsement` | This node forwards experimental htlc endorsement signals | N | | [bLIP 4](./blip-004.md) | -| 262/263 | `bolt11_blinded_path` | This invoice may contain a new blinded path tagged field | I | `option_route_blinding` | [bLIP 39](./blip-0039.md) | +| Bits | Name | Description | Context | Dependencies | Link | +|---------|------------------------|------------------------------------------------------------|---------|-------------------------|---------------------------| +| 54/55 | `keysend` | A form of spontaneous payment | N | `var_onion_optin` | [bLIP 3](./blip-0003.md) | +| 256/257 | `hosted_channels` | This node accepts requests for hosted channels | IN | | [bLIP 17](./blip-0017.md) | +| 258/259 | `dns_resolver` | This node accepts DNSSEC proof requests | N | | [bLIP 32](./blip-0032.md) | +| 260/261 | `htlc_endorsement` | This node forwards experimental htlc endorsement signals | N | | [bLIP 4](./blip-004.md) | +| 262/263 | `bolt11_blinded_path` | This invoice may contain a new blinded path tagged field | I | `option_route_blinding` | [bLIP 39](./blip-0039.md) | +| 729 | `option_supports_lsps` | This node supports LSPS protocol(s) | IN | | [bLIP 50](./blip-0050.md) | ### Messages @@ -63,6 +64,7 @@ bLIPs may create new messages and reserve their type in the following table: | Type | Name | Link | | ------- | ------------------------------- | -------------------------- | +| 37913 | `lsps0_message_id` | [bLIP 50](./blip-0050.md) | | 65535 | `invoke_hosted_channel` | [bLIP 17](./blip-0017.md) | | 65533 | `init_hosted_channel` | [bLIP 17](./blip-0017.md) | | 65531 | `last_cross_signed_state` | [bLIP 17](./blip-0017.md) | diff --git a/blip-0050.md b/blip-0050.md new file mode 100644 index 0000000..7a731b1 --- /dev/null +++ b/blip-0050.md @@ -0,0 +1,1091 @@ +``` +bLIP: 50 +Title: LSPS0: LSP Spec Transport Layer +Status: Active +Author: ZmnSCPxj jxPCSnmZ +Created: 2024-12-02 +License: MIT +``` + +## Abstract + +This bLIP defines a protocol that is used for communcations between LSP nodes +and their clients. To this end, it utilizes a JSON-RPC format spoken over BOLT8 +peer-to-peer messages to allow clients to request services from the LSP. + +## Copyright + +This bLIP is licensed under the MIT license. + +## Reference Implementation + +A reference implementation of this protocol can be found as part of the +[`lightning-liquidity`][] crate. + +[`lightning-liquidity`]: https://github.com/lightningdevkit/rust-lightning/tree/main/lightning-liquidity + +### Actors + +The 'LSP' is the API provider, and acts as the server. +The 'client' is the API consumer. + +## Protocol + +### Lightning Peer-To-Peer Transport + +The Lightning Network [BOLT8][] specification describes the transport +layer for Lightning Network peer-to-peer messages. + +[BOLT8]: https://github.com/lightning/bolts/blob/f7dcc32694b8cd4f3a1768b904f58cb177168f29/08-transport.md + +Lightning Network BOLT8 describes messages as having two components: + +* A 16-bit message ID (encoded in big-endian). +* A variable-length message payload (the remainder of the message). + +Access to the endpoints defined in other LSPS specifications is provided +via messages using the [BOLT8][] protocol. + +> **Rationale** All clients and LSPs are expected to be Lightning +> Network participants, and need to somehow talk using the BOLT8 +> protocol. +> Thus, this does not introduce an additional dependency for the +> client or any LSP, the way an HTTP(S) or gRPC protocol would pull +> in additional dependencies. +> +> During development of this specification, onion messages were +> proposed. +> As a client needs to connect to the LSP node and manage channels +> with that node anyway, and LSP nodes want to be easily contactable +> from IPv4, IPv6, DNS, or TorV3 contact points, the ability of +> onion messages to send to a remote Lightning Network node (that +> might not be directly contactable) was deemed unnecessary for +> most client-LSP communication needs. + +All LSPS messages MUST use the [BOLT8][] message ID 37913 +(`lsps0_message_id`). + +> **Rationale** We indicate a single message ID as this reduces +> the "footprint" of all LSPS specifications to only that single +> message ID, increasing the probability that other protocols +> using the Lightning BOLT8 peer-to-peer transport will be +> compatible with the LSPS specifications. +> The BOLT8 message ID 37913 is odd in order to comply with the ["it's +> OK to be odd"][] rule, and is in the 32768 to 65535 range reserved +> for extensions of the BOLT protocol. +> Some implementations, such as Core Lightning, only expose +> odd-numbered messages to custom message handlers, while others, +> such as LDK, only expose 32768 and above. +> The message ID was otherwise selected randomly and has no +> special meaning. + +["it's OK to be odd"]: https://github.com/lightning/bolts/blob/master/00-introduction.md#its-ok-to-be-odd + +#### Message Payload Format + +The BOLT8 message payload contains the UTF-8 encoding of a +complete JSON object. + +The JSON object embedded in the message payload is defined +by the [JSON-RPC 2.0][] protocol. + +[JSON-RPC 2.0]: https://www.jsonrpc.org/specification + +If a client or LSP receives a BOLT8 message with message ID +37913, it MUST perform the checks below. +If any of these checks fail, then the incoming message has +failed with a "bad message format" error. + +* The payload MUST parse as a single complete UTF-8 encoded + JSON object (i.e. a JSON key-value store or dictionary), + with optional leading or trailing whitespaces (space, tab, + line feed, carriage return). + * For example, "` { } `" would pass this check, but + "`{`" and "` [ ] `" would not. +* The payload MUST NOT parse as more than a single complete + JSON object. + * For example, "` { } { `" and "` { } { }`" would not pass + this check. +* The payload MUST NOT contain any 0 bytes. +* If the client, the received payload MUST parse to a JSON + object that is a [JSON-RPC 2.0][] response or notification + object. +* If the LSP, the received payload MUST parse to a JSON + object that is a [JSON-RPC 2.0][] request. + +In case of a "bad message format" error, the client: + +* MUST ignore the message. +* SHOULD log this as an unusual event. +* SHOULD NOT send further BOLT8 messages with ID 37913 to the + peer on this connection session. + * SHOULD re-attempt this on reconnection. + +In case of a "bad message format" error, the LSP: + +* MUST respond with a [JSON-RPC 2.0][] error response with + the reason "parse error" (`code` = `-32700`, `id` = `null`). +* MUST ignore the message. +* SHOULD log this as an unusual event. + +> **Rationale** Requiring a single complete JSON object +> simplifies handling of messages, so that a single message +> maps to a single request or response. +> C uses the NUL character as a string terminator and thus +> embedded 0 bytes may cause problems in implementations +> that pass the JSON string to C code. +> Conversely, we do not require the payload to be terminated +> by a 0 byte / NUL character as it is unnecessary in many +> modern non-C-based languages; C code can copy the buffer +> and append the NUL character if necessary, as the payload +> size is known. +> UTF-8 spends 1 byte per JSON representation character for +> characters in the ASCII range, and we expect most data sent +> over this protocol to fit in the ASCII range. +> +> An LSP sending a bad message is a serious bug that affects +> all clients of the LSP, and presumably the LSP operator has +> to restart the node in order to fix the bug. +> Thus, clients should not re-attempt sending any requests to +> the LSP until the client connects to it again, as the +> reconnection may signal that the LSP was restarted, which +> may signal that the LSP has had this serious bug fixed. + +The LSP acts as a JSON-RPC 2.0 server, while the client acts +as a JSON-RPC 2.0 client. + +> **Rationale** JSON is a simple format that is easy to +> extend and is extensively supported. +> The Lightning Network peer-to-peer transport protocol in +> BOLT8 is not inherently a request-response protocol like +> HTTP(S) or gRPC, and JSON-RPC 2.0 describes a +> JSON-based protocol that builds a request-response +> protocol from a simple tunnel; BOLT8 message ID 37913 acts as +> that tunnel. +> JSON-RPC 2.0 is simple to implement, and this specification +> describes an even simpler subset of JSON-RPC 2.0. +> Although BOLT8 messages are limited to payloads of 65533 +> bytes, it is expected that both requests and responses +> would be far below that limit, and thus the ability to +> "cut" a large object across multiple messages, the +> use of a compression algorithm, and the use of a binary +> format instead of JSON, are all considered unnecessary. +> In particular, we expect most reasonable requests and +> responses to be less than 1000 bytes, and any compression +> would not significantly reduce the number of MTUs +> (generally about 1400 bytes per MTU) that lower network +> layers would need to transport. + +> Moreover, the lack of compression greatly simplifies +> implementation, testing, interoperability, and dependencies. +> Compression is potentially vulnerable to [zip bomb][]s, a +> short piece of compressed data that expands to several +> gigabytes or terabytes of uncompressed data. +> We would need to impose *some* limit on the uncompressed +> text, and that limit might as well be the 65533-byte +> limit of BOLT8 message payloads. + +[zip bomb]: https://en.wikipedia.org/wiki/Zip_bomb + +The client: + +* MUST send single complete JSON-RPC 2.0 request objects, + UTF-8-encoded, as payloads for BOLT8 message ID 37913. +* MUST NOT send JSON-RPC 2.0 notification objects + (i.e. every object it sends must have an `id` field). +* MUST NOT use by-position parameter structures, and MUST + use by-name parameter structures. +* MUST NOT batch multiple JSON-RPC 2.0 request objects in + an array as described in the ["Batch"](https://www.jsonrpc.org/specification#batch) + section of the JSON-RPC 2.0 specification. + +> **Rationale** By disallowing by-position parameter +> structures, other LSPS specifications need only to define +> parameter names and not some canonical order of parameters +> for by-position use. +> Having to handle only by-name parameter structures also +> simplifies the LSP code, as it does not have to check +> whether the `params` value is an array or a dictionary, +> and to separately map array elements to `params` keys. +> Batched requests require batched responses according to +> JSON-RPC 2.0, and it may be simpler for LSPs to handle +> unrelated LSPS methods separately without requiring +> re-batching of the responses; it gives a simple "one +> message is one request / response" rule. + +The LSP: + +* MUST send either of the below as payloads for BOLT8 message + ID 37913: + * a single complete JSON-RPC 2.0 response object, UTF-8-encoded. + * a single complete JSON-RPC 2.0 notification object (i.e. an + object without `id` but with `method`, `params`, and `jsonrpc` + fields). + * MUST use by-name parameter structures for notifications. +* MUST respect the [JSON-RPC 2.0 standard error codes][]. +* SHOULD NOT send BOLT8 message ID 37913 unless the peer had + already sent a BOLT8 message ID 37913, possibly in a past + connection session. +* MAY send responses in an order different from the order in + which the client sent the requests. + +[JSON-RPC 2.0 standard error codes]: https://www.jsonrpc.org/specification#error_object + +> **Rationale** The peer sending BOLT8 message ID 37913 is an +> indicator that it understands the LSPS0 protocol and wishes +> to act as a client. +> If the peer does not send it, then it is not an LSPS client +> and the LSP has no reason to send BOLT8 message ID 37913. +> Notifications allow the LSP to signal events to the client, +> provided the client has already previously signalled a +> willingness to receive such events by calling some +> LSPS-defined method to enable such events. +> For example, an LSPS might specify a method that enables +> the client to be signalled by an LSPS-specified notification, +> whenever the client could have received a payment but lacks +> the inbound liquidity for it. + +#### LSPS Method And Notification Specifications + +Other LSPS specifications: + +* MUST indicate `method` names for each client-callable API + endpoint and each LSP-initiated notification, as well as + the key names of the `params` for each `method`, and the + meanings of each parameter. + * `method` names MUST be in snake_case, i.e. words are lower + case and separated by `_` characters. + * MUST prefix `method` names with `lsps` followed by the LSPS + number followed by `.`, e.g. `lsps999.do_this` for a client + request or `lsps999.that_happened` for an LSP notification. + * MUST also describe the possible error `code`s for each API + endpoint, together with any `data` (which MUST be an object + (dictionary)) for that error code, if there should be a + `data` field. + * MAY elect to not define a `data` object for an error + `code`, in which case clients MUST NOT expect a `data` + field. + * MUST define `result` values for its API endpoints that + are objects (dictionaries), and MUST define keys of the + response. + +> **Rationale** A prefix ensures that method names do not +> conflict across LSPS specifications, and creates a convention +> that allows non-standard extensions to define their own prefix. +> A `result` dictionary allows for later revisions of an +> LSPS specification to seamlessly add new keys in the response. + +An LSP MAY return additional keys in the `response` values +that are not defined in the relevant LSPS specification. +Clients conversely MUST ignore unrecognized keys. + +> **Rationale** This allows later revisions of an LSPS specification +> to seamlessly add new keys to the response while maintaining +> backwards compatibility with older clients that do not know the +> later revision with additional keys. +> Later revisions can make parameters backwards compatible by only +> adding optional new parameters, which when absent causes the +> API endpoint to behave identically to older revisions. + +Other LSPS specifications MUST be designed to be resilient +against responses and notifications being lost on the way +from LSP to client. +The LSP may believe it has delivered the message, but the +IP packet containing the message may not have reached the +client before the client suffers some unexpected crash, or +the client may have been parsing and doing early processing +of the message before being able to persist the data related +to the response or notification. +Other LSPS specifications MUST: + +* require that the LSP time-bound any information that the LSP + has to keep before the LSP is compensated. +* require or support that the client store as much state as + possible, and include some kind of signature or MAC that the + LSP can use to recognize that it issued that state, without + the LSP having to remember that state. +* describe level-triggered and not edge-triggered + notifications (i.e. notifications should mean "client, you + still have some X you have to handle" and not "client, a + new X was added / an X was removed / an X was changed"). +* require that LSPs MUST send notifications whenever a change + occurs in the items the client has to handle (send on edge), + but also send notifications on connecting with the client + if the level-trigger is still true on reconnection (i.e. + also check the level on reconnection and re-send it). +* describe any queues (i.e. for items the client has to + handle, such as HTLCs that cannot be delivered to the client + via the normal BOLT messages yet, due to lack of a viable + channel) as having separate "peek at first item" and "remove + first item then peek at next item" APIs, while ensuring that + each item in a queue has a unique identifier that the client + can use to check if the item has already been read but the + "remove first item" call for that item was not able to be + delivered previously. + +> **Rationale** Clients may crash due to operator mistakes +> or unrelated reasons, and a client may be an attacker and +> not a legitimate paying client. +> Thus, a client may initiate some flow or process that +> requires multiple communication rounds with the LSP, and +> then abort partway through due to a crash or a deliberate +> attempt to waste LSP resources. +> The network between the client and the LSP may also be +> unreliable, so notifications may be lost, so the state +> of whatever is being notified should be re-sent on +> reconnection. +> In particular, notifications are not ever explicitly +> acknowledged by the client on the transport layer +> level. +> Queues are particularly good for level-triggered +> notification, with the "level" being "is the queue +> not empty?". +> Processing of one item in this queue can take time on +> the client side, during which the client may crash or the +> connection interrupted, thus the first item should be +> retained on the LSP side, as the client may not have been +> able to persist it after it received the response from +> the "peek at first item" call. +> The client needs to be able to detect if it already +> completed processing of the queue top item, and combining +> the "remove first item then peek at next item" into a +> single call reduces the round trips needed to handle each +> completely-processed item while still retaining the item +> currently being processed on the LSP side in case of a +> client crash during processing. + +#### Error Handling + +In general, the client, the LSP, and any LSPS building on top of LSPS0 MUST respect +[JSON-RPC error codes][]. +This document extends the error codes by describing edge cases +combining [JSON-RPC 2.0][] with [BOLT8][]. Any error code like `-32603 Internal error` is still valid +even though not mentioned explicitly. + +[JSON-RPC error codes]: https://www.jsonrpc.org/specification#error_object + +--- + +JSON-RPC 2.0 `error`s include a `message` field which is a +human-readable error message. + +Clients MUST carefully filter error messages of possibly +problematic characters, such as NUL characters, `<`, newlines, +or ASCII control characters, which may cause problems when +displaying in a dynamic context such as an HTML webpage, a +TTY, or C string processing functions. + +Clients SHOULD NOT directly expose error `message`s to users +unless users have enabled some kind of "advanced" mode or +"developer" mode. +Clients MAY write them in a log that is accessible to "advanced" +or "developer" mode users. + +Clients SHOULD generate their own messages based on the +error `code`, for display to the user, possibly integrating +information in the optional `data` object if the error `code` +defines it. +If the client does not recognize the error `code`, or is +expecting a field in the `data` object that is not present, it +SHOULD indicate this as an "unrecognized" error to the user. + +> **Rationale** LSPs might write incorrect or misleading +> human-readable error messages, and users might report such +> error messages as bugs to the client developers, since the +> user-visible source of the error message would be the client; +> it is thus better if the client writes its own error +> messages that it can change based on user feedback. +> Later revisions of some LSPS spec may introduce new error codes, +> or the specification may be incomplete and actual development +> shows that some unspecified error could possibly occur, in +> which case the human-readable `error` field could contain a +> description of this error, which developers of clients can +> then use to help guide the evolution of the specification, or +> to comply with later revisions of the LSPS spec. + +##### `-32602` Invalid Parameters Error + +An LSP that sends back an invalid parameter error with +`code = -32602` MUST include a `data` object in the `error` +response. + +This `data` object MUST contain at least the field `unrecognized`, +a JSON array of strings, representing an unordered set of +parameter names. +These are the parameter names that are not recognized by the LSP as +valid for the RPC method. + +For example, suppose the client sent this request: + +```JSON +{ + "jsonrpc": "2.0", + "method": "example.method_name", + "params": { + "future_feature1_param": "value1", + "future_feature2_param": "value2" + }, + "id": "42" +} +``` + +Suppose the LSP recognizes `example.method_name` as a valid +method, and recognizes `future_feature2_param` as a valid +parameter of that method, but does not recognize +`future_feature1_param`. +In that case, the LSP would respond with: + +```JSON +{ + "jsonrpc": "2.0", + "error": { + "code": -32602, + "message": "Invalid params", + "data": { + "unrecognized": ["future_feature1_param"] + } + }, + "id": "42" +} +``` + +> **Rationale** Suppose there exists some other LSPS. +> Suppose some future revision(s) of this LSPS includes (in +> whatever order) two additional parameters, +> `future_feature1_param` and `future_feature2_param`. +> If a client sends a request that includes +> `future_feature1_param` *and* `future_feature2_param`, and the +> LSP does not support one or both of them, the LSP would return +> an "Invalid params" -32602 error. +> However, without the `unrecognized` field in the `data` object, +> the client cannot know if the LSP does not support +> `future_feature1_param`, `future_feature2_param`, or both. + +##### Custom Errors + +[JSON-RPC 2.0][] protocol defines the range of `-31999 to +32767` (inclusive) to be application defined errors. +Each LSPS is provided and MUST use an error range of max 100 error codes. +The range for each LSPS is calculated as follow: `LSPS-number * 100 to LSPS-number * 100 + 99` (inclusive). + +For example: +- LSPS0: `00000 to 00099` +- LSPS1: `00100 to 00199` +- LSPS2: `00200 to 00299` + +And so on until `+32699`. The range of `-31999 to -1` (inclusive) is undefined +and MAY be used by applications outside of the LSPSpec. Such applications MAY request +the spec group to register an error code range to avoid collision. + +As per [JSON-RPC 2.0][], the range between `-32000 to -32099` is +"reserved for implementation-defined server-errors". These error codes MAY be used by LSPs +too. Clients MUST treat an error in this range similar to a `-32603 Internal error` if it does not know otherwise. + + +#### Disconnection Handling + +Networks are unreliable, and network participants are also unreliable. + +Clients MUST use a high-entropy `id` string for JSON-RPC 2.0 requests, +such as a UUID, or a hex encoding of a random binary blob of at least +80 bits, with randomness acquired from a cryptographically secure +source. +Clients MUST NOT use just a simple incrementing counter for the `id`. + +If a client receives a JSON-RPC 2.0 response with an `id` it does +not remember sending a request for, it MUST ignore that response. + +> **Rationale** Clients, LSPs, and the network between them +> can individually be unreliable, leading to clients that forget +> `id`s they issued previously, or clients or LSPs thinking that +> the other side may have restarted when it is the network +> between them that failed. +> Thus, random `id`s picked from a large space are the safest +> when the client might lose any counter state (by crashing if +> using an in-memory counter, or loss of persistent storage on +> hardware without storage redundancy), and are resilient +> against reconnections when both sides remained running. + +Clients MAY include additional information in their `id` for +internal tracking, as long as the total `id` has sufficient +entropy for universal uniqueness. + +Clients SHOULD internally impose a reasonable timeout, on +the scale of minutes, for receiving a response for a request, +and SHOULD treat a timeout event as a temporary server failure, +and forget the `id` of the timed-out request. + +> **Rationale** The LSP might crash between the time the client +> makes the request to the time it could complete processing of +> the response, and thus lost track of the request. + +If the LSP is unable to deliver a response to the client due +to a disconnection, it SHOULD treat this no differently from +successfully delivering the response but the client then does +not "follow up" on any action after that. + +> **Rationale** Even if the LSP delivers the response to the +> client, the client could then crash while processing the +> response, which means that the LSP cannot be sure that any +> response is received by the client anyway. + +### Lightning Feature Bit + +The [BOLT7][] specification describes the `node_announcement` +gossip message, which includes a `features` bit field. +The [BOLT1][] specification describes the `init` message, +which also includes a `features` bit field. + +[BOLT7]: https://github.com/lightning/bolts/blob/f7dcc32694b8cd4f3a1768b904f58cb177168f29/07-routing-gossip.md +[BOLT1]: https://github.com/lightning/bolts/blob/f7dcc32694b8cd4f3a1768b904f58cb177168f29/01-messaging.md + +LSPs MAY set the `features` bit numbered 729 +(`option_supports_lsps`) in both the `init` message on connection +establishment, and in their own advertised `node_announcement`. +Clients MUST NOT set `features` bit numbered 729 in either +context. + +> **Rationale** Because all communication is initiated +> through clients sending BOLT-8 messages +> only servers need to advertise themselves. +> Servers can choose to advertise themselves using feature +> bit 729. Clients can discover LSP's by downloading +> gossip and inspecting the channel-graph. +> The bit 729 was chosen randomly and has no special meaning. + +LSPs MAY set the `features` bit 729 `option_supports_lsps` if it +supports at least LSPS0, and MAY set the `features` bit even if it +does not support some of the LSPS specifications. + +> **Rationale** This specification also describes a `lsps0.list_protocols` +> API which the LSP uses to report exactly which LSPS specifications +> it supports. + +### LSPS Specification Support Query + +The client can determine if an LSP supports a particular LSPS +specification other than LSPS0 via the `method` named +`lsps0.list_protocols`, which accepts no parameters `{}`. + +`lsps0.list_protocols` has no errors defined. + +The response datum is an object like the below: + +```JSON +{ + "protocols": [1, 3] +} +``` + +`protocols` is an array of numbers, indicating the LSPS specification +number for the LSPS specification the LSP supports. +LSPs do not advertise LSPS0 support and 0 MUST NOT appear in the +`protocols` array. + +> **Rationale** LSPS0 support is advertised via `features` bit 729 +> already, so specifying `0` here is redundant. + +> **Non-normative** The example below would not be necessary for other +> LSPS specifications, but gives an idea of how the JSON-RPC 2.0 +> protocol would look like, when using this API endpoint. + +As a concrete example, a client might send the JSON object below +inside a BOLT8 message ID 37913 in order to query what LSPS protocols +the LSP supports: + +```JSON +{ + "method": "lsps0.list_protocols", + "jsonrpc": "2.0", + "id": "example#3cad6a54d302edba4c9ade2f7ffac098", + "params": {} +} +``` + +The LSP could then respond with a BOLT8 message ID 37913 with the following +payload, indicating it supports LSPS1 and LSPS3 (in addition to LSPS0): + +```JSON +{ + "jsonrpc": "2.0", + "id": "example#3cad6a54d302edba4c9ade2f7ffac098", + "result": { + "protocols": [1, 3], + "example-undefined-key-that-clients-should-ignore": true + } +} +``` + +### LSPS Extension And Versioning + +Individual LSPS SHOULD NOT include their own explicit versioning +scheme. + +An individual LSPS MAY be revised to a later revision. +Later revisions of an individual LSPS MUST NOT break compatibility with +earlier revisions. + +If there is a need for a change that breaks compatibility with an +existing revision of an LSPS, then a new LSPS with a different + LSPS number MUST be used. + +Later revisions of an LSPS MAY, if a new need arises: + +* Add new optional `params` fields for a client-callable API `method`. + * If the optional parameter is not specified, the `method` MUST + act the same as in previous revision of the LSPS, before the + parameter existed. +* Add new required or optional `result` fields for a client-callable + API `method`. + * Provided that the client is free to ignore the added fields and + that acceptance of a new `result` field is signalled by using a + new optional `params` field of some client-callable API `method`, + or by calling a new client-callable API `method`, or by some other + method, and that a lack of signal of acceptance of the new field + results in behaving the same as in older revisions. + (**Rationale** Older clients WILL ignore unknown fields and thus + would not be aware of them) + * Allowed recursively for any field that itself contains a + dictionary (i.e. a `result` field may contain a dictionary, and a + new revision of an LSPS may define a new field for that dictionary) + to any depth of dictionary fields, provided the previous provision + is followed. +* Add new error `code`s for a client-callable API `method`. +* Add completely new client-callable API `method`s. +* Add completely new LSP-initiated notification `method`s, provided + they are enabled only by a new optional field in some + client-callable API `method` or by a completely new client-callable + API `method`. + +Later revisions of an LSPS MUST NOT make changes beyond the above. + +A client: + +* MUST ignore unrecognized `result` fields from the result of a + client-callable API `method`. + * MUST recursively ignore unrecognized fields from any + dictionaries in a recognized `result` field, to any depth. +* MUST treat as a generic error any unrecognized error `code` from + an `error` result for a client-callable API `method`. + * SHOULD log this as unusual. +* MUST ignore unrecognized LSP-initiated notification `method`s. + * SHOULD log this as unusual. + +An LSP MUST: + +* Respond with a -32602 "Invalid params" error, described in a + previous section with `unrecognized` field in the `data` + dictionary, if it receives a client-callable API `method` it + recognized, but with an unrecognized parameter. +* Respond with a -32601 "Method not found" error, if it receives a + request to call a client-callable API `method` that it does not + recognize. + +If the client supports an older revision of an LSPS, it simply does +not provide any new optional parameters to existing client-callable +API `method`s, or any new client-callable API `method`s. +Then the interface would act the same as in the older revision. + +If a client wants to use a newer revision of an LSPS, it can look +for some new `result` field, and if that does not exist, it knows +the LSP supports an older revision of the LSPS. +The client can provide any new optional parameters it wants to +use, and if the LSP responds with a -32602 "Invalid params" error, +that error includes an `unrecognized` field, described above, that +contains the parameters that the LSP does not supoort. +The client can use a new client-callable API `method`, and if the +LSP responds with a -32601 "Method not found" error, knows that the +LSP does not support that `method`. + +Vendor-specific extensions to an LSPS can also obey the above +rules, and would remain compatible with a non-vendor LSP and a +non-vendor client. + + +## Common Schemas + +This section describes how particular Lightning and LSPS-specific types +are encoded into a JSON format, so that other LSPS specifications need +only refer to this document without having to repeat this over and over +again. + +Here are a few facts about JSON: + +* JSON numbers are technically supposed to be [IEEE 754][] Double + Precision floating-point numbers. + These numbers have 53-bit mantissas; if a number would require + more than 53 bits of significant binary digits to represent, then + IEEE Double Precision numbers will lose accuracy. + * Even so, as a text representation, there may be numbers that can + be accurately represented in [IEEE 754][] but which will require a + very long decimal string representation, which common JSON printers + are likely to truncate. + * Some short decimal string representations may not be accurately + represented as [IEEE 754][] Double Precision numbers, too. +* Many JSON parsers will separate floating-point and integer numbers, + based on whether a `.` character exists in a number. + Most of these will use signed 32-bit integers for parsed integral + numbers. + +[IEEE 754]: https://ieeexplore.ieee.org/document/30711 + +When a schema specifies a JSON string format, characters that can be +embedded into a JSON string without escapes MUST be encoded directly. +For example, even though the JSON strings `"A"` and `"\u0041"` are +equivalent encodings of the same string, only `"A"` would be allowed +under this specification. + +> **Rationale** Using alternative ways of expressing the same +> characters will both increase the size of the string *and* make +> the string less readable to humans, to no advantage. + +### Monetary Amounts + +###### Link: LSPS0.sat +###### Link: LSPS0.msat + +Monetary amounts MUST be expressed in either millisatoshi or satoshi +units. + +Other LSPS specifications MUST add a suffix to object field keys whose +value is a monetary amount: + +* `_msat` for monetary amounts in millisatoshi units. +* `_sat` for monetary amounts in satoshi units. + +> **Rationale** In some contexts, such as on-chain amounts like +> channel capacities, it is impossible to use sub-satoshi amounts, +> so using millisatoshi units for those is pointless. +> On the other hand, in many contexts Lightning uses millisatoshi +> amounts. +> An explicit suffix in field names helps ensure that developers +> do not confuse the two units. +> +> Using larger units may require writing numbers out as a decimal +> string representation with `.`, which might lose accuracy during +> conversion from text to an IEEE 754 number or vice-versa. + +Monetary amounts MUST be encoded as JSON strings containing the +decimal text representation of the number of millisatoshis or +satoshis. +LSPS implementations SHOULD internally use an unsigned 64-bit +number to represent amounts. + +> **Rationale** The maximum number of millisatoshis on the Bitcoin +> blockchain would require 63 significant bits. +> A JSON integral number might be parsed into only a 32-bit +> representation, or an actual IEEE 754 floating-point number +> with only 53 bits of mantissa. + +For example, the Bitcoin dust limit of 546 satoshis would be +encoded as `"546000"` for an `_msat`-suffixed field, or +`"546"` for a `_sat`-suffixed field. + +### On-chain Feerates + +###### Link: LSPS0.onchain_fee_rate + +On-chain feerates MUST be expressed in units of millisatoshi per +weight unit, or equivalently, satoshi per 1000 weight units +(sats/kWU). + +The minimum feerate when using satoshi per 1000 weight units is +253sat/kWU, or approximately 1.0sat/vbyte. + +On-chain feerates MUST be encoded as JSON integral numbers. + +For example, the minimum feerate would be encoded as `253`. + +### Proportional / Parts-per-million + +###### Link: LSPS0.ppm + +Proportional numbers (i.e. anything that humans might typically +express as a percentage) MUST be expressed in units of +parts-per-million. + +Parts-per-million units MUST be encoded as JSON integral numbers. + +For example, 0.25% would be encoded as `2500`. + +> **Rationale** This is its own type so that fractions can be +> expressed using this type, instead of as a floating-point +> type which might lose accuracy when serialized into text. +> This is effectively a fixed-point number format. +> Using parts-per-million gives granularity smaller than a +> percentage does. +> Lightning Network BOLT specifications already use the +> parts-per-million unit for proportional channel feerates. +> +> We expect that proportional amounts would be smaller than +> 100% or 1.0, which would be encoded as the JSON integral +> number 1000000, which is small enough to easily fit into +> IEEE 754 numbers or 32-bit signed integers with no loss +> of significant bits. + +### Short Channel Identifiers (SCID) + +###### Link: LSPS0.scid + +SCIDs MUST be encoded as a JSON string containing the +"human-readable" format of `BBBxTTTxOOO`, as defined +in [BOLT7 Definition of `short_channel_id`][]. + +[BOLT7 Definition of `short_channel_id`]: https://github.com/lightning/bolts/blob/29c14c6e12cbdf33f6b724094c81658a614d2e02/07-routing-gossip.md#definition-of-short_channel_id + +`BBB` is the top 24 bits in decimal text. +`TTT` is the middle 24 bits in decimal text. +`OOO` is the lowest 16 bits in decimal text. +`x` are literal lowercase `x` characters. + +For example, an SCID which would be hex-dumped as the binary +blob `083a8400034d0001` when encoded in a typical BOLT binary +encoding, would be written as the JSON string `"539268x845x1"`. + +> **Rationale** This format is a recognizable and distinctive +> format for SCIDs, and helps separate the SCID type from other +> types. + +### SECP256K1 Points / Public Keys / Lightning Network Node IDs + +###### Link: LSPS0.pubkey + +Lightning Network node IDs are SECP256K1 ECC public keys, which +are points on the SECP256K1 elliptic curve, as noted in +[BOLT8](https://github.com/lightning/bolts/blob/50b7391a6ef5310021c2a6378334e65e04e46876/08-transport.md?plain=1#L5-L6). + +SECP256K1 elliptic curve points MUST be encoded as a JSON string +of the hexadecimal dump of the compressed DER encoding of the +point. + +The compressed DER encoding is a 33-byte representation, and the +JSON string hexadecimal dump would therefore be encoded as 66 +characters (hexadecimal digits). +The first byte is either the byte `0x02` for an even Y coordinate, +or `0x03` for an odd Y coordinate, while the rest of the bytes is +the big-endian 32-byte integer representation of the X coordinate. + +In contexts where the case is significant (for example, if it +will be committed to by some hash or signature) then the +representation MUST be in lowercase. +Otherwise, readers MUST allow both lowercase and uppercase +hexadecimal digits. + +Readers SHOULD validate that the point is indeed on the SECP256K1 +curve. + +For example, the SECP256K1 generator point `G` would be written +as the JSON string +`"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"`. + +> **Rationale** Bitcoin and Lightning have standardized on this +> format, and traditionally used lowercase for hexadecimal digits. + +### Lightning Network Connection Strings + +###### Link: LSPS0.connection_string + +Lightning Network connection strings describe a Lightning Network +Node ID and one way to connect to that node. + +Connection strings MUST be encoded as a JSON string, with three +parts: + +1. The Lightning Network Node ID, encoded as the hexadecimal + dump of the compressed DER encoding, followed by the `@` + sign. +2. The address, which follows the `@` sign and lasts to the + last `:` in the string. + Address formats allowed are those defined in the BOLT + specifications: + * [IPv4 addresses](https://datatracker.ietf.org/doc/html/rfc4001). + * [IPv6 addresses](https://datatracker.ietf.org/doc/html/rfc5952). + * [A Torv3 hidden service](https://gitweb.torproject.org/torspec.git/tree/proposals/224-rend-spec-ng.txt). + * A DNS-resolvable name. +3. A port number, which is started by the last `:` + in the string, and is a decimal text representation of + the number. + +For example, a node with ID `0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798` +on an IPv6 `localhost` listening on the default +port 9735 might be expressed as the JSON string: +`"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798@::1:9735"` + +A reader SHOULD extract the node ID as the part of the string until +the first `@` character, and the port number as the part of the +string from the last `:` character to the end of the string, +and the address as between the first `@` to the last `:`, then +individually parse and validate each part. + +### On-chain Addresses + +###### Link: LSPS0.onchain_address + +An on-chain address MUST be a SegWit address, from version 0 to +any future version. + +An on-chain address MUST be encoded as a string containing the +SegWit address: + +* For SegWit v0 addresses, encoded using [bech32][]. +* For SegWit v1 (Taproot) or later, encoded using [bech32m][]. + +[bech32]: https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki +[bech32m]: https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki + +Readers MUST support, and writers SHOULD only emit: + +* SegWit v0 addresses with 20 byte commitment (P2WPKH) or + 32 byte commitment (P2WSH). +* SegWit v1 addresses with 32 byte commitment (Taproot). + +Readers MAY support other SegWit versions. + +### Lightning Network Node Signatures + +###### Link: LSPS0.ln_signature + +Signatures generated by a particular Lightning Network node, with a +particular known node ID, MUST be generated and represented using +the [LND `signmessage` #specinatweet][], and encoded as a JSON string +containing the zbase32 encoding: + +> `zbase32(SigRec(SHA256(SHA256("Lightning Signed Message:" + msg))))`. +> `zbase32` from https://philzimmermann.com/docs/human-oriented-base-32-encoding.txt +> and `SigRec` has first byte 31 + recovery id, followed by 64 byte sig. + +[LND `signmessage` #specinatweet]: https://web.archive.org/web/20191010011846/https://twitter.com/rusty_twit/status/1182102005914800128 + +> **Rationale** This non-BOLT specification is widely immplemented +> and is commonly used in many bespoke protocols, often used to +> prove that a particular user owns a particular node, or as an +> informal way to reduce spam by requiring that a participant +> prove they have a published Lightning Network node. + +CLN, LND, and Eclair all have a `signmessage` command that allows +generation of this signature, and verification can be implemented +using (**Non-normative**) e.g. [ln-verifymessagejs][]. + +[ln-verifymessagejs]: https://github.com/SeverinAlexB/ln-verifymessagejs + +Other LSPS specifications MUST specify that messages to be signed +include the string `LSPS` followed by the LSPS specification +number, as well as a human-readable ASCII text warning not to sign +the message manually. +For example, a hypothetical LSPS999 might specify: + + ```Markdown + The signature MUST sign a message with the template: + + "LSPS999: DO NOT SIGN THIS MESSAGE MANUALLY: I will make an `OP_RETURN` output with ${hash}." + ``` + +> **Rationale** `signmessage` is widely used in +> many bespoke protocols where a verifier will ask a human operator +> to prove they control a node by signing a specified message +> from the verifier. +> The verifier could then provide a template from an LSPS +> specification, hoping to get a signature that could be used +> in an LSPS protocol to prove something other than what the +> human operator expected. + +### Datetimes + +###### Link: LSPS0.datetime + +Particular points of time in the modern era (a "datetime") MUST be +encoded as a JSON string containing the [ISO 8601][] format +`"YYYY-MM-DDThh:mm:ss.uuuZ"`. +These are always in the UTC timezone. + +[ISO 8601]: https://www.iso.org/iso-8601-date-and-time-format.html + +### Binary Blobs (Raw Transactions, PSBTs, Lightning onions, etc.) + +###### Link: LSPS0.binary_blob + +Binary blobs MUST be encoded as a JSON string containing the +Base 64 encoding of the binary blob, as described in [RFC 4648 +Section 4][]. + +[RFC 4648 Section 4]: https://datatracker.ietf.org/doc/html/rfc4648#section-4 + +Padding characters `=` MUST be used. + +> **Rationale** All characters in the [RFC 4648 Section 4][] +> Base 64 encoding can be inside a JSON string without any escapes, +> leading to an encoding that is only 33.33% longer than the +> equivalent straight binary encoding, while having reasonably +> simple encoding and decoding implementations. +> +> Although padding is unnecessary, as the length of a JSON string +> can be determined from the `"` delimiters, +> some base64 parsers completely reject the input if you do +> not include the padding `=` characters, despite their +> uselessness, as this [tweet](https://twitter.com/fiatjaf/status/1558525040374718465) +> laments. + +### Transaction IDs + +###### Link: LSPS0.txid + +Transaction ID without witness data as defined in [BIP0141](https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#transaction-id). + +It MUST be converted to little-endian to enable searching and MUST be encoded as a 64 character HEX string. + +> **Rationale** We use the little-endian format to let users easily copy-paste the txid to a block explorer. +Example: + +```json +{ + "txid": "F27C97F46ED7281A3EFA7287410082EBA0CD1424D72703A217E435EA840957B0" +} +``` + + + +### Output Index + +###### Link: LSPS0.output_index + +`output_index` is the 0 based index for transaction output UTXOs. It is a maximum of [16 bits (2 bytes)](https://github.com/lightning/bolts/blob/aad959a297ff66946effb165518143be15777dd6/07-routing-gossip.md#definition-of-short_channel_id). It MUST be represented as a JSON integer (number). + +Example: + +```json +{ + "output_index": 0 +} +``` + + +### Outpoints + +###### Link: LSPS0.outpoint + +An outpoint consist of a `LSPS0.txid` and a `LSPS0.output_index`. It is defined in [BOLT0](https://github.com/lightning/bolts/blob/master/00-introduction.md#outpoint). It MUST be encoded as a JSON string in the `txid:output_index` format. + +> **Rationale** The `txid:outpoint` format is conveniently used in block explorers and can just be copy pasted to search for the affected UTXO. +Example: + +```json +{ + "funding_outpoint": "F27C97F46ED7281A3EFA7287410082EBA0CD1424D72703A217E435EA840957B0:0" +} +``` + +### Error Codes + +Commonly shared error codes that are used in multiple LSPS. + +#### 001 Client rejected + +###### Link: LSPS0.client_rejected_error + +A LSP MAY return a `Client rejected` error in case it does not want to offer a service to the client. A LSP MAY reject a client by its node_id or IP for example. + +| Code | Message | Data | Description | +| ---- | --------------- | -------------------------------- | ---------------------------- | +| 001 | Client rejected | {"message": %human_message% } | The LSP rejected the client. | + +- `%human_message% ` A human readable message that indicates why the lsp rejected the client. + - May simply be `{ "message": "Client rejected" }`.