本文档是闪电网络BOLT1协议规范,定义了节点间通信的基础协议,包括连接处理、消息格式(类型-长度-值格式)、基本数据类型、设置消息(init、error、warning)和控制消息(ping、pong)等。该协议旨在建立认证和排序的传输机制,保证消息的可靠传输和处理,并允许通过TLV格式进行协议扩展。
本协议假定存在一个底层经过身份验证且排序的传输机制,该机制负责构建单个消息的框架。 BOLT #8 规定了 Lightning 中使用的规范传输层,尽管它可以被任何满足上述保证的传输层所取代。
默认的 TCP 端口是 9735。这对应于十六进制 0x2607
:LIGHTNING 的 Unicode 代码点。<sup>1</sup>
除非另有说明,所有数据字段均为无符号大端序。
实现 必须 为每个 peer 使用单个连接;通道消息(包括通道 ID)通过此单个连接进行多路复用。
解密后,所有闪电消息的格式如下:
type
:一个 2 字节的大端序字段,指示消息的类型payload
:一个可变长度的 payload,它构成了消息的其余部分,并且符合与 type
匹配的格式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
确定的特定于消息的格式进行编码或解码。
发送节点:
type
对 tlv_stream
中的 tlv_record
进行排序,因此 必须不 生成多个具有相同 type
的 TLV 记录type
和 length
。type
标识符时:
type
标识符以避免与其他自定义类型冲突。type
标识符。type
标识符。tlv_record
中使用冗余的可变长度编码。接收节点:
type
之前剩余零字节:
tlv_stream
。type
或 length
未以最小方式编码:
tlv_stream
。type
不是严格递增的(包括遇到相同 type
的两次或多次出现的情况):
tlv_stream
。length
超过消息中剩余的字节数:
tlv_stream
。type
是已知的:
type
的已知编码解码接下来的 length
字节。length
与 type
的已知编码所需的长度不完全相等:
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 的上限:
0x000775f05a074000
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 填充到字节。
类型:16 (init
)
数据:
u16
:gflen
]gflen*byte
:globalfeatures
]u16
:flen
]flen*byte
:features
]init_tlvs
:tlvs
]tlv_stream
: init_tlvs
类型:
类型:1 (networks
)
数据:
...*chain_hash
:chains
]类型:3 (remote_addr
)
数据:
address descriptor
(1 字节类型和数据,参见 BOLT 7)可选的 networks
指示节点感兴趣的链。
可选的 remote_addr
可用于规避 NAT 问题。
发送节点:
init
作为任何连接的第一条闪电消息。globalfeatures
中设置大于 13 的功能。features
字段所需的最小长度。networks
设置为其将进行 gossip 或打开通道的所有链。remote_addr
设置为反映传入连接的远程 IP 地址(和端口),如果节点是接收方并且连接是通过 IP 完成的。remote_addr
。接收节点:
init
才能发送任何其他消息。features
映射。networks
时
remote_addr
来更新其 node_annoucement
这里曾经有两个功能位字段,但为了向后兼容,它们现在合并为一个。
这种语义允许未来的不兼容更改和未来的向后兼容更改。位通常应成对分配,以便可选功能以后可能变为强制性。
节点等待接收对方的功能,以简化功能不兼容时的错误诊断。
由于所有网络共享同一个端口,但大多数实现仅支持单个网络,因此 `networks` 字段避免节点错误地认为它们将收到有关其首选网络的更新,或者它们可以打开通道。
error
和 warning
消息为了简化诊断,告诉 peer 某些事情不正确通常很有用。
类型:17 (error
)
数据:
channel_id
:channel_id
]u16
:len
]len*byte
:data
]类型:1 (warning
)
数据:
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_id
的 error
,以回复与未知通道相关的类型 32
-255
的消息。error
时:
channel_id
设置为全零以指示所有通道。warning
时:
channel_id
设置为全零。data
字段。funding_created
、funding_signed
、closing_signed
或 commitment_signed
消息时,包含原始的十六进制编码事务。接收节点:
error
时:
channel_id
全为零:
channel_id
引用的通道失败(如果该通道与发送节点之间存在)。warning
时:
shutdown
。channel_id
未引用任何现有通道:
data
并非完全由可打印的 ASCII 字符组成(参考:可打印字符集包括字节值 32 到 126,包括首尾):
data
。有些错误是无法恢复的,需要中止对话;如果连接只是被丢弃,那么 peer 可能会重试连接。描述协议冲突以进行诊断也很有用,因为这表明一个 peer 存在 bug。
另一方面,过度使用错误消息已导致实现忽略它们(以避免代价高昂的通道中断),因此添加了“warning”消息,以允许在一定程度上重试或恢复虚假错误。
明智的做法可能是在生产设置中不区分错误,以免泄露信息——因此,提供了可选的 `data` 字段。
ping
和 pong
消息为了允许长期存在的 TCP 连接的存在,有时可能需要在应用程序级别上保持两端的 TCP 连接处于活动状态。此类消息还可以模糊流量模式。
ping
)u16
:num_pong_bytes
]u16
:byteslen
]byteslen*byte
:ignored
]每当收到 ping
消息时,都应发送 pong
消息。它用作回复,并且还用于保持连接处于活动状态,同时显式通知另一端接收者仍处于活动状态。在收到的 ping
消息中,发送方将指定要包含在 pong
消息的数据 payload 中的字节数。
pong
)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
与其发送的任何 ping
的 num_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) 中指定的频繁密钥轮换。
以下测试向量可用于断言 TLV 格式中使用的 BigSize 实现的正确性。该格式与比特币中使用的 CompactSize 编码相同,但将多字节值的小端序编码替换为大端序。
使用 BigSize 编码的值将产生 1、3、5 或 9 个字节的编码,具体取决于整数的大小。该编码是一个分段函数,它采用一个 uint64
值 x
并产生:
uint8(x) if x < 0xfd
0xfd + be16(uint16(x)) if x < 0x10000
0xfe + be32(uint32(x)) if x < 0x100000000
0xff + be64(x) otherwise.
这里 +
表示串联,be16
、be32
和 be64
分别生成 16 位、32 位和 64 位整数的输入的大端序编码。
如果一个值无法使用更少的字节进行编码,则称该值被 最小编码。例如,占用 5 个字节但其值小于 0x10000 的 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 编码测试的示例。
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"
}
]
以下测试假定存在两个独立的 TLV 命名空间:n1 和 n2。
n1 命名空间支持以下 TLV 类型:
tlv_stream
: n1
tlv1
)tu64
:amount_msat
]tlv2
)short_channel_id
:scid
]tlv3
)point
:node_id
]u64
:amount_msat_1
]u64
:amount_msat_2
]tlv4
)u16
:cltv_delta
]n2 命名空间支持以下 TLV 类型:
tlv_stream
: n2
tlv1
)tu64
:amount_msat
]tlv2
)tu32
:cltv_expiry
]任何命名空间中的以下TLV流都应触发解码失败:
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流都应正确解码,并被忽略:
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
将无效流附加到有效流的任何操作都应触发解码失败。
将编号较高的有效流附加到编号较低的有效流的任何操作都不应触发解码失败。
此外,命名空间 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记录,但排序无效
本节包含关于 init
消息的有效和无效扩展的示例。 这些示例的基本 init
消息(没有扩展)是 0x001000000000
(所有功能均已关闭)。
以下 init
消息有效:
0x001000000000
: 未提供扩展0x001000000000c9012acb0104
: 扩展包含两个未知的 奇数 TLV记录(类型为 0xc9
和 0xcb
)以下 init
消息无效:
0x00100000000001
: 扩展存在但被截断0x001000000000ca012a
: 扩展包含未知的 偶数 TLV记录(假设TLV类型 0xca
是未知的)0x001000000000c90101c90102
: 扩展TLV流无效(重复的TLV记录类型 0xc9
)请注意,当消息被签名时,扩展 是签名字节的一部分。 节点应存储 扩展 字节,即使他们不理解它们,以便能够正确验证签名。
[ TODO: (roasbeef); fin ]
[ FIXME: 插入作者列表 ]
<br>
本作品采用 知识共享署名 4.0 国际许可协议 进行许可。
- 原文链接: github.com/lightning/bol...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!