BOLT 1:基础协议

  • lightning
  • 发布于 2025-02-25 17:29
  • 阅读 17

本文档是闪电网络BOLT1协议规范,定义了节点间通信的基础协议,包括连接处理、消息格式(类型-长度-值格式)、基本数据类型、设置消息(init、error、warning)和控制消息(ping、pong)等。该协议旨在建立认证和排序的传输机制,保证消息的可靠传输和处理,并允许通过TLV格式进行协议扩展。

BOLT #1: 基础协议

概述

本协议假定存在一个底层经过身份验证且排序的传输机制,该机制负责构建单个消息的框架。 BOLT #8 规定了 Lightning 中使用的规范传输层,尽管它可以被任何满足上述保证的传输层所取代。

默认的 TCP 端口是 9735。这对应于十六进制 0x2607:LIGHTNING 的 Unicode 代码点。<sup>1</sup>

除非另有说明,所有数据字段均为无符号大端序。

目录

连接处理和多路复用

实现 必须 为每个 peer 使用单个连接;通道消息(包括通道 ID)通过此单个连接进行多路复用。

闪电消息格式

解密后,所有闪电消息的格式如下:

  1. type:一个 2 字节的大端序字段,指示消息的类型
  2. payload:一个可变长度的 payload,它构成了消息的其余部分,并且符合与 type 匹配的格式
  3. extension:一个可选的 TLV 流

type 字段指示如何解释 payload 字段。 每种单独类型的格式由本仓库中的规范定义。 类型遵循 可以为奇数 规则,因此节点 可以 发送 数编号的类型,而无需确定接收者是否理解它。

