本文档是闪电网络基础协议规范(BOLT 1),定义了闪电网络节点之间通信的基本规则。内容涵盖连接处理、消息格式、类型-长度-值(TLV)编码、基本数据类型以及设置、控制消息,并详细阐述了节点如何通过ping/pong消息保持连接活跃,以及如何使用peer_storage消息进行对等存储。文档还包含了BigSize和TLV格式的测试向量,便于开发者验证实现。
本协议假定了一个底层的认证且排序的传输机制,该机制负责构建单独的消息。 BOLT #8 指定了 Lightning 中使用的规范传输层,尽管它可以被任何满足上述保证的传输层所替代。
默认的 TCP 端口取决于所使用的网络。最常见的网络有:
0x2607
;0x4D17
);0x9B37
)。LIGHTNING 的 Unicode 代码点 <sup>1</sup> 和端口约定尝试遵循 Bitcoin Core 的约定。
除非另有说明,所有数据字段均为无符号大端数据。
实现必须为每个对等方使用单个连接;通道消息(包括通道 ID)在此单个连接上进行多路复用。
解密后,所有 Lightning 消息的格式如下:
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 字节,并且协议中的消息无论如何都不会超过该长度。
保持奇数 规则允许将来可选的扩展,而无需客户端进行协商或特殊编码。扩展
字段类似地允许将来的扩展,允许发送方包含额外的 TLV 数据。 请注意,仅当消息 payload
尚未填满 65535 字节的最大长度时,才能添加 扩展
字段。
实现可能希望使消息数据与 8 字节边界对齐(此处任何类型的最大自然对齐要求); 但是,在类型字段之后添加 6 字节的填充被认为是浪费:可以通过将消息解密到具有 6 字节预填充的缓冲区中来实现对齐。
在整个协议中,使用 TLV(类型-长度-值)格式来允许向现有消息类型向后兼容地添加新字段。
tlv_record
表示单个字段,编码形式为:
bigsize
: type
]bigsize
: length
]length
: value
]tlv_stream
是一系列(可能为零)tlv_record
,表示为编码的 tlv_record
的串联。 当用于扩展现有消息时,tlv_stream
通常放置在所有当前定义的字段之后。
type
使用 BigSize 格式进行编码。 它充当消息特定的 64 位标识符,用于 tlv_record
,确定应如何解码 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 位字节s8
:一个 8 位有符号整数u16
:一个 2 字节无符号整数s16
:一个 2 字节有符号整数u32
:一个 4 字节无符号整数s32
:一个 4 字节有符号整数u64
:一个 8 字节无符号整数s64
:一个 8 字节有符号整数有符号整数使用标准的大端二进制补码表示形式(参见下面的测试向量 below)。
对于 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 字节的比特币椭圆曲线签名bip340sig
:根据 BIP-340 的 64 字节比特币椭圆曲线 Schnorr 签名point
:一个 33 字节的椭圆曲线点(根据 SEC 1 标准 的压缩编码)short_channel_id
:一个 8 字节的值,用于标识通道(参见 BOLT #7)sciddir_or_pubkey
:分别是 9 或 33 个字节,用于引用或标识节点
short_channel_id
,总共 9 个字节
channel_announcement
中 short_channel_id
的 node_id_1
channel_announcement
中 short_channel_id
的 node_id_2
(参见 BOLT #7point
bigsize
:一个可变长度的无符号整数,类似于比特币的 CompactSize 编码,但采用大端字节序。在BigSize 中描述。utf8
:作为 UTF-8 字符串一部分的一个字节。编写器必须确保这些字节数组是有效的 UTF-8 字符串,读取器可以拒绝任何包含无效 UTF-8 字符串的此类消息。init
消息身份验证完成后,第一条消息会显示此节点支持或需要的功能,即使这是重新连接。
BOLT #9 指定了功能列表。 每个功能通常用 2 位表示。 最低有效位编号为 0,这是 偶数,下一个最高有效位编号为 1,这是 奇数。 出于历史原因,功能分为全局和本地功能位掩码。
如果对等方在当前连接的 init
消息中设置了某个功能(作为偶数或奇数),则该功能将被提供。 如果两个对等方都提供了某个功能,或者本地节点将其作为偶数提供了,则该功能将被协商:它可以假定对等方支持该功能,因为它没有断开连接(这是必需的)。
features
字段必须用 0 填充到字节。
类型: 16 (init
)
数据:
u16
:gflen
]gflen*byte
:globalfeatures
]u16
:flen
]flen*byte
:features
]init_tlvs
:tlvs
]tlv_stream
: init_tlvs
类型:
networks
)...*chain_hash
:chains
]remote_addr
)...*byte
:data
]可选的 networks
指示节点感兴趣的链。
可选的 remote_addr
可用于绕过 NAT 问题。
发送节点:
init
作为任何连接的第一条 Lightning 消息发送。globalfeatures
中大于 13 的功能设置为 0。features
字段所需的最小长度。networks
设置为其将传播流言或打开通道的所有链。remote_addr
以反映传入连接的远程 IP 地址(和端口)。remote_addr
:
address descriptor
(1 字节类型和数据),如 BOLT 7 中所述。remote_addr
。接收节点:
init
,然后再发送任何其他消息。features
映射。networks
,但不包含常用链时:
remote_addr
来更新其 node_announcement
过去这里有两个功能位字段,但为了向后兼容,它们现在合并为一个。
这种语义允许将来不兼容的更改和将来向后兼容的更改。 位通常应成对分配,以便将来的可选功能可以变为强制性的。
节点等待接收对方的功能以简化功能不兼容时的错误诊断。
由于所有网络共享相同的端口,但大多数实现仅支持单个网络,因此 networks
字段可避免节点错误地认为它们将收到有关其首选网络的更新,或者它们可以打开通道。
error
和 warning
消息为了简化诊断,告诉对等方某些内容不正确通常很有用。
类型: 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),在这种情况下,它引用所有通道。
使用通道建立 v1 (open_channel
) 的资金节点:
funding_created
消息之前(包括该消息)发送的所有错误消息:
temporary_channel_id
代替 channel_id
。使用通道建立 v1 (accept_channel
) 的被资助节点:
funding_signed
消息之前(不包括该消息)发送的所有错误消息:
temporary_channel_id
代替 channel_id
。使用通道建立 v2 (open_channel2
) 的开启者节点:
accept_channel2
消息之前发送的所有错误消息:
temporary_channel_id
代替 channel_id
。使用通道建立 v2 (open_channel2
) 的接受者节点:
accept_channel2
消息之前(包括该消息)发送的所有错误消息:
temporary_channel_id
代替 channel_id
。发送节点:
error
。32
-255
类型的消息时,发送带有未知 channel_id
的 error
。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
。存在不可恢复的错误,需要中止对话; 如果仅仅丢弃连接,则对等方可能会重试连接。 描述用于诊断的协议违规也很有用,因为这表明一个对等方存在错误。
另一方面,过度使用错误消息已导致实现忽略它们(以避免原本昂贵的通道中断),因此添加了“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 中定义的 onion 路由协议结合使用时,小心地进行统计驱动的合成流量可以进一步增强网络中参与者的隐私。
建议采取有限的预防措施以防止 ping
泛洪,但是由于网络延迟会给予一定的灵活性。 请注意,还有其他传入流量泛洪的方法(例如,发送_奇数_未知消息类型,或最大程度地填充每个消息)。
最后,定期使用 ping
消息有助于促进 BOLT #8 中指定的频繁密钥轮换。
peer_storage
和 peer_storage_retrieval
消息声明了 option_provide_storage
功能的节点会为其对等节点提供存储任意数据的功能。 存储的数据不得超过 65531 字节,这使其可以放入闪电网络消息中。
节点可以通过比较检索到的数据的内容与上次发送的数据,来验证其 option_provide_storage
对等节点在每次重新连接时是否正确存储了其数据。 但是,节点不应期望对等节点始终具有其最新的可用数据。
节点使用 peer_storage
消息要求其对等节点存储数据,并期望对等节点使用 peer_storage_retrieval
消息将最新的数据返回给它们:
类型: 7 (peer_storage
)
数据:
u16
:length
]length*byte
:blob
]类型: 9 (peer_storage_retrieval
)
数据:
u16
:length
]length*byte
:blob
]要求:
peer_storage
的发送者:
peer_storage
。blob
限制为 65531 字节。blob
以确保持其长度始终完全为 65531 字节。peer_storage
的接收者:
option_provide_storage
:
blob
替换为最新收到的 blob
。init
消息后,必须在重新连接后再次发送 peer_storage_retrieval
。peer_storage_retrieval
的发送者:
blob
。blob
之前至少等待 2016 个区块。peer_storage_retrieval
的接收者:
peer_storage_retrieval
时:
peer_storage
和 peer_storage_retrieval
消息使节点能够安全地存储与网络中其他节点共享的数据,从而充当重要信息的备份机制。 通过利用它们,节点可以保护关键数据,从而提高网络的弹性和可靠性。 此外,即使我们没有开放的通道,某些节点也可以通过存储 peer_storage
来换取一些 sats 来提供此服务。
节点应填充 blob
以掩盖其实际大小,从而通过使基于大小的分析对接收者更加困难来增强隐私。
不应在 channel_reestablish
之后发送 peer_storage_retrieval
,因为这样用户将无法恢复节点并在丢失数据的情况下更新其状态。
节点应在希望更新与其对等节点存储的 blob
时发送 peer_storage
消息。 该 blob
可用于分发加密数据,这可能有助于恢复节点。
以下测试向量可用于断言 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)
}
}
```一个正确的实现应该通过这些测试向量:
```json
[
{
"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 流都应触发解码失败:
无效流: 0xfd
理由: type 截断
无效流: 0xfd01
理由: type 截断
无效流: 0xfd0001 00
理由: 非最小编码 type
无效流: 0xfd0101
理由: 缺少 length
无效流: 0x0f fd
理由: (length 截断)
无效流: 0x0f fd26
理由: (length 截断)
无效流: 0x0f fd2602
理由: 缺少 value
无效流: 0x0f fd0001 00
理由: 非最小编码 length
无效流: 0x0f fd0201 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
理由: value 截断
任何命名空间中的以下 TLV 流都应触发解码失败:
无效流: 0x12 00
理由: 未知的偶数 type。
无效流: 0xfd0102 00
理由: 未知的偶数 type。
无效流: 0xfe01000002 00
理由: 未知的偶数 type。
无效流: 0xff0100000000000002 00
理由: 未知的偶数 type。
命名空间 n1
中的以下 TLV 流应触发解码失败:
无效流: 0x01 09 ffffffffffffffffff
理由: 大于 n1
的 tlv1
的编码长度。
无效流: 0x01 01 00
理由: n1
的tlv1
的amount_msat
的编码不是最小的
无效流: 0x01 02 0001
理由: n1
的tlv1
的amount_msat
的编码不是最小的
无效流: 0x01 03 000100
理由: n1
的tlv1
的amount_msat
的编码不是最小的
无效流: 0x01 04 00010000
理由: n1
的tlv1
的amount_msat
的编码不是最小的
无效流: 0x01 05 0001000000
理由: n1
的tlv1
的amount_msat
的编码不是最小的
无效流: 0x01 06 000100000000
理由: n1
的tlv1
的amount_msat
的编码不是最小的
无效流: 0x01 07 00010000000000
理由: n1
的tlv1
的amount_msat
的编码不是最小的
无效流: 0x01 08 0001000000000000
理由: n1
的tlv1
的amount_msat
的编码不是最小的
无效流: 0x02 07 01010101010101
理由: 小于 n1
的 tlv2
的编码长度。
无效流: 0x02 09 010101010101010101
理由: 大于 n1
的 tlv2
的编码长度。
无效流: 0x03 21 023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb
理由: 小于 n1
的 tlv3
的编码长度。
无效流: 0x03 29 023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb0000000000000001
理由: 小于 n1
的 tlv3
的编码长度。
无效流: 0x03 30 023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb000000000000000100000000000001
理由: 小于 n1
的 tlv3
的编码长度。
无效流: 0x03 31 043da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb00000000000000010000000000000002
理由: n1
的node_id
不是有效的点。
无效流: 0x03 32 023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb0000000000000001000000000000000001
理由: 大于 n1
的 tlv3
的编码长度。
无效流: 0xfd00fe 00
理由: 小于 n1
的 tlv4
的编码长度。
无效流: 0xfd00fe 01 01
理由: 小于 n1
的 tlv4
的编码长度。
无效流: 0xfd00fe 03 010101
理由: 大于 n1
的 tlv4
的编码长度。
无效流: 0x00 00
理由: n1
命名空间未知的偶数字段。
任何命名空间中的以下TLV流都应正确解码,并被忽略:
有效流: 0x
解释: 空消息
有效流: 0x21 00
解释: 未知的奇数类型。
有效流: 0xfd0201 00
解释: 未知的奇数类型。
有效流: 0xfd00fd 00
解释: 未知的奇数类型。
有效流: 0xfd00ff 00
解释: 未知的奇数类型。
有效流: 0xfe02000001 00
解释: 未知的奇数类型。
有效流: 0xff0200000000000001 00
解释: 未知的奇数类型。
命名空间 n1
中的以下TLV流应正确解码,并具有此处给出的值:
有效流: 0x01 00
值: tlv1
amount_msat
=0
有效流: 0x01 01 01
值: tlv1
amount_msat
=1
有效流: 0x01 02 0100
值: tlv1
amount_msat
=256
有效流: 0x01 03 010000
值: tlv1
amount_msat
=65536
有效流: 0x01 04 01000000
值: tlv1
amount_msat
=16777216
有效流: 0x01 05 0100000000
值: tlv1
amount_msat
=4294967296
有效流: 0x01 06 010000000000
值: tlv1
amount_msat
=1099511627776
有效流: 0x01 07 01000000000000
值: tlv1
amount_msat
=281474976710656
有效流: 0x01 08 0100000000000000
值: tlv1
amount_msat
=72057594037927936
有效流: 0x02 08 0000000000000226
值: tlv2
scid
=0x0x550
有效流: 0x03 31 023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb00000000000000010000000000000002
值: tlv3
node_id
=023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb amount_msat_1
=1 amount_msat_2
=2
有效流: 0xfd00fe 02 0226
值: tlv4
cltv_delta
=550
任何将无效流附加到有效流的操作都应触发解码失败。
任何将编号较高的有效流附加到编号较低的有效流的操作都不应触发解码失败。
此外,命名空间 n1
中的以下 TLV 流应触发解码失败:
无效流: 0x02 08 0000000000000226 01 01 2a
理由: 有效的 TLV 记录,但排序无效
无效流: 0x02 08 0000000000000231 02 08 0000000000000451
理由: 重复的 TLV 类型
无效流: 0x1f 00 0f 01 2a
理由: 有效(已忽略)的 TLV 记录,但排序无效
无效流: 0x1f 00 1f 01 2a
理由: 重复的 TLV 类型(已忽略)
命名空间 n2
中的以下 TLV 流应触发解码失败:
本节包含init
消息上有效和无效扩展的示例。这些示例的基本init
消息(没有扩展)是0x001000000000
(所有功能均已关闭)。
以下init
消息是有效的:
0x001000000000
: 没有提供扩展0x001000000000c9012acb0104
: 扩展包含两个未知的奇数TLV 记录(类型为0xc9
和0xcb
)以下init
消息是无效的:
0x00100000000001
: 扩展存在但被截断0x001000000000ca012a
: 该扩展包含未知的偶数TLV记录(假设TLV类型0xca
是未知的)0x001000000000c90101c90102
: 扩展TLV流无效(重复的TLV记录类型0xc9
)请注意,当消息签名时,扩展是签名字节的一部分。节点应存储扩展字节,即使它们不理解它们,以便能够正确验证签名。
以下测试向量显示了如何使用大端 two's complement 对有符号整数(s8
、s16
、s32
和 s64
)进行编码。
[
{
"value": 0,
"bytes": "00"
},
{
"value": 42,
"bytes": "2a"
},
{
"value": -42,
"bytes": "d6"
},
{
"value": 127,
"bytes": "7f"
},
{
"value": -128,
"bytes": "80"
},
{
"value": 128,
"bytes": "0080"
},
{
"value": -129,
"bytes": "ff7f"
},
{
"value": 15000,
"bytes": "3a98"
},
{
"value": -15000,
"bytes": "c568"
},
{
"value": 32767,
"bytes": "7fff"
},
{
"value": -32768,
"bytes": "8000"
},
{
"value": 32768,
"bytes": "00008000"
},
{
"value": -32769,
"bytes": "ffff7fff"
},
{
"value": 21000000,
"bytes": "01406f40"
},
{
"value": -21000000,
"bytes": "febf90c0"
},
{
"value": 2147483647,
"bytes": "7fffffff"
},
{
"value": -2147483648,
"bytes": "80000000"
},
{
"value": 2147483648,
"bytes": "0000000080000000"
},
{
"value": -2147483649,
"bytes": "ffffffff7fffffff"
},
{
"value": 500000000000,
"bytes": "000000746a528800"
},
{
"value": -500000000000,
"bytes": "ffffff8b95ad7800"
},
{
"value": 9223372036854775807,
"bytes": "7fffffffffffffff"
},
{
"value": -9223372036854775808,
"bytes": "8000000000000000"
}
]
[ TODO: (roasbeef); fin ]
[ FIXME: 插入作者列表 ]
<br>
本作品已获得 Creative Commons Attribution 4.0 International License 许可。
- 原文链接: github.com/lightning/bol...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!