LSPS0传输层协议规范
本文定义了LSPS0传输层协议,用于LSP(Lightning Service Provider)节点与客户端之间的通信。该协议基于BOLT8点对点消息传输JSON-RPC 2.0格式的请求和响应。规范包括消息格式、错误处理、特性位、协议查询、扩展与版本控制以及常见模式的编码(如金额、费率、公钥、连接字符串等)。通过单一BOLT8消息ID 37913承载所有LSPS协议通信,减少了协议占用。还定义了LSP支持的特性位和协议列表查询方法。
bLIP: 50
标题: LSPS0: LSP 规范传输层
状态: 活跃
作者: ZmnSCPxj jxPCSnmZ <zmnscpxj@protonmail.com>
创建日期: 2024-12-02
许可证: MIT
摘要
本 bLIP 定义了一种用于 LSP 节点及其客户端之间通信的协议。它使用通过 BOLT8 点对点消息传输的 JSON-RPC 格式,允许客户端向 LSP 请求服务。
版权
本 bLIP 根据 MIT 许可证授权。
参考实现
该协议的参考实现作为 lightning-liquidity crate 的一部分提供。
参与者
'LSP' 是 API 提供者,充当服务器。
'客户端' 是 API 消费者。
协议
Lightning 点对点传输
Lightning Network BOLT8 规范描述了 Lightning Network 点对点消息的传输层。
Lightning Network BOLT8 将消息描述为包含两个部分:
- 一个 16 位的消息 ID(以大端序编码)。
- 一个可变长度的消息负载(消息的其余部分)。
通过 BOLT8 协议发送的消息提供了访问其他 LSPS 规范中定义的端点。
理由 所有客户端和 LSP 都应为 Lightning Network 参与者,并且需要以某种方式使用 BOLT8 协议进行通信。因此,这不会为客户端或任何 LSP 引入额外依赖,而 HTTP(S) 或 gRPC 协议则会引入额外依赖。
在本规范开发期间,曾提议使用洋葱消息。由于客户端无论如何都需要连接到 LSP 节点并与之管理通道,并且 LSP 节点希望从 IPv4、IPv6、DNS 或 TorV3 联系点易于被联系,因此洋葱消息发送到远程 Lightning Network 节点(可能无法直接联系)的能力被认为对于大多数客户端-LSP 通信需求来说是不必要的。
所有 LSPS 消息 必须 使用 BOLT8 消息 ID 37913 (lsps0_message_id)。
理由 我们指定了一个单一消息 ID,因为这减小了所有 LSPS 规范的“占用空间”,只使用一个消息 ID,从而增加了使用 Lightning BOLT8 点对点传输的其他协议与 LSPS 规范兼容的可能性。BOLT8 消息 ID 37913 是奇数,以符合 奇怪也没关系 规则,并且位于为 BOLT 协议扩展保留的 32768 到 65535 范围内。一些实现(如 Core Lightning)仅向自定义消息处理程序暴露奇数编号的消息,而其他实现(如 LDK)仅暴露 32768 及以上的消息。消息 ID 是随机选择的,没有特殊含义。
消息负载格式
BOLT8 消息负载包含一个完整 JSON 对象的 UTF-8 编码。
嵌入在消息负载中的 JSON 对象由 JSON-RPC 2.0 协议定义。
如果客户端或 LSP 收到一条消息 ID 为 37913 的 BOLT8 消息,它 必须 执行以下检查。如果任何检查失败,则传入消息失败并出现“错误消息格式”错误。
- 负载 必须 解析为单个完整的 UTF-8 编码 JSON 对象(即 JSON 键值存储或字典),并允许前导或尾随空白字符(空格、制表符、换行、回车)。
- 例如,“
{ }”将通过此检查,但“{”和“[ ]”不会通过。
- 例如,“
- 负载 必须 不能解析为多个完整的 JSON 对象。
- 例如,“
{ } {”和“{ } { }”不会通过此检查。
- 例如,“
- 负载 必须 不包含任何 0 字节。
- 如果是客户端,收到的负载 必须 解析为符合 JSON-RPC 2.0 响应或通知对象的 JSON 对象。
- 如果是 LSP,收到的负载 必须 解析为符合 JSON-RPC 2.0 请求对象的 JSON 对象。
如果出现“错误消息格式”错误,客户端必须:
- 必须 忽略该消息。
- 应该 将此记录为异常事件。
- 应该 在此连接会话中不再向对等方发送 ID 为 37913 的 BOLT8 消息。
- 应该 在重新连接时重试。
如果出现“错误消息格式”错误,LSP 必须:
- 必须 以 JSON-RPC 2.0 错误响应回复,原因为“parse error”(
code=-32700,id=null)。 - 必须 忽略该消息。
- 应该 将此记录为异常事件。
理由 要求一个完整的 JSON 对象简化了消息处理,使得一条消息对应一个请求或响应。C 语言将 NUL 字符用作字符串终止符,因此嵌入的 0 字节可能会在将 JSON 字符串传递给 C 代码的实现中引起问题。相反,我们不要求负载以 0 字节 / NUL 字符终止,因为这在许多现代非 C 语言中是不必要的;C 代码可以复制缓冲区并附加 NUL 字符(如果必要),因为负载大小是已知的。UTF-8 在 ASCII 范围内的每个 JSON 表示字符占用 1 字节,我们预计通过此协议发送的大部分数据适合 ASCII 范围。
LSP 发送错误消息是一个严重错误,会影响 LSP 的所有客户端,并且 LSP 操作者需要重启节点来修复该错误。因此,客户端不应重新尝试向 LSP 发送任何请求,直到客户端再次连接到它,因为重新连接可能表明 LSP 已重启,这又可能表明 LSP 的这个严重错误已修复。
LSP 充当 JSON-RPC 2.0 服务器,而客户端充当 JSON-RPC 2.0 客户端。
理由 JSON 是一种简单、易于扩展且得到广泛支持的格式。BOLT8 中的 Lightning Network 点对点传输协议本身并不是像 HTTP(S) 或 gRPC 那样的请求-响应协议,而 JSON-RPC 2.0 描述了一种基于 JSON 的协议,该协议通过一个简单的隧道构建了一个请求-响应协议;BOLT8 消息 ID 37913 充当了这个隧道。JSON-RPC 2.0 易于实现,而本规范描述了 JSON-RPC 2.0 的一个更简单的子集。尽管 BOLT8 消息的负载限制为 65533 字节,但预计请求和响应都将远低于该限制,因此跨多条消息“切割”大型对象、使用压缩算法以及使用二进制格式而非 JSON 都被认为是不必要的。特别是,我们预计大多数合理的请求和响应小于 1000 字节,任何压缩都不会显著减少下层网络层需要传输的 MTU(通常每个 MTU 约 1400 字节)数量。
此外,缺乏压缩大大简化了实现、测试、互操作性和依赖关系。压缩可能容易受到 zip 炸弹 的攻击,即一小段压缩数据展开为几千兆字节或太字节的未压缩数据。我们需要对未压缩文本施加 某种 限制,而这个限制可能恰好是 BOLT8 消息负载的 65533 字节限制。
客户端:
- 必须 发送一个完整的 JSON-RPC 2.0 请求对象,UTF-8 编码,作为 BOLT8 消息 ID 37913 的负载。
- 必须 不发送 JSON-RPC 2.0 通知对象(即它发送的每个对象都必须有
id字段)。 - 必须 不使用基于位置的参数结构,并且 必须 使用基于名称的参数结构。
- 必须 不在数组中批处理多个 JSON-RPC 2.0 请求对象,如 JSON-RPC 2.0 规范的 “Batch” 部分所述。
理由 通过禁止基于位置的参数结构,其他 LSPS 规范只需要定义参数名称,而不需要定义用于基于位置的某种规范顺序的参数。只处理基于名称的参数结构也简化了 LSP 代码,因为它不必检查
params值是数组还是字典,并单独将数组元素映射到params键。批处理请求需要根据 JSON-RPC 2.0 进行批处理响应,对于 LSP 来说,单独处理不相关的 LSPS 方法而不需要重新批处理响应可能更简单;这给了我们一条简单的规则:“一条消息是一个请求 / 响应”。
LSP:
- 必须 发送以下任一内容作为 BOLT8 消息 ID 37913 的负载:
- 一个完整的 JSON-RPC 2.0 响应对象,UTF-8 编码。
- 一个完整的 JSON-RPC 2.0 通知对象(即一个没有
id但有method、params和jsonrpc字段的对象)。- 必须 对通知使用基于名称的参数结构。
- 必须 遵守 JSON-RPC 2.0 标准错误码。
- 应该 不发送 BOLT8 消息 ID 37913,除非对等方已经发送过 BOLT8 消息 ID 37913(可能在过去某个连接会话中)。
- 可以 以与客户端发送请求顺序不同的顺序发送响应。
理由 发送 BOLT8 消息 ID 37913 的对等方表明它理解 LSPS0 协议并希望充当客户端。如果对等方没有发送它,那么它就不是 LSPS 客户端,LSP 没有理由发送 BOLT8 消息 ID 37913。通知允许 LSP 向客户端发出事件信号,前提是客户端之前已经通过调用某个 LSPS 定义的方法来表示愿意接收此类事件。例如,一个 LSPS 可能指定一个方法,允许在客户端本可以接收付款但缺乏入站流动性时,通过 LSPS 指定的通知向客户端发出信号。
LSPS 方法和通知规范
其他 LSPS 规范:
- 必须 为每个客户端可调用的 API 端点和每个 LSP 发起的通知指示
method名称,以及每个method的params键名,以及每个参数的含义。method名称 必须 为 snake_case 格式,即单词为小写并由_字符分隔。- 必须 将
method名称前缀为lsps后跟 LSPS 编号,然后是.,例如用于客户端请求的lsps999.do_this或用于 LSP 通知的lsps999.that_happened。 - 必须 还描述每个 API 端点可能的错误
code,以及该错误码的data(必须 是一个对象(字典)),如果应该有data字段的话。- 可以 选择不为某个错误
code定义data对象,在这种情况下客户端 必须 不期望有data字段。
- 可以 选择不为某个错误
- 必须 为其 API 端点定义
result值,这些值应为对象(字典),并且 必须 定义响应的键。
理由 前缀确保方法名称不会跨 LSPS 规范冲突,并创建了一种约定,允许非标准扩展定义自己的前缀。
result字典允许将来的 LSPS 规范修订版无缝地向响应添加新键。
LSP 可以 在 response 值中返回未在相关 LSPS 规范中定义的额外键。相反,客户端 必须 忽略无法识别的键。
理由 这允许将来的 LSPS 规范修订版无缝地向响应添加新键,同时保持与不了解后期修订版(带有额外键)的旧客户端的向后兼容性。后期修订版可以通过仅添加新的可选参数来保持向后兼容性,这些参数在不存在时使 API 端点的行为与旧修订版相同。
其他 LSPS 规范 必须 设计为能够抵御响应和通知在从 LSP 到客户端的途中丢失的情况。LSP 可能认为消息已送达,但包含消息的 IP 数据包可能在客户端遭遇意外崩溃之前未能到达客户端,或者客户端可能在解析和处理消息的早期阶段未能持久化与响应或通知相关的数据。其他 LSPS 规范 必须:
- 要求 LSP 对 LSP 在获得补偿之前必须保留的信息进行时间限制。
- 要求或支持客户端尽可能多地存储状态,并包括某种签名或 MAC,LSP 可以使用它来识别该状态是由其发出的,而无需 LSP 记住该状态。
- 描述电平触发而非边沿触发的通知(即通知应表示“客户端,你仍有一些 X 需要处理”,而不是“客户端,添加了一个新 X / 移除了一个 X / 更改了一个 X”)。
- 要求 LSP 必须 每当客户端需要处理的项目发生更改时发送通知(在边沿上发送),但在与客户端重新连接时,如果电平触发条件仍然为真,也 必须 发送通知(即在重新连接时也检查电平并重新发送)。
- 将任何队列(即客户端需要处理的项目,例如由于缺乏可行通道而无法通过正常 BOLT 消息传递给客户端的 HTLC)描述为具有单独的“查看第一个项目”和“移除第一个项目然后查看下一个项目”API,同时确保队列中的每个项目都有一个唯一标识符,客户端可以使用该标识符来检查该项目是否已被读取,但之前的“移除第一个项目”调用未能送达。
理由 客户端可能因操作员错误或无关原因而崩溃,并且客户端可能是攻击者而非合法的付费客户端。因此,客户端可能会启动一个需要与 LSP 进行多次通信轮次的流程或过程,然后由于崩溃或故意浪费 LSP 资源而在中途中止。客户端和 LSP 之间的网络也可能不可靠,因此通知可能会丢失,因此被通知的任何状态应在重新连接时重新发送。特别是,通知永远不会在传输层级别由客户端明确确认。队列特别适合电平触发通知,其中“电平”是“队列是否非空?”处理此队列中的一个项目可能需要客户端端的时间,在此期间客户端可能崩溃或连接中断,因此第一个项目应保留在 LSP 端,因为客户端在收到“查看第一个项目”调用的响应后可能无法持久化它。客户端需要能够检测是否已完成对队列顶部项目的处理,将“移除第一个项目然后查看下一个项目”合并为单个调用减少了处理每个完全处理项目所需的往返次数,同时仍然将当前正在处理的项目保留在 LSP 端,以防在处理期间客户端崩溃。
错误处理
通常,客户端、LSP 以及任何基于 LSPS0 构建的 LSPS 必须 遵守 JSON-RPC 错误码。本文档通过描述结合 JSON-RPC 2.0 和 BOLT8 的边缘情况来扩展错误码。任何像 -32603 内部错误 这样的错误码即使没有明确提到也是有效的。
JSON-RPC 2.0 error 包含一个 message 字段,这是一个人类可读的错误消息。
客户端 必须 仔细过滤错误消息中可能存在问题字符,例如 NUL 字符、<、换行符或 ASCII 控制字符,这些字符在动态上下文中显示时可能引起问题,如 HTML 网页、TTY 或 C 字符串处理函数。
客户端 不应该 直接将错误 message 暴露给用户,除非用户启用了某种“高级”模式或“开发者”模式。客户端 可以 将它们写入可供“高级”或“开发者”模式用户访问的日志中。
客户端 应该 基于错误 code 生成自己的消息,用于向用户显示,可能结合可选的 data 对象中的信息(如果错误 code 定义了该信息)。如果客户端无法识别该错误 code,或者期望在 data 对象中存在某个字段但该字段不存在,它 应该 向用户指示这是一个“无法识别”的错误。
理由 LSP 可能编写不正确或具有误导性的人类可读错误消息,并且用户可能将这些错误消息报告为客户端开发者的错误,因为用户可见的错误消息源是客户端;因此,客户端最好编写自己的错误消息,以便根据用户反馈进行更改。某些 LSPS 规范的后期修订版可能会引入新的错误码,或者规范可能不完整,实际开发表明可能发生某些未指定的错误,在这种情况下,人类可读的
error字段可以包含对此错误的描述,客户端开发者可以使用该描述来帮助指导规范的演变,或遵循 LSPS 规范的后期修订版。
-32602 无效参数错误
发送无效参数错误且 code = -32602 的 LSP 必须 在 error 响应中包含一个 data 对象。
此 data 对象 必须 至少包含字段 unrecognized,它是一个 JSON 字符串数组,表示一组无序的参数名称。这些是 LSP 无法识别为 RPC 方法有效参数的参数名称。
例如,假设客户端发送了此请求:
{
"jsonrpc": "2.0",
"method": "example.method_name",
"params": {
"future_feature1_param": "value1",
"future_feature2_param": "value2"
},
"id": "42"
}
假设 LSP 识别 example.method_name 为有效方法,并识别 future_feature2_param 为该方法的有效参数,但不识别 future_feature1_param。在这种情况下,LSP 将响应:
{
"jsonrpc": "2.0",
"error": {
"code": -32602,
"message": "Invalid params",
"data": {
"unrecognized": ["future_feature1_param"]
}
},
"id": "42"
}
理由 假设存在某个其他 LSPS。假设该 LSPS 的某个未来修订版(按某种顺序)包括两个额外的参数
future_feature1_param和future_feature2_param。如果客户端发送包含future_feature1_param和future_feature2_param的请求,而 LSP 不支持其中一个或两个,则 LSP 将返回一个“无效参数” -32602 错误。但是,如果没有data对象中的unrecognized字段,客户端无法知道 LSP 是不支持future_feature1_param、future_feature2_param还是两者都不支持。
自定义错误
JSON-RPC 2.0 协议将 -31999 到 +32767(含)的范围定义为应用程序定义的错误。每个 LSPS 被分配一个错误范围,最大为 100 个错误码。每个 LSPS 的范围计算如下:LSPS 编号 * 100 到 LSPS 编号 * 100 + 99(含)。
例如:
- LSPS0:
00000 到 00099 - LSPS1:
00100 到 00199 - LSPS2:
00200 到 00299
依此类推,直到 +32699。-31999 到 -1(含)的范围未定义,可以 由 LSPSpec 之外的应用程序使用。此类应用程序 可以 请求规范组注册一个错误码范围以避免冲突。
根据 JSON-RPC 2.0,-32000 到 -32099 之间的范围“保留供实现定义的服务器错误”。这些错误码也 可以 由 LSP 使用。客户端 必须 将在此范围内的错误视为类似于 -32603 内部错误,除非它另有了解。
断开连接处理
网络不可靠,网络参与者也不可靠。
客户端 必须 为 JSON-RPC 2.0 请求使用高熵的 id 字符串,例如 UUID,或至少 80 位随机二进制 blob 的十六进制编码,随机性来自加密安全源。客户端 不得 仅使用简单的递增计数器作为 id。
如果客户端收到的 JSON-RPC 2.0 响应的 id 不记得发送过请求,它 必须 忽略该响应。
理由 客户端、LSP 及其之间的网络各自可能不可靠,导致客户端忘记之前发出的
id,或者客户端或 LSP 认为另一端可能已重启,而实际上是它们之间的网络故障。因此,从大空间中随机选择的id在客户端可能丢失任何计数器状态(如果使用内存计数器,则崩溃;如果硬件没有存储冗余,则持久存储丢失)时最安全,并且在双方都保持运行时对于重新连接具有弹性。
客户端 可以 在其 id 中包含额外信息用于内部跟踪,只要整个 id 具有足够的熵以实现全局唯一性。
客户端 应该 在内部施加一个合理的超时时间(分钟级别),用于等待请求的响应,并且 应该 将超时事件视为临时服务器故障,并忘记超时请求的 id。
理由 LSP 可能在客户端发出请求到完成响应处理之间崩溃,从而丢失了请求的跟踪。
如果 LSP 由于断开连接而无法将响应传递给客户端,它 应该 以与成功传递响应但客户端之后未对其执行“跟进”操作相同的方式处理。
理由 即使 LSP 将响应传递给客户端,客户端也可能在处理响应时崩溃,这意味着 LSP 无论如何也无法确定任何响应已被客户端收到。
Lightning 功能位
BOLT7 规范描述了 node_announcement 八卦消息,其中包含一个 features 位字段。BOLT1 规范描述了 init 消息,其中也包含一个 features 位字段。
LSP 可以 在连接建立时的 init 消息中,以及在其自身公告的 node_announcement 中设置编号为 729 的 features 位(option_supports_lsps)。客户端 不得 在任何上下文中设置编号为 729 的 features 位。
理由 因为所有通信都是通过客户端发送 BOLT-8 消息发起的,只有服务器需要公告自己。服务器可以选择使用功能位 729 来公告自己。客户端可以通过下载八卦消息并检查通道图来发现 LSP。位 729 是随机选择的,没有特殊含义。
如果 LSP 至少支持 LSPS0,则 LSP 可以 设置 features 位 729 option_supports_lsps,并且即使它不支持某些 LSPS 规范,也 可以 设置该 features 位。
理由 本规范还描述了一个
lsps0.list_protocolsAPI,LSP 使用它来报告它确切支持哪些 LSPS 规范。
LSPS 规范支持查询
客户端可以通过名为 lsps0.list_protocols 的 method 来确定 LSP 是否支持除 LSPS0 以外的特定 LSPS 规范,该方法不接受参数 {}。
lsps0.list_protocols 未定义任何错误。
响应数据是一个类似如下的对象:
{
"protocols": [1, 3]
}
protocols 是一个数字数组,指示 LSP 支持的 LSPS 规范编号。LSP 不公告 LSPS0 支持,并且 0 不得 出现在 protocols 数组中。
理由 LSPS0 支持已经通过
features位 729 公告,因此在此指定0是多余的。
非规范 下面的例子对于其他 LSPS 规范不是必需的,但给出了使用此 API 端点时 JSON-RPC 2.0 协议的样子。
作为一个具体示例,客户端可以在 BOLT8 消息 ID 37913 内部发送下面的 JSON 对象,以查询 LSP 支持哪些 LSPS 协议:
{
"method": "lsps0.list_protocols",
"jsonrpc": "2.0",
"id": "example#3cad6a54d302edba4c9ade2f7ffac098",
"params": {}
}
然后 LSP 可以通过以下负载回复 BOLT8 消息 ID 37913,表明它支持 LSPS1 和 LSPS3(除了 LSPS0):
{
"jsonrpc": "2.0",
"id": "example#3cad6a54d302edba4c9ade2f7ffac098",
"result": {
"protocols": [1, 3],
"example-undefined-key-that-clients-should-ignore": true
}
}
LSPS 扩展和版本控制
各个 LSPS 不应该 包含自己的显式版本控制方案。
各个 LSPS 可以 修订到后续版本。单个 LSPS 的后续版本 不得 破坏与早期版本的兼容性。
如果需要进行破坏与现有 LSPS 版本兼容性的更改,则 必须 使用具有不同 LSPS 编号的新 LSPS。
LSPS 的后续版本 可以,如果出现新需求:
- 为客户端可调用的 API
method添加新的可选params字段。- 如果未指定可选参数,则
method必须 与参数存在之前的 LSPS 版本相同的方式运行。
- 如果未指定可选参数,则
- 为客户端可调用的 API
method添加新的必需或可选result字段。- 前提是客户端可以自由忽略添加的字段,并且接受新
result字段的信号是通过使用某个客户端可调用 APImethod的新可选params字段,或通过调用一个新的客户端可调用 APImethod,或通过其他方式发出的,并且缺少接受新字段的信号会导致行为与旧版本相同。(理由 旧客户端将忽略未知字段,因此不会意识到它们) - 允许递归地应用于任何本身包含字典的字段(即
result字段可能包含一个字典,LSPS 的新版本可以为该字典定义一个新字段),达到任意深度的字典字段,前提是遵循前面的规定。
- 前提是客户端可以自由忽略添加的字段,并且接受新
- 为客户端可调用的 API
method添加新的错误code。 - 添加全新的客户端可调用 API
method。 - 添加全新的 LSP 发起的通知
method,前提是它们仅通过某个客户端可调用 APImethod中的新可选字段或通过全新的客户端可调用 APImethod启用。
LSPS 的后续版本 不得 进行超出上述范围的更改。
客户端:
- 必须 忽略来自客户端可调用 API
method结果的无法识别的result字段。- 必须 递归地忽略已识别
result字段中任何字典的无法识别字段,达到任意深度。
- 必须 递归地忽略已识别
- 必须 将来自客户端可调用 API
method的error结果中任何无法识别的错误code视为一般错误。- 应该 将此记录为异常。
- 必须 忽略无法识别的 LSP 发起的通知
method。- 应该 将此记录为异常。
LSP 必须:
- 如果收到一个它识别但包含无法识别参数的客户端可调用 API
method,则使用前面带有data字典中unrecognized字段的 -32602 “无效参数”错误进行响应。 - 如果收到调用它无法识别的客户端可调用 API
method的请求,则使用 -32601 “方法未找到”错误进行响应。
如果客户端支持某个 LSPS 的旧版本,它只需不向现有的客户端可调用 API method 提供任何新的可选参数,或不提供任何新的客户端可调用 API method。这样接口的行为将与旧版本相同。
如果客户端想要使用某个 LSPS 的新版本,它可以寻找一些新的 result 字段,如果不存在,它就知道 LSP 支持该 LSPS 的旧版本。客户端可以提供它想要使用的任何新的可选参数,如果 LSP 响应 -32602 “无效参数”错误,该错误包含一个上面描述的 unrecognized 字段,其中包含 LSP 不支持的参数。客户端可以使用新的客户端可调用 API method,如果 LSP 响应 -32601 “方法未找到”错误,就知道 LSP 不支持该 method。
LSPS 的供应商特定扩展也可以遵守上述规则,并且与不是供应商的 LSP 和不是供应商的客户端保持兼容。
公共模式
本节描述如何将特定的 Lightning 和 LSPS 特定类型编码为 JSON 格式,以便其他 LSPS 规范只需引用本文档,而无需重复说明。
以下是关于 JSON 的一些事实:
- JSON 数字在技术上应该是 IEEE 754 双精度浮点数。这些数字有 53 位的尾数;如果一个数字需要超过 53 位有效二进制数字来表示,那么 IEEE 双精度数字将失去精度。
- 许多 JSON 解析器会根据数字中是否存在
.字符来区分浮点数和整数。其中大多数将对解析的整数使用有符号 32 位整数。
当模式指定 JSON 字符串格式时,可以嵌入 JSON 字符串而无需转义的字符必须直接编码。例如,即使 JSON 字符串 "A" 和 "\u0041" 是同一字符串的等价编码,但在此规范下只允许 "A"。
理由 使用替代方式表达相同字符会增加字符串大小 并且 降低字符串的人类可读性,没有任何好处。
货币金额
链接:LSPS0.sat
链接:LSPS0.msat
货币金额必须以毫聪或聪为单位表示。
其他 LSPS 规范 必须 对其值为货币金额的对象字段键添加后缀:
_msat用于以毫聪为单位的货币金额。_sat用于以聪为单位的货币金额。
理由 在某些上下文中,例如链上金额(如通道容量),不可能使用子聪金额,因此对这些金额使用毫聪单位是毫无意义的。另一方面,在许多上下文中,Lightning 使用毫聪金额。字段名称中的显式后缀有助于确保开发者不会混淆这两个单位。
使用更大的单位可能需要将数字写成带.的十进制字符串表示,这可能在从文本转换为 IEEE 754 数字或反向转换时失去精度。
货币金额 必须 编码为 JSON 字符串,包含毫聪或聪数量的十进制文本表示。LSPS 实现 应该 在内部使用无符号 64 位数字来表示金额。
理由 比特币区块链上的最大毫聪数量需要 63 个有效位。JSON 整数可能仅被解析为 32 位表示,或者解析为只有 53 位尾数的实际 IEEE 754 浮点数。
例如,比特币的粉尘限制 546 聪将在 _msat 后缀字段中编码为 "546000",或在 _sat 后缀字段中编码为 "546"。
链上费率
链接:LSPS0.onchain_fee_rate
链上费率 必须 以毫聪每权重单位表示,或者等效地,以聪每 1000 权重单位(sats/kWU)表示。
使用聪每 1000 权重单位时的最低费率为 253 sat/kWU,或约 1.0 sat/vbyte。
链上费率 必须 编码为 JSON 整数。
例如,最低费率将编码为 253。
比例 / 百万分之几
链接:LSPS0.ppm
比例数字(即人类通常表示为百分比的东西)必须 以百万分之几为单位表示。
百万分之几单位 必须 编码为 JSON 整数。
例如,0.25% 将编码为 2500。
理由 这是一个独立的类型,以便可以使用此类型表示分数,而不是使用浮点类型(在序列化为文本时可能会丢失精度)。这实际上是一种定点数格式。使用百万分之几提供的粒度比百分比更细。Lightning Network BOLT 规范已经对比例通道费率使用了百万分之几单位。
我们预计比例金额将小于 100% 或 1.0,这将编码为 JSON 整数 1000000,该值足够小,可以轻松地以 IEEE 754 数字或 32 位有符号整数表示,而不会丢失有效位。
短通道标识符 (SCID)
链接:LSPS0.scid
SCID 必须 编码为 JSON 字符串,包含 BOLT7 中 short_channel_id 定义 的“人类可读”格式 BBBxTTTxOOO。
BBB 是十进制文本中的前 24 位。TTT 是十进制文本中的中间 24 位。OOO 是十进制文本中的最低 16 位。x 是字面小写 x 字符。
例如,一个 SCID,如果以典型的 BOLT 二进制编码表示为十六进制转储的二进制 blob 083a8400034d0001,则将写为 JSON 字符串 "539268x845x1"。
理由 此格式是 SCID 的可识别且独特的格式,有助于将 SCID 类型与其他类型区分开来。
SECP256K1 点 / 公钥 / Lightning Network 节点 ID
链接:LSPS0.pubkey
Lightning Network 节点 ID 是 SECP256K1 ECC 公钥,它们是 SECP256K1 椭圆曲线上的点,如 BOLT8 所述。
SECP256K1 椭圆曲线点 必须 编码为 JSON 字符串,包含该点压缩 DER 编码的十六进制转储。
压缩 DER 编码是一个 33 字节的表示,因此 JSON 字符串十六进制转储将编码为 66 个字符(十六进制数字)。第一个字节是 0x02(如果 Y 坐标为偶数)或 0x03(如果 Y 坐标为奇数),其余字节是 X 坐标的大端 32 字节整数表示。
在大小写重要的上下文中(例如,如果有哈希或签名提交),则表示 必须 使用小写。否则,读取者 必须 允许小写和大写十六进制数字。
读取者 应该 验证该点确实在 SECP256K1 曲线上。
例如,SECP256K1 生成点 G 将写为 JSON 字符串 "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"。
理由 Bitcoin 和 Lightning 已经标准化了这种格式,并且传统上对十六进制数字使用小写。
Lightning Network 连接字符串
链接:LSPS0.connection_string
Lightning Network 连接字符串描述一个 Lightning Network 节点 ID 和连接到该节点的一种方式。
连接字符串 必须 编码为 JSON 字符串,包含三个部分:
- Lightning Network 节点 ID,编码为压缩 DER 编码的十六进制转储,后跟
@符号。 - 地址,跟在
@符号之后,一直持续到字符串中的最后一个:。允许的地址格式是 BOLT 规范中定义的格式:- IPv4 地址.
- IPv6 地址.
- Torv3 隐藏服务.
- DNS 可解析的名称。
- 端口号,以字符串中的最后一个
:开始,是数字的十进制文本表示。
例如,一个 ID 为 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 的节点,在 IPv6 localhost 上监听默认端口 9735,可以表示为 JSON 字符串:"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798@::1:9735"
读取者 应该 将节点 ID 提取为字符串中直到第一个 @ 字符的部分,将端口号提取为从最后一个 : 字符到字符串末尾的部分,将地址提取为第一个 @ 和最后一个 : 之间的部分,然后分别解析和验证每个部分。
链上地址
链接:LSPS0.onchain_address
链上地址 必须 是 SegWit 地址,从版本 0 到任何未来版本。
链上地址 必须 编码为包含 SegWit 地址的字符串:
读取者 必须 支持,编写者 应该 仅输出:
- 具有 20 字节承诺(P2WPKH)或 32 字节承诺(P2WSH)的 SegWit v0 地址。
- 具有 32 字节承诺(Taproot)的 SegWit v1 地址。
读取者 可以 支持其他 SegWit 版本。
Lightning Network 节点签名
链接:LSPS0.ln_signature
由特定 Lightning Network 节点(具有特定的已知节点 ID)生成的签名 必须 使用 LND signmessage #specinatweet 生成和表示,并编码为包含 zbase32 编码的 JSON 字符串:
zbase32(SigRec(SHA256(SHA256("Lightning Signed Message:" + msg))))。zbase32来自 https://philzimmermann.com/docs/human-oriented-base-32-encoding.txt,SigRec的第一个字节是 31 + recovery id,后跟 64 字节的签名。
理由 这个非 BOLT 规范被广泛实现,并在许多特定协议中常用,通常用于证明某个特定用户拥有某个特定节点,或者作为一种非正式方式减少垃圾信息,要求参与者证明他们拥有一个已发布的 Lightning Network 节点。
CLN、LND 和 Eclair 都有一个 signmessage 命令,允许生成此签名,并且可以使用(非规范)例如 ln-verifymessagejs 实现验证。
其他 LSPS 规范 必须 指定要签名的消息包含字符串 LSPS 后跟 LSPS 规范编号,以及一条人类可读的 ASCII 文本警告,不要手动签署消息。例如,假设的 LSPS999 可能指定:
签名必须签署一条具有以下模板的消息:
"LSPS999: 不要手动签署此消息:我将使用 ${hash} 创建一个 `OP_RETURN` 输出。"
理由
signmessage在许多特定协议中被广泛使用,其中验证者会要求人类操作员通过签署来自验证者的指定消息来证明他们控制一个节点。验证者然后可以提供来自 LSPS 规范的模板,期望获得一个可用于 LSPS 协议的签名,以证明某些不同于人类操作员预期的事情。
日期时间
链接:LSPS0.datetime
现代时期中的特定时间点(“日期时间”)必须 编码为 JSON 字符串,包含 ISO 8601 格式 "YYYY-MM-DDThh:mm:ss.uuuZ"。这些始终是 UTC 时区。
二进制 blob(原始交易、PSBT、Lightning 洋葱等)
链接:LSPS0.binary_blob
二进制 blob 必须 编码为 JSON 字符串,包含二进制 blob 的 Base 64 编码,如 RFC 4648 第 4 节 所述。
必须 使用填充字符 =。
理由 RFC 4648 第 4 节 Base 64 编码中的所有字符都可以嵌入 JSON 字符串而无需任何转义,这种编码只比等效的原始二进制编码长 33.33%,同时具有相当简单的编码和解码实现。
尽管填充是不必要的,因为可以从"分隔符确定 JSON 字符串的长度,但有些 base64 解析器如果未包含填充=字符则会完全拒绝输入,尽管它们毫无用处,正如这条 tweet 所抱怨的那样。
交易 ID
链接:LSPS0.txid
交易 ID(不含见证数据)如 BIP0141 所定义。它 必须 转换为小端顺序以便搜索,并且 必须 编码为 64 字符的十六进制字符串。
理由 我们使用小端格式,以便用户轻松地将 txid 复制粘贴到区块浏览器中。
示例:
{
"txid": "F27C97F46ED7281A3EFA7287410082EBA0CD1424D72703A217E435EA840957B0"
}
输出索引
链接:LSPS0.output_index
output_index 是交易输出 UTXO 的从 0 开始的索引。它最多 16 位 (2 字节)。它 必须 表示为 JSON 整数(数字)。
示例:
{
"output_index": 0
}
输出点
链接:LSPS0.outpoint
一个输出点由一个 LSPS0.txid 和一个 LSPS0.output_index 组成。它在 BOLT0 中定义。它 必须 编码为 JSON 字符串,格式为 txid:output_index。
理由
txid:outpoint格式在区块浏览器中方便使用,可以直接复制粘贴以搜索受影响的 UTXO。
示例:
{
"funding_outpoint": "F27C97F46ED7281A3EFA7287410082EBA0CD1424D72703A217E435EA840957B0:0"
}
错误码
多个 LSPS 中通用的错误码。
001 客户端被拒绝
链接:LSPS0.client_rejected_error
如果 LSP 不想向客户端提供服务,LSP 可以 返回“客户端被拒绝”错误。LSP 可以 例如通过 node_id 或 IP 拒绝客户端。
| 代码 | 消息 | 数据 | 描述 |
|---|---|---|---|
| 001 | 客户端被拒绝 | {"message": %human_message% } | LSP 拒绝了该客户端。 |
%human_message% <字符串>指示为何 LSP 拒绝客户端的人类可读消息。- 可能只是
{ "message": "客户端被拒绝" }。
- 可能只是
- 原文链接: github.com/lightning/bli...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~