消息在逻辑上分为五组,按设置的最高有效位排序:

  • 设置和控制(类型 0-31):与连接设置、控制、支持的功能和错误报告相关的消息(如下所述)
  • 通道(类型 32-127):用于设置和关闭微支付通道的消息(在 BOLT #2 中描述)
  • 承诺(类型 128-255):与更新当前承诺交易相关的消息,包括添加、撤销和结算 HTLC,以及更新费用和交换签名(在 BOLT #2 中描述)
  • 路由(类型 256-511):包含节点和通道公告的消息,以及任何主动路由探索(在 BOLT #7 中描述)
  • 自定义(类型 32768-65535):实验性和特定于应用程序的消息

消息的大小由传输层要求适合 2 字节的无符号整数;因此,最大可能大小为 65535 字节。

发送节点:

  • 必须不 在没有事先协商的情况下发送此处未列出的偶数类型消息。
  • 必须不 在没有事先协商的情况下在 extension 中发送偶数类型 TLV 记录。
  • 在本规范中协商了一个选项:
    • 必须 包含所有用该选项注释的字段。
  • 在定义自定义消息时:
    • 应该 选择一个随机 type 以避免与其他自定义类型冲突。
    • 应该 选择一个 type,该 type 不与其他实验冲突 此问题 中列出。
    • 应该 在常规节点应忽略额外数据时选择奇数 type 标识符。
    • 应该 在常规节点应拒绝该消息并关闭连接时选择偶数 type 标识符。

接收节点:

  • 收到 奇数 的未知类型的消息时:
    • 必须 忽略收到的消息。
  • 收到 偶数 的未知类型的消息时:
    • 必须 关闭连接。
    • 可以 使通道失败。
  • 收到内容长度不足的已知消息时:
    • 必须 关闭连接。
    • 可以 使通道失败。
  • 收到带有 extension 的消息时:
    • 可以 忽略 extension
    • 否则,如果 extension 无效:
      • 必须 关闭连接。
      • 可以 使通道失败。
默认情况下,“SHA2”和比特币公钥都编码为大端序,因此对其他字段使用不同的端序是不寻常的。

长度受密码包装的限制为 65535 字节,并且协议中的消息无论如何都不会超过该长度。

_可以为奇数_ 规则允许在将来进行可选扩展,而无需客户端进行协商或特殊编码。`extension` 字段类似地允许将来的扩展,方法是让发送者包含额外的 TLV 数据。请注意,只有当消息 `payload` 尚未达到 65535 字节的最大长度时,才能添加 `extension` 字段。

实现可能希望使消息数据与 8 字节边界对齐(此处任何类型的最大自然对齐要求);但是,在类型字段之后添加 6 字节的填充被认为是浪费:可以通过将消息解密到具有 6 字节预填充的缓冲区中来实现对齐。

类型-长度-值 格式

在整个协议中,使用 TLV(类型-长度-值)格式以允许向后兼容地向现有消息类型添加新字段。

tlv_record 表示单个字段,以以下形式编码:

  • [bigsize: type]
  • [bigsize: length]
  • [length: value]

tlv_stream 是一系列(可能为零)tlv_record,表示为编码的 tlv_record 的串联。当用于扩展现有消息时,tlv_stream 通常放置在所有当前定义的字段之后。

type 使用 BigSize 格式编码。它用作 tlv_record 的特定于消息的 64 位标识符,用于确定应如何解码 value 的内容。低于 2^16 的 type 标识符保留供本规范使用。大于或等于 2^16 的 type 标识符可用于自定义记录。本规范中未定义的任何记录都被视为自定义记录。这包括实验性和特定于应用程序的消息。

length 使用 BigSize 格式编码,以字节为单位表示 value 的大小。

value 完全取决于 type,并且应根据 type 确定的特定于消息的格式进行编码或解码。

要求

发送节点:

  • 必须 按严格递增的 typetlv_stream 中的 tlv_record 进行排序,因此 必须不 生成多个具有相同 type 的 TLV 记录
  • 必须 以最小方式编码 typelength
  • 在定义自定义记录 type 标识符时:
    • 应该 选择随机 type 标识符以避免与其他自定义类型冲突。
    • 应该 在常规节点应忽略其他数据时选择奇数 type 标识符。
    • 应该 在常规节点应拒绝包含自定义记录的完整 tlv 流时选择偶数 type 标识符。
  • 不应该tlv_record 中使用冗余的可变长度编码。

接收节点:

  • 如果在解析 type 之前剩余零字节:
    • 必须 停止解析 tlv_stream
  • 如果 typelength 未以最小方式编码:
    • 必须 无法解析 tlv_stream
  • 如果解码的 type 不是严格递增的(包括遇到相同 type 的两次或多次出现的情况):
    • 必须 无法解析 tlv_stream
  • 如果 length 超过消息中剩余的字节数:
    • 必须 无法解析 tlv_stream
  • 如果 type 是已知的:
    • 必须 使用 type 的已知编码解码接下来的 length 字节。
    • 如果 lengthtype 的已知编码所需的长度不完全相等:
      • 必须 无法解析 tlv_stream
    • 如果 type 的已知编码中的可变长度字段不是最小的:
      • 必须 无法解析 tlv_stream
  • 否则,如果 type 是未知的:
    • 如果 type 是偶数:
      • 必须 无法解析 tlv_stream
    • 否则,如果 type 是奇数:
      • 必须 丢弃接下来的 length 字节。
使用 TLV 的主要优点是,读取器能够忽略它不理解的新字段,因为每个字段都带有编码元素的精确大小。如果没有 TLV,即使节点不希望使用特定字段,该节点也必须为该字段添加解析逻辑,以便确定任何后续字段的偏移量。

严格的单调性约束确保所有 `type` 都是唯一的,并且最多可以出现一次。映射到复杂对象(例如向量、映射或结构)的字段应通过定义编码来确保对象在单个 `tlv_record` 中序列化。唯一性约束除其他外,还支持以下优化:
  - 规范排序的定义独立于编码的 `value`。
  - 规范排序可以在编译时知道,而不是在编码时动态确定。
  - 验证规范排序需要较少的状态并且成本较低。
  - 可变大小的字段可以预先保留其预期大小,而不是按顺序附加元素并导致双重复制开销。

对 `type` 和 `length` 使用 bigsize 允许节省小 `type` 或短 `value` 的空间。这可能会为线路或 onion payload 上的应用程序数据留下更多空间。

所有 `type` 必须按递增顺序出现,以创建基础 `tlv_record` 的规范编码。这在计算 `tlv_stream` 上的签名时至关重要,因为它确保验证者能够重新计算与签名者相同的消息摘要。请注意,即使验证者不了解字段包含的内容,也可以强制执行字段集上的规范排序。

编写者应避免在 `tlv_record` 中使用冗余的可变长度编码,因为这会导致长度被编码两次并使计算外部长度复杂化。例如,在编写可变长度字节数组时,`value` 应仅包含原始字节,并放弃额外的内部长度,因为 `tlv_record` 已经携带了后面的字节数。另一方面,如果 `tlv_record` 包含多个可变长度元素,则这不被认为是冗余的,并且需要允许接收者从 `value` 解析单个元素。

基本类型

消息规范中引用了各种基本类型:

  • byte:一个 8 位字节
  • u16:一个 2 字节的无符号整数
  • u32:一个 4 字节的无符号整数
  • u64:一个 8 字节的无符号整数

在包含单个值的 TLV 记录中,可以省略整数中的前导零:

  • tu16:一个 0 到 2 字节的无符号整数
  • tu32:一个 0 到 4 字节的无符号整数
  • tu64:一个 0 到 8 字节的无符号整数

当用于编码金额时,前面的字段 必须 符合 2100 万 BTC 的上限:

  • satoshi 金额 必须 最多为 0x000775f05a074000
  • milli-satoshi 金额 必须 最多为 0x1d24b2dfac520000

还定义了以下便捷类型:

  • chain_hash:一个 32 字节的链标识符(参见 BOLT #0
  • channel_id:一个 32 字节的 channel_id(参见 BOLT #2
  • sha256:一个 32 字节的 SHA2-256 哈希
  • signature:一个 64 字节的比特币椭圆曲线签名
  • point:一个 33 字节的椭圆曲线点(根据 SEC 1 标准 进行压缩编码)
  • short_channel_id:一个 8 字节的值,用于标识通道(参见 BOLT #7
  • bigsize:一个可变长度的无符号整数,类似于比特币的 CompactSize 编码,但采用大端序。在 BigSize 中描述。

设置消息

init 消息

身份验证完成后,即使这是重新连接,第一条消息也会揭示此节点支持或需要的功能。

BOLT #9 指定了功能列表。每个功能通常由 2 位表示。最低有效位编号为 0,它是 偶数,下一个最高有效位编号为 1,它是 奇数。由于历史原因,功能分为全局和本地功能位掩码。

features 字段 必须 用 0 填充到字节。

  1. 类型:16 (init)

  2. 数据:

    • [u16:gflen]
    • [gflen*byte:globalfeatures]
    • [u16:flen]
    • [flen*byte:features]
    • [init_tlvs:tlvs]
  3. tlv_stream: init_tlvs

  4. 类型:

    1. 类型:1 (networks)

    2. 数据:

      • [...*chain_hash:chains]
    3. 类型:3 (remote_addr)

    4. 数据:

      • address descriptor (1 字节类型和数据,参见 BOLT 7)

可选的 networks 指示节点感兴趣的链。 可选的 remote_addr 可用于规避 NAT 问题。

要求

发送节点:

  • 必须 发送 init 作为任何连接的第一条闪电消息。
  • 必须 按照 BOLT #9 中的定义设置功能位。
  • 必须 将任何未定义的功能位设置为 0。
  • 不应该globalfeatures 中设置大于 13 的功能。
  • 应该 使用表示 features 字段所需的最小长度。
  • 应该networks 设置为其将进行 gossip 或打开通道的所有链。
  • 应该remote_addr 设置为反映传入连接的远程 IP 地址(和端口),如果节点是接收方并且连接是通过 IP 完成的。
  • 不应该 将私有地址设置为 remote_addr

接收节点:

  • 必须 等待接收 init 才能发送任何其他消息。
  • 必须 将两个功能位图组合(逻辑 OR)为一个逻辑 features 映射。
  • 必须 按照 BOLT #9 中的规定响应已知的功能位。
  • 收到未知的 奇数 非零功能位时:
    • 必须 忽略该位。
  • 收到未知的 偶数 非零功能位时:
    • 必须 关闭连接。
  • 收到不包含任何公共链的 networks
    • 可以 关闭连接。
  • 如果功能向量未设置所有已知的传递依赖项:
    • 必须 关闭连接。
  • 可以 使用 remote_addr 来更新其 node_annoucement
这里曾经有两个功能位字段,但为了向后兼容,它们现在合并为一个。

这种语义允许未来的不兼容更改和未来的向后兼容更改。位通常应成对分配,以便可选功能以后可能变为强制性。

节点等待接收对方的功能,以简化功能不兼容时的错误诊断。

由于所有网络共享同一个端口,但大多数实现仅支持单个网络,因此 `networks` 字段避免节点错误地认为它们将收到有关其首选网络的更新,或者它们可以打开通道。

errorwarning 消息

为了简化诊断,告诉 peer 某些事情不正确通常很有用。

  1. 类型:17 (error)

  2. 数据:

    • [channel_id:channel_id]
    • [u16:len]
    • [len*byte:data]
  3. 类型:1 (warning)

  4. 数据:

    • [channel_id:channel_id]
    • [u16:len]
    • [len*byte:data]
要求

通道由 channel_id 引用,除非 channel_id 为 0(即所有字节均为 0),在这种情况下,它引用所有通道。

融资节点:

  • 对于在 funding_created 消息之前(包括该消息)发送的所有错误消息:
    • 必须 使用 temporary_channel_id 代替 channel_id

被融资节点:

  • 对于在 funding_signed 消息之前(不包括该消息)发送的所有错误消息:
    • 必须 使用 temporary_channel_id 代替 channel_id

发送节点:

  • 应该 为协议冲突或内部错误发送 error,这些错误使通道无法使用或使进一步的通信无法使用。
  • 应该 发送带有未知 channel_iderror,以回复与未知通道相关的类型 32-255 的消息。
  • 发送 error 时:
    • 必须 使错误消息引用的通道失败。
    • 可以channel_id 设置为全零以指示所有通道。
  • 发送 warning 时:
    • 如果警告与特定通道无关,则 可以channel_id 设置为全零。
  • 可以 在发送后关闭连接。
  • 可以 发送一个空的 data 字段。
  • 当故障是由无效签名检查引起的时:
    • 应该 在回复 funding_createdfunding_signedclosing_signedcommitment_signed 消息时,包含原始的十六进制编码事务。

接收节点:

  • 收到 error 时:
    • 如果 channel_id 全为零:
      • 必须 使与发送节点的所有通道失败。
    • 否则:
      • 必须 使 channel_id 引用的通道失败(如果该通道与发送节点之间存在)。
  • 收到 warning 时:
    • 应该 记录该消息以供日后诊断。
    • 可以 断开连接。
    • 可以 在延迟一段时间后重新连接以重试。
    • 可以 在此时允许的情况下尝试 shutdown
  • 如果 channel_id 未引用任何现有通道:
    • 必须 忽略该消息。
  • 如果 data 并非完全由可打印的 ASCII 字符组成(参考:可打印字符集包括字节值 32 到 126,包括首尾):
    • 不应该 按原样打印出 data
有些错误是无法恢复的,需要中止对话;如果连接只是被丢弃,那么 peer 可能会重试连接。描述协议冲突以进行诊断也很有用,因为这表明一个 peer 存在 bug。

另一方面,过度使用错误消息已导致实现忽略它们(以避免代价高昂的通道中断),因此添加了“warning”消息,以允许在一定程度上重试或恢复虚假错误。

明智的做法可能是在生产设置中不区分错误,以免泄露信息——因此,提供了可选的 `data` 字段。

控制消息

pingpong 消息

为了允许长期存在的 TCP 连接的存在,有时可能需要在应用程序级别上保持两端的 TCP 连接处于活动状态。此类消息还可以模糊流量模式。

  1. 类型:18 (ping)
  2. 数据:
    • [u16:num_pong_bytes]
    • [u16:byteslen]
    • [byteslen*byte:ignored]

每当收到 ping 消息时,都应发送 pong 消息。它用作回复,并且还用于保持连接处于活动状态,同时显式通知另一端接收者仍处于活动状态。在收到的 ping 消息中,发送方将指定要包含在 pong 消息的数据 payload 中的字节数。

  1. 类型:19 (pong)
  2. 数据:
    • [u16:byteslen]
    • [byteslen*byte:ignored]
要求

发送 ping 消息的节点:

  • 应该ignored 设置为 0。
  • 必须不ignored 设置为敏感数据,例如密钥或初始化的部分内存。
  • 如果未收到相应的 pong
    • 可以 关闭网络连接,
      • 并且在这种情况下 必须不 使通道失败。

发送 pong 消息的节点:

  • 应该ignored 设置为 0。
  • 必须不ignored 设置为敏感数据,例如密钥或初始化的部分内存。

接收 ping 消息的节点:

  • 如果 num_pong_bytes 小于 65532:
    • 必须 通过发送 pong 消息来响应,其中 byteslen 等于 num_pong_bytes
  • 否则(num_pong_bytes 小于 65532):
    • 必须 忽略 ping

接收 pong 消息的节点:

  • 如果 byteslen 与其发送的任何 pingnum_pong_bytes 值不对应:
    • 可以 关闭连接。
最大的可能消息是 65535 字节;因此,最大明智的 `byteslen` 是 65531——为了考虑类型字段(`pong`)和 `byteslen` 本身。这允许为 `num_pong_bytes` 提供一个方便的截止值,以指示不应发送任何回复。

网络中节点之间的连接可能是长期的,因为支付通道具有无限的生命周期。但是,在连接的生命周期内,很可能不会交换任何新数据。此外,在某些平台上,Lightning 客户端可能会在没有事先警告的情况下进入休眠状态。因此,使用不同的 `ping` 消息来探测另一侧连接的活动状态,并保持已建立的连接处于活动状态。

此外,发送方请求接收方发送具有特定字节数的响应的能力使网络上的节点能够创建_合成_流量。这种流量可用于部分防御数据包和时间分析——因为节点可以伪造典型交换的流量模式,而无需对其各自的通道应用任何真正的更新。

当与 [BOLT #4](https://learnblockchain.cn/article/18119) 中定义的 onion 路由协议相结合时,仔细的统计驱动的合成流量可以进一步增强网络中参与者的隐私。

建议采取有限的预防措施来防止 `ping` 洪水,但是由于网络延迟,会给出一些余地。请注意,还有其他传入流量洪水的方法(例如,发送_奇数_未知消息类型或最大程度地填充每条消息)。

最后,定期使用 `ping` 消息有助于促进 [BOLT #8](https://learnblockchain.cn/article/18120) 中指定的频繁密钥轮换。

附录 A:BigSize 测试向量

以下测试向量可用于断言 TLV 格式中使用的 BigSize 实现的正确性。该格式与比特币中使用的 CompactSize 编码相同,但将多字节值的小端序编码替换为大端序。

使用 BigSize 编码的值将产生 1、3、5 或 9 个字节的编码,具体取决于整数的大小。该编码是一个分段函数,它采用一个 uint64x 并产生:

        uint8(x)                if x &lt; 0xfd
        0xfd + be16(uint16(x))  if x &lt; 0x10000
        0xfe + be32(uint32(x))  if x &lt; 0x100000000
        0xff + be64(x)          otherwise.

这里 + 表示串联,be16be32be64 分别生成 16 位、32 位和 64 位整数的输入的大端序编码。

如果一个值无法使用更少的字节进行编码,则称该值被 最小编码。例如,占用 5 个字节但其值小于 0x10000 的 BigSize 编码不是最小编码的。应检查使用 BigSize 解码的所有值,以确保它们是最小编码的。

BigSize 解码测试

以下是如何执行 BigSize 解码测试的示例。

func testReadBigSize(t *testing.T, test bigSizeTest) {
        var buf [8]byte
        r := bytes.NewReader(test.Bytes)
        val, err := tlv.ReadBigSize(r, &buf)
        if err != nil && err.Error() != test.ExpErr {
                t.Fatalf("expected decoding error: %v, got: %v",
                        test.ExpErr, err)
        }

        // 如果我们预期会出现解码错误,那么检查该值就没有意义。
        if test.ExpErr != "" {
                return
        }

        if val != test.Value {
                t.Fatalf("expected value: %d, got %d", test.Value, val)
        }
}

正确的实现应通过这些测试向量:

[
    {
        "name": "zero",
        "value": 0,
        "bytes": "00"
    },
    {
        "name": "one byte high",
        "value": 252,
        "bytes": "fc"
    },
    {
        "name": "two byte low",
        "value": 253,
        "bytes": "fd00fd"
    },
    {
        "name": "two byte high",
        "value": 65535,
        "bytes": "fdffff"
    },
    {
        "name": "four byte low",
        "value": 65536,
        "bytes": "fe00010000"
    },
    {
        "name": "four byte high",
        "value": 4294967295,
        "bytes": "feffffffff"
    },
    {
        "name": "eight byte low",
        "value": 4294967296,
        "bytes": "ff0000000100000000"
    },
    {
        "name": "eight byte high",
        "value": 18446744073709551615,
        "bytes": "ffffffffffffffffff"
    },
    {
        "name": "two byte not canonical",
        "value": 0,
        "bytes": "fd00fc",
        "exp_error": "decoded bigsize is not canonical"
    },
    {
        "name": "four byte not canonical",
        "value": 0,
        "bytes": "fe0000ffff",
        "exp_error": "decoded bigsize is not canonical"
    },
    {
        "name": "eight byte not canonical",
        "value": 0,
        "bytes": "ff00000000ffffffff",
        "exp_error": "decoded bigsize is not canonical"
    },
    {
        "name": "two byte short read",
        "value": 0,
        "bytes": "fd00",
        "exp_error": "unexpected EOF"
    },
    {
        "name": "four byte short read",
        "value": 0,
        "bytes": "feffff",
        "exp_error": "unexpected EOF"
    },
    {
        "name": "eight byte short read",
        "value": 0,
        "bytes": "ffffffffff",
        "exp_error": "unexpected EOF"
    },
    {
        "name": "one byte no read",
        "value": 0,
        "bytes": "",
        "exp_error": "EOF"
    },
    {
        "name": "two byte no read",
        "value": 0,
        "bytes": "fd",
        "exp_error": "unexpected EOF"
    },
    {
        "name": "four byte no read",
        "value": 0,
        "bytes": "fe",
        "exp_error": "unexpected EOF"
    },
    {
        "name": "eight byte no read",
        "value": 0,
        "bytes": "ff",
        "exp_error": "unexpected EOF"
    }
]

BigSize 编码测试

以下是如何执行 BigSize 编码测试的示例。

func testWriteBigSize(t *testing.T, test bigSizeTest) {
        var (
                w   bytes.Buffer
                buf [8]byte
        )
        err := tlv.WriteBigSize(&w, test.Value, &buf)
        if err != nil {
                t.Fatalf("unable to encode %d as bigsize: %v",
                        test.Value, err)
        }

        if bytes.Compare(w.Bytes(), test.Bytes) != 0 {
                t.Fatalf("expected bytes: %v, got %v",
                        test.Bytes, w.Bytes())
        }
}

正确的实现应通过以下测试向量:

[
    {
        "name": "zero",
        "value": 0,
        "bytes": "00"
    },
    {
        "name": "one byte high",
        "value": 252,
        "bytes": "fc"
    },
    {
        "name": "two byte low",
        "value": 253,
        "bytes": "fd00fd"
    },
    {
        "name": "two byte high",
        "value": 65535,
        "bytes": "fdffff"
    },
    {
        "name": "four byte low",
        "value": 65536,
        "bytes": "fe00010000"
    },
    {
        "name": "four byte high",
        "value": 4294967295,
        "bytes": "feffffffff"
    },
    {
        "name": "eight byte low",
        "value": 4294967296,
        "bytes": "ff0000000100000000"
    },
    {
        "name": "eight byte high",
        "value": 18446744073709551615,
        "bytes": "ffffffffffffffffff"
    }
]

附录 B:类型-长度-值 测试向量

以下测试假定存在两个独立的 TLV 命名空间:n1 和 n2。

n1 命名空间支持以下 TLV 类型:

  1. tlv_stream: n1
  2. 类型:
    1. 类型:1 (tlv1)
    2. 数据:
      • [tu64:amount_msat]
    3. 类型:2 (tlv2)
    4. 数据:
      • [short_channel_id:scid]
    5. 类型:3 (tlv3)
    6. 数据:
      • [point:node_id]
      • [u64:amount_msat_1]
      • [u64:amount_msat_2]
    7. 类型:254 (tlv4)
    8. 数据:
      • [u16:cltv_delta]

n2 命名空间支持以下 TLV 类型:

  1. tlv_stream: n2
  2. 类型:
    1. 类型: 0 (tlv1)
    2. 数据:
      • [tu64:amount_msat]
    3. 类型: 11 (tlv2)
    4. 数据:
      • [tu32:cltv_expiry]

TLV解码失败

任何命名空间中的以下TLV流都应触发解码失败:

  1. 无效流: 0xfd
  2. 原因: 类型被截断
1. 无效流: 0xfd01
2. 原因: 类型被截断
1. 无效流: 0xfd0001 00
2. 原因: 不是最小编码类型
1. 无效流: 0xfd0101
2. 原因: 缺少长度
1. 无效流: 0x0f fd
2. 原因: (长度被截断)
1. 无效流: 0x0f fd26
2. 原因: (长度被截断)
1. 无效流: 0x0f fd2602
2. 原因: 缺少值
1. 无效流: 0x0f fd0001 00
2. 原因: 不是最小编码长度
1. 无效流: 0x0f fd0201 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
2. 原因: 值被截断

以下任一命名空间中的TLV流应触发解码失败:

1. 无效流: 0x12 00
2. 原因: 未知的偶数类型。
1. 无效流: 0xfd0102 00
2. 原因: 未知的偶数类型。
1. 无效流: 0xfe01000002 00
2. 原因: 未知的偶数类型。
1. 无效流: 0xff0100000000000002 00
2. 原因: 未知的偶数类型。

命名空间 n1 中的以下TLV流应触发解码失败:

1. 无效流: 0x01 09 ffffffffffffffffff
2. 原因: 大于 `n1` 的 `tlv1` 的编码长度。
1. 无效流: 0x01 01 00
2. 原因: `n1` 的 `tlv1` 的 `amount_msat` 的编码不是最小的
1. 无效流: 0x01 02 0001
2. 原因: `n1` 的 `tlv1` 的 `amount_msat` 的编码不是最小的
1. 无效流: 0x01 03 000100
2. 原因: `n1` 的 `tlv1` 的 `amount_msat` 的编码不是最小的
1. 无效流: 0x01 04 00010000
2. 原因: `n1` 的 `tlv1` 的 `amount_msat` 的编码不是最小的
1. 无效流: 0x01 05 0001000000
2. 原因: `n1` 的 `tlv1` 的 `amount_msat` 的编码不是最小的
1. 无效流: 0x01 06 000100000000
2. 原因: `n1` 的 `tlv1` 的 `amount_msat` 的编码不是最小的
1. 无效流: 0x01 07 00010000000000
2. 原因: `n1` 的 `tlv1` 的 `amount_msat` 的编码不是最小的
1. 无效流: 0x01 08 0001000000000000
2. 原因: `n1` 的 `tlv1` 的 `amount_msat` 的编码不是最小的
1. 无效流: 0x02 07 01010101010101
2. 原因: 小于 `n1` 的 `tlv2` 的编码长度。
1. 无效流: 0x02 09 010101010101010101
2. 原因: 大于 `n1` 的 `tlv2` 的编码长度。
1. 无效流: 0x03 21 023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb
2. 原因: 小于 `n1` 的 `tlv3` 的编码长度。
1. 无效流: 0x03 29 023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb0000000000000001
2. 原因: 小于 `n1` 的 `tlv3` 的编码长度。
1. 无效流: 0x03 30 023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb000000000000000100000000000001
2. 原因: 小于 `n1` 的 `tlv3` 的编码长度。
1. 无效流: 0x03 31 043da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb00000000000000010000000000000002
2. 原因: `n1` 的 `node_id` 不是有效的点。
1. 无效流: 0x03 32 023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb0000000000000001000000000000000001
2. 原因: 大于 `n1` 的 `tlv3` 的编码长度。
1. 无效流: 0xfd00fe 00
2. 原因: 小于 `n1` 的 `tlv4` 的编码长度。
1. 无效流: 0xfd00fe 01 01
2. 原因: 小于 `n1` 的 `tlv4` 的编码长度。
1. 无效流: 0xfd00fe 03 010101
2. 原因: 大于 `n1` 的 `tlv4` 的编码长度。
1. 无效流: 0x00 00
2. 原因: `n1` 命名空间的未知偶数字段。

TLV解码成功

以下任一命名空间中的TLV流都应正确解码,并被忽略:

1. 有效流: 0x
2. 解释: 空消息
1. 有效流: 0x21 00
2. 解释: 未知的奇数类型。
1. 有效流: 0xfd0201 00
2. 解释: 未知的奇数类型。
1. 有效流: 0xfd00fd 00
2. 解释: 未知的奇数类型。
1. 有效流: 0xfd00ff 00
2. 解释: 未知的奇数类型。
1. 有效流: 0xfe02000001 00
2. 解释: 未知的奇数类型。
1. 有效流: 0xff0200000000000001 00
2. 解释: 未知的奇数类型。

n1 命名空间中的以下TLV流应正确解码,并在此处给出值:

1. 有效流: 0x01 00
2. 值: `tlv1` `amount_msat`=0
1. 有效流: 0x01 01 01
2. 值: `tlv1` `amount_msat`=1
1. 有效流: 0x01 02 0100
2. 值: `tlv1` `amount_msat`=256
1. 有效流: 0x01 03 010000
2. 值: `tlv1` `amount_msat`=65536
1. 有效流: 0x01 04 01000000
2. 值: `tlv1` `amount_msat`=16777216
1. 有效流: 0x01 05 0100000000
2. 值: `tlv1` `amount_msat`=4294967296
1. 有效流: 0x01 06 010000000000
2. 值: `tlv1` `amount_msat`=1099511627776
1. 有效流: 0x01 07 01000000000000
2. 值: `tlv1` `amount_msat`=281474976710656
1. 有效流: 0x01 08 0100000000000000
2. 值: `tlv1` `amount_msat`=72057594037927936
1. 有效流: 0x02 08 0000000000000226
2. 值: `tlv2` `scid`=0x0x550
1. 有效流: 0x03 31 023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb00000000000000010000000000000002
2. 值: `tlv3` `node_id`=023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb `amount_msat_1`=1 `amount_msat_2`=2
1. 有效流: 0xfd00fe 02 0226
2. 值: `tlv4` `cltv_delta`=550

TLV流解码失败

将无效流附加到有效流的任何操作都应触发解码失败。

将编号较高的有效流附加到编号较低的有效流的任何操作都不应触发解码失败。

此外,命名空间 n1 中的以下TLV流应触发解码失败:

1. 无效流: 0x02 08 0000000000000226 01 01 2a
2. 原因: 有效的TLV记录,但排序无效
1. 无效流: 0x02 08 0000000000000231 02 08 0000000000000451
2. 原因: 重复的TLV类型
1. 无效流: 0x1f 00 0f 01 2a
2. 原因: 有效(已忽略)的TLV记录,但排序无效
1. 无效流: 0x1f 00 1f 01 2a
2. 原因: 重复的TLV类型(已忽略)

命名空间 n2 中的以下TLV流应触发解码失败:

1. 无效流: 0xffffffffffffffffff 00 00 00
2. 原因: 有效的TLV记录,但排序无效

附录 C:消息扩展

本节包含关于 init 消息的有效和无效扩展的示例。 这些示例的基本 init 消息(没有扩展)是 0x001000000000 (所有功能均已关闭)。

以下 init 消息有效:

  • 0x001000000000: 未提供扩展
  • 0x001000000000c9012acb0104: 扩展包含两个未知的 奇数 TLV记录(类型为 0xc90xcb

以下 init 消息无效:

  • 0x00100000000001: 扩展存在但被截断
  • 0x001000000000ca012a: 扩展包含未知的 偶数 TLV记录(假设TLV类型 0xca 是未知的)
  • 0x001000000000c90101c90102: 扩展TLV流无效(重复的TLV记录类型 0xc9

请注意,当消息被签名时,扩展 是签名字节的一部分。 节点应存储 扩展 字节,即使他们不理解它们,以便能够正确验证签名。

致谢

[ TODO: (roasbeef); fin ]

参考文献

  1. <a id="reference-1">http://www.unicode.org/charts/PDF/U2600.pdf&lt;/a>

作者

[ FIXME: 插入作者列表 ]

Creative Commons License <br> 本作品采用 知识共享署名 4.0 国际许可协议 进行许可。

  • 原文链接: github.com/lightning/bol...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
lightning
lightning
江湖只有他的大名,没有他的介绍。