本文档描述了简单的节点发现、通道发现和通道更新机制,这些机制不依赖于第三方来传播信息。为了支持通道和节点发现,支持三种 gossip 消息:node_announcement、channel_announcement 和 channel_update。文章还详细介绍了这些消息的格式、要求以及路由推荐。
本规范描述了简单的节点发现、通道发现和通道更新机制,这些机制不依赖于第三方来传播信息。
节点和通道发现服务于两个不同的目的:
为了支持通道和节点发现,支持三个 gossip messages:
对于节点发现,节点交换 node_announcement
消息,该消息提供有关节点的其他信息。可能存在
多个 node_announcement
消息,以便更新节点信息。
channel_announcement
消息,其中包含有关两个节点之间新
通道的信息。他们还可以交换 channel_update
消息,以更新有关通道的信息。对于任何通道,只能有一个有效的
channel_announcement
,但至少需要两个
channel_update
消息。short_channel_id
的定义announcement_signatures
消息channel_announcement
消息node_announcement
消息channel_update
消息short_channel_id
的定义short_channel_id
是 funding transaction 的唯一描述。
它的构造方式如下:
short_channel_id
的标准人类可读格式是通过输出上述组件来创建的,顺序为:区块高度、交易索引和输出索引。每个组件都以十进制数输出,并用小写字母 x
彼此分隔。例如,short_channel_id
可以写成 539268x845x1
,表示在高度为 539268 的区块中索引为 845 的交易的输出 1 上的通道。
short_channel_id
人类可读格式的设计目的是
以便双击或双击它将在大多数系统上选择整个 ID。
人类在阅读数字时更喜欢十进制,因此 ID 组件以十进制书写。
使用小写字母 x
,因为在大多数字体中,
x
明显小于十进制数字,
从而可以轻松地在视觉上对 ID 的每个组件进行分组。
announcement_signatures
消息这是通道的两个端点之间的直接消息,用作允许向网络其余部分公布通道的选择加入机制。它包含发送方构造 channel_announcement
消息所需的签名。
announcement_signatures
)channel_id
:channel_id
]short_channel_id
:short_channel_id
]signature
:node_signature
]signature
:bitcoin_signature
]启动节点宣布通道的意愿在通道打开期间通过在 channel_flags
中设置 announce_channel
位来发出信号(参见 BOLT #2)。
announcement_signatures
消息是通过构造 channel_announcement
消息来创建的,该消息对应于新建立的通道,并使用与端点的 node_id
和 bitcoin_key
匹配的密钥对其进行签名。签名后,可以发送
announcement_signatures
消息。
一个节点:
open_channel
消息设置了 announce_channel
位 并且尚未发送 shutdown
消息:
announcement_signatures
消息。
funding_locked
并且 funding transaction 至少有六个确认之前,不得发送 announcement_signatures
消息。announcement_signatures
消息。announcement_signatures
消息来响应第一个 announcement_signatures
消息。announcement_signatures
消息:
announcement_signatures
消息。接收节点:
short_channel_id
不正确:
warning
并关闭连接,或者发送一个
error
并使通道失败。node_signature
或 bitcoin_signature
不正确:
warning
并关闭连接,或者发送一个
error
并使通道失败。announcement_signatures
消息:
channel_announcement
消息。允许推迟过早的 announcement_signatures 的原因是 该规范的早期版本不需要等待收到 funding locked:推迟而不是忽略它允许与 此行为兼容。
channel_announcement
消息此 gossip message 包含有关通道的所有权信息。它将
每个链上比特币密钥与关联的闪电网络节点密钥联系起来,反之亦然。
在至少一方使用 channel_update
宣布其费用水平和到期时间之前,该通道实际上不可用。
证明 node_1
和 node_2
之间存在通道需要:
bitcoin_key_1
和
bitcoin_key_2
node_1
拥有 bitcoin_key_1
node_2
拥有 bitcoin_key_2
假设所有节点都知道未花费的交易输出,则第一个证明是
通过节点查找由 short_channel_id
给出的输出并
验证它是否确实是 BOLT #3 中指定的那些密钥的 P2WSH funding transaction 输出来实现的。
最后两个证明是通过显式签名来实现的:
为每个 bitcoin_key
生成 bitcoin_signature_1
和 bitcoin_signature_2
,并对每个对应的 node_id
进行签名。
还需要证明 node_1
和 node_2
都同意
announcement message:这是通过拥有来自每个 node_id
的签名(node_signature_1
和 node_signature_2
)来签署消息来实现的。
channel_announcement
)signature
:node_signature_1
]signature
:node_signature_2
]signature
:bitcoin_signature_1
]signature
:bitcoin_signature_2
]u16
:len
]len*byte
:features
]chain_hash
:chain_hash
]short_channel_id
:short_channel_id
]point
:node_id_1
]point
:node_id_2
]point
:bitcoin_key_1
]point
:bitcoin_key_2
]始发节点:
chain_hash
设置为唯一标识该通道在其内打开的链的 32 字节哈希值:
chain_hash
值(以十六进制编码)设置为等于 6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000
。short_channel_id
以引用已确认的 funding transaction,
如 BOLT #2 中所述。
node_id_1
和 node_id_2
设置为操作该通道的两个节点的公钥,
使得 node_id_1
是按升序字典顺序排序的两个压缩密钥中按字典顺序较小的。bitcoin_key_1
和 bitcoin_key_2
设置为 node_id_1
和 node_id_2
各自的 funding_pubkey
。h
,从偏移量
256 开始,直到消息结束。
node_signature_1
和 node_signature_2
设置为哈希 h
的有效
签名(使用 node_id_1
和 node_id_2
各自的
密钥)。bitcoin_signature_1
和 bitcoin_signature_2
设置为哈希 h
的有效
签名(使用 bitcoin_key_1
和 bitcoin_key_2
各自的
密钥)。features
,如 BOLT #9 中所述len
设置为保存其设置的 features
位所需的最小长度。接收节点:
features
字段中存在未知的偶数位:
short_channel_id
的输出不对应于 P2WSH(使用
bitcoin_key_1
和 bitcoin_key_2
,如
BOLT #3 中所述)或者输出已
花费:
chain_hash
:
bitcoin_signature_1
、bitcoin_signature_2
、node_signature_1
或
node_signature_2
无效或不正确:
warning
。node_id_1
或 node_id_2
已被列入黑名单:channel_announcement
,对于
相同的交易,在同一个区块中,但对于不同的 node_id_1
或
node_id_2
:node_id_1
和 node_id_2
列入黑名单,
以及此 node_id_1
和 node_id_2
,并忘记与它们连接的任何通道。channel_announcement
。两个节点都需要签名以表明它们愿意通过此通道路由其他 付款(即成为公共网络的一部分);要求它们的 比特币签名证明它们控制该通道。
将冲突节点列入黑名单不允许进行多个不同的 公告。任何节点都不应广播此类冲突公告,因为这表明密钥已泄露。
虽然不应在通道足够深之前对其进行广告,但 对重新广播的要求仅适用于交易尚未转移到 不同区块的情况。
为了避免存储过大的消息,但仍然允许合理的未来扩展,允许节点限制重新广播(可能在统计上)。
未来可能会出现新的通道功能:向后兼容(或 可选)功能将具有 奇数 功能位,而不兼容的功能将具有 偶数 功能位 ("It's OK to be odd!")。
node_announcement
消息此 gossip message 允许节点指示与其关联的额外数据, 除了其公钥之外。为了避免微不足道的拒绝服务攻击, 不与已知通道关联的节点将被忽略。
node_announcement
)signature
:signature
]u16
:flen
]flen*byte
:features
]u32
:timestamp
]point
:node_id
]3*byte
:rgb_color
]32*byte
:alias
]u16
:addrlen
]addrlen*byte
:addresses
]timestamp
允许在多个
公告的情况下对消息进行排序。rgb_color
和 alias
允许情报部门为
节点分配颜色,例如黑色,以及诸如“IRATEMONK”和“WISTFULTOLL”之类的酷炫绰号。
addresses
允许节点宣布其接受传入网络
连接的意愿:它包含一系列用于连接到
节点的 address descriptor
。第一个字节描述了地址类型,后跟
该类型的适当数量的字节。
定义了以下 address descriptor
类型:
1
: ipv4; data = [4:ipv4_addr][2:port]
(length 6)2
: ipv6; data = [16:ipv6_addr][2:port]
(length 18)3
: 已弃用(长度 12)。用于包含 Tor v2 onion services。4
: Tor v3 onion service; data = [35:onion_addr][2:port]
(length 37)
[32:32_byte_ed25519_pubkey] || [2:checksum] || [1:version]
, where
checksum = sha3(".onion checksum" | pubkey || version)[:2]
.始发节点:
timestamp
设置为大于先前创建的任何
node_announcement
的时间戳。
signature
设置为 signature
之后整个
剩余数据包的 double-SHA256 的签名(使用 node_id
给出的密钥)。alias
和 rgb_color
以自定义其在地图和
图表中的外观。
rgb_color
的第一个字节是红色值,第二个字节是
绿色值,最后一个字节是蓝色值。alias
设置为有效的 UTF-8 字符串,任何 alias
尾随字节
都等于 0。addresses
填充每个期望传入连接的公共网络
地址的地址描述符。addrlen
设置为 addresses
中的字节数。addresses
之后的字段。port
等于 0 的 type 1
或 type 2
地址描述符。ipv4_addr
和 ipv6_addr
是可路由的地址。features
flen
设置为保存其设置的 features
位所需的最小长度。接收节点:
node_id
不是有效的压缩公钥:
warning
。signature
不是有效签名(使用 node_id
对双重SHA256 进行签名,整个消息在 signature
字段之后,包括附加到末尾的任何未来字段):
warning
。features
字段包含 未知偶数位:
address descriptor
。addrlen
不足以容纳已知类型的地址描述符:
warning
。port
等于 0:
ipv6_addr
或 ipv4_addr
。node_id
以前未知来自 channel_announcement
消息,
或者如果 timestamp
不大于从此 node_id
收到的最后一个 node_announcement
:
timestamp
大于从此 node_id
收到的最后一个 node_announcement
:
rgb_color
和 alias
来引用接口中的节点。
未来可能会出现新的节点功能:向后兼容(或
可选)的功能将具有 奇数 feature
位,不兼容的功能将具有
偶数 feature
位。这些将正常传播;此处不兼容的
功能位是指节点,而不是 node_announcement
消息本身。
将来可能会添加新的地址类型;由于地址描述符
必须按升序排列,因此可以安全地忽略未知的描述符。addresses
之外的其他字段也可能会在将来添加——如果它们需要特定的对齐方式,则在 addresses
中使用可选填充。
节点别名是用户定义的,并提供了潜在的注入攻击途径,无论是在呈现过程中还是在持久化过程中。
节点别名应始终在 HTML/Javascript 上下文或任何其他动态解释的呈现框架中显示之前进行清理。同样,请考虑使用预准备语句、输入验证和转义来防止注入漏洞和支持 SQL 或其他动态解释的查询语言的持久化引擎。
不要像 Little Bobby Tables 学校那样。
channel_update
消息最初公告通道后,每一方都会独立
宣布其需要的费用和最小到期增量,以通过
此通道中继 HTLC。每一方都使用与
channel_announcement
匹配的 8 字节通道 shortid 和 1 位 channel_flags
字段来指示它位于
通道的哪一端(起始端还是最终端)。节点可以多次执行此操作,
以便更改费用。
请注意,channel_update
gossip message 仅在
_中继_付款的上下文中才有用,而_发送_付款则不然。在进行付款时
A
-> B
-> C
-> D
,只有与 B
发起的 B
-> C
(由 B
宣布)通道和 C
发起的 C
-> D
(由 C
宣布)通道相关的 channel_update
才会生效。构建路由时,需要从目的地到源头向后计算 HTLC 的金额和到期时间。
要在路由中的最后一个 HTLC 中使用的 amount_msat
的确切初始值和 cltv_expiry
的最小值在付款请求中提供
(参见 BOLT #11)。
channel_update
)signature
:signature
]chain_hash
:chain_hash
]short_channel_id
:short_channel_id
]u32
:timestamp
]byte
:message_flags
]byte
:channel_flags
]u16
:cltv_expiry_delta
]u64
:htlc_minimum_msat
]u32
:fee_base_msat
]u32
:fee_proportional_millionths
]u64
:htlc_maximum_msat
] (option_channel_htlc_max)channel_flags
位字段用于指示通道的方向:它
标识此更新的来源节点,并发出有关
通道的各种选项的信号。下表指定了其
各个位的含义:
位位置 | 名称 | 含义 |
---|---|---|
0 | direction |
此更新引用的方向。 |
1 | disable |
禁用通道。 |
message_flags
位字段用于指示 channel_update
消息中是否存在可选
字段:
位位置 | 名称 | 字段 |
---|---|---|
0 | option_channel_htlc_max |
htlc_maximum_msat |
请注意,htlc_maximum_msat
字段在当前
协议的生命周期内是静态的:它 _不_设计用于
指示每个方向上的实时通道容量,这将
既是大量数据泄漏,又是无用地垃圾邮件网络(
gossip 平均需要 30 秒才能传播每个跃点)。
用于签名验证的 node_id
取自相应的
channel_announcement
:如果标志的最低有效位为 0,则为 node_id_1
,否则为
node_id_2
。
始发节点:
funding_locked
之前,不得发送创建的 channel_update
。channel_update
以将通道参数通信给
通道对等方,即使尚未公告该通道(即未设置 announce_channel
位)。
channel_update
转发给其他对等方。channel_update
(未在 channel_announcement
之前)对任何其他对等方都无效,并且会被丢弃。node_id
将 signature
设置为
signature
之后剩余的整个数据包的 double-SHA256 的签名。chain_hash
和 short_channel_id
以匹配唯一标识 channel_announcement
消息中指定的通道的
32 字节哈希和 8 字节通道 ID。node_id_1
:
channel_flags
的 direction
位设置为 0。channel_flags
的 direction
位设置为 1。htlc_maximum_msat
字段:
message_flags
的 option_channel_htlc_max
位设置为 1。htlc_maximum_msat
设置为其将通过此通道发送的单个 HTLC 的最大值。
max_htlc_value_in_flight_msat
。message_flags
的 option_channel_htlc_max
位设置为 0。channel_flags
和 message_flags
中未分配含义的位设置为 0。disable
位设置为 1 的 channel_update
,以
指示通道的临时不可用(例如,由于连接丢失)或永久不可用(例如,在链上
结算之前)。
disable
位设置为 0 的后续 channel_update
以重新启用该通道。timestamp
设置为大于 0,并且大于此 short_channel_id
先前发送的任何 channel_update
。
timestamp
基于 UNIX 时间戳。cltv_expiry_delta
设置为将从传入 HTLC 的 cltv_expiry
中减去的区块数。htlc_minimum_msat
设置为通道对等方将接受的最小 HTLC 值(以毫聪为单位)。fee_base_msat
设置为它将对任何 HTLC 收取的基本费用(以毫聪为单位)。fee_proportional_millionths
设置为它将按每转移的聪收取的金额(以百万分之一聪为单位)。channel_update
。接收节点:
short_channel_id
与先前的 channel_announcement
不匹配,
或者如果通道已在此期间关闭:
channel_update
,必须忽略 channel_update
。channel_update
(即使是非公开的),
以便了解关联的起始节点的转发参数。signature
不是有效签名,则使用双 SHA256 的 node_id
对签名字段后的整个消息进行签名(
包括 fee_proportional_millionths
之后的未知字段):
warning
并关闭连接。chain_hash
值未知(意味着它在指定的链上不活跃):
timestamp
等于此 short_channel_id
和 node_id
的最后一个收到的 channel_update
:
timestamp
下面的字段不同:
node_id
列入黑名单。timestamp
下面的字段相等:
timestamp
低于此 short_channel_id
和 node_id
的最后一个收到的 channel_update
的时间戳:
timestamp
在未来不合理地遥远:
channel_update
。message_flags
的 option_channel_htlc_max
位为 0:
htlc_maximum_msat
不存在。htlc_maximum_msat
不存在或大于通道容量:
node_id
列入黑名单htlc_maximum_msat
。节点使用 timestamp
字段来修剪 channel_update
,这些 channel_update
要么在未来太远,要么两周内没有更新;因此,
将其设置为 UNIX 时间戳(即自 UTC 1970-01-01 以来的秒数)是有意义的。但是,考虑到在
一秒钟内可能出现两个 channel_update
,这不能成为硬性要求。
假设在同一秒内多次更改通道参数的 channel_update
消息可能是 DoS 尝试,因此,可以禁止负责对这些消息签名的节点。但是,节点可以发送具有不同签名的相同 channel_update
消息(更改签名中的 nonce),因此,除了签名之外,还会检查字段以查看通道参数是否已更改为同一时间戳。同样重要的是要注意,ECDSA 签名是可延展的。因此,收到 channel_update
消息的中间节点可以通过仅将签名的 s
分量更改为 -s
来重新广播它。但是,这不应导致始发消息的 node_id
被列入黑名单。
显式 option_channel_htlc_max
标志指示
htlc_maximum_msat
的存在(而不是通过消息长度暗示 htlc_maximum_msat
)允许我们在未来使用不同的字段扩展 channel_update
。由于 Bitcoin 中的通道限制为 2^32-1
毫聪,因此 htlc_maximum_msat
具有相同的限制。
不建议使用冗余的 channel_update
,这会最大限度地减少垃圾邮件网络,
但这有时是不可避免的。例如,与
无法访问的对等方的通道最终将导致 channel_update
指示该通道已禁用,而当
对等方重新建立联系时,另一个更新将重新启用该通道。由于 gossip message
被批量处理并替换以前的 gossip message,因此结果可能是一个看似冗余的更新。
通过 init
协商 gossip_queries
选项启用了许多
用于 gossip 同步的扩展查询。这些显式
请求应该接收哪些 gossip。
有几个消息包含一个长的 short_channel_id
数组(称为 encoded_short_ids
),因此我们包含一个编码字节,该字节允许将来定义不同的编码方案,如果它们
提供好处。
编码类型:
0
:未压缩的 short_channel_id
类型数组,按升序排列。1
:以前用于 zlib 压缩,_不得_使用此编码。此编码也用于其他类型的数组(时间戳、标志...),
并使用 encoded_
前缀指定。例如,encoded_timestamps
是带有 0
前缀的时间戳数组。
查询消息可以使用可选字段进行扩展,这些字段可以通过启用以下功能来帮助减少同步路由表所需的消息数量:
channel_update
消息过滤:仅请求比你已有的 channel_update
消息更新的消息。channel_update
消息过滤:仅请求与你已有的 channel_update
消息携带不同信息的消息。节点可以使用 gossip_queries_ex
功能位来指示它们支持扩展 gossip 查询。
query_short_channel_ids
/reply_short_channel_ids_end
消息type: 261 (query_short_channel_ids
) (gossip_queries
)
data:
chain_hash
:chain_hash
]u16
:len
]len*byte
:encoded_short_ids
]query_short_channel_ids_tlvs
:tlvs
]tlv_stream
: query_short_channel_ids_tlvs
types:
query_flags
)byte
:encoding_type
]...*byte
:encoded_query_flags
]encoded_query_flags 是一个位字段数组,每个 short_channel_id 一个 bigsize 位字段。位具有以下含义: |
Bit Position | Meaning |
---|---|---|
0 | Sender wants channel_announcement |
|
1 | Sender wants channel_update for node 1 |
|
2 | Sender wants channel_update for node 2 |
|
3 | Sender wants node_announcement for node 1 |
|
4 | Sender wants node_announcement for node 2 |
查询标志必须进行最小编码,这意味着一个标志将用单个字节进行编码。
reply_short_channel_ids_end
) (gossip_queries
)chain_hash
:chain_hash
]byte
:full_information
]这是一种通用机制,允许节点查询特定通道的 channel_announcement
和 channel_update
消息(通过 short_channel_id
标识)。这通常用于节点看到它没有 channel_announcement
的 channel_update
,或者因为它从 reply_channel_range
获得了以前未知的 short_channel_id
。
发送方:
query_short_channel_ids
并且未收到 reply_short_channel_ids_end
,则必须不发送 query_short_channel_ids
。chain_hash
设置为唯一标识 short_channel_id
引用的链的 32 字节哈希值。encoded_short_ids
的第一个字节设置为编码类型。short_channel_id
编码为 encoded_short_ids
channel_announcement
的 short_channel_id
的 channel_update
,则可以发送此消息。query_flags
。如果是这样:
encoding_type
,如 encoded_short_ids
一样。short_channel_id
编码一个查询标志。接收方:
encoded_short_ids
的第一个字节不是已知的编码类型:
warning
。encoded_short_ids
没有解码为整数个 short_channel_id
:
warning
。query_short_channel_ids
发送 reply_short_channel_ids_end
:
warning
。query_short_channel_ids_tlvs
:
encoding_type
不是已知的编码类型:
warning
。encoded_query_flags
没有解码为每个 short_channel_id
恰好一个标志:
warning
。short_channel_id
:
encoded_query_flags
:
channel_announcement
和每个端的最新 channel_update
channel_announcement
的任何 node_announcement
encoded_short_ids
中第 N 个 short_channel_id
的 query_flag
定义为解码后的 encoded_query_flags
的第 N 个 bigsize。query_flag
的第 0 位:channel_announcement
query_flag
的第 1 位,并且它已收到来自 node_id_1
的 channel_update
:node_id_1
的最新 channel_update
query_flag
的第 2 位,并且它已收到来自 node_id_2
的 channel_update
:node_id_2
的最新 channel_update
query_flag
的第 3 位,并且它已收到来自 node_id_1
的 node_announcement
:node_id_1
的最新 node_announcement
query_flag
的第 4 位,并且它已收到来自 node_id_2
的 node_announcement
:node_id_2
的最新 node_announcement
query_short_channel_ids
时发送重复的 node_announcements
。reply_short_channel_ids_end
。chain_hash
的最新通道信息:
full_information
设置为 0。full_information
设置为 1。未来的节点可能没有完整的信息;它们当然不会拥有关于未知 chain_hash
链的完整信息。虽然这个 full_information
字段(以前被错误地称为 complete
)不可信,但 0 表示发送者应该在其他地方搜索其他数据。
显式的 reply_short_channel_ids_end
消息意味着接收者可以表明它一无所知,并且发送者不需要依赖超时。它还会导致查询的自然速率限制。
query_channel_range
和 reply_channel_range
消息type: 263 (query_channel_range
) (gossip_queries
)
data:
chain_hash
:chain_hash
]u32
:first_blocknum
]u32
:number_of_blocks
]query_channel_range_tlvs
:tlvs
]tlv_stream
: query_channel_range_tlvs
types:
query_option
)bigsize
:query_option_flags
]query_option_flags
是一个位域,表示为最小编码的 bigsize。各位具有以下含义:
Bit Position | Meaning |
---|---|
0 | Sender wants timestamps |
1 | Sender wants checksums |
虽然这是可能的,但如果不要求提供时间戳也要求提供校验和,那就不会很有用:接收节点可能具有具有不同校验和的较旧的 channel_update
,要求提供它是没有用的。并且如果 channel_update
校验和实际上为 0(这不太可能),则不会查询它。
type: 264 (reply_channel_range
) (gossip_queries
)
data:
chain_hash
:chain_hash
]u32
:first_blocknum
]u32
:number_of_blocks
]byte
:sync_complete
]u16
:len
]len*byte
:encoded_short_ids
]reply_channel_range_tlvs
:tlvs
]tlv_stream
: reply_channel_range_tlvs
types:
timestamps_tlv
)byte
:encoding_type
]...*byte
:encoded_timestamps
]checksums_tlv
)...*channel_update_checksums
:checksums
]对于单个 channel_update
,时间戳编码为:
channel_update_timestamps
u32
:timestamp_node_id_1
]u32
:timestamp_node_id_2
]其中:
timestamp_node_id_1
是 node_id_1
的 channel_update
的时间戳,如果该节点没有 channel_update
,则为 0。timestamp_node_id_2
是 node_id_2
的 channel_update
的时间戳,如果该节点没有 channel_update
,则为 0。对于单个 channel_update
,校验和编码为:
channel_update_checksums
u32
:checksum_node_id_1
]u32
:checksum_node_id_2
]其中:
checksum_node_id_1
是 node_id_1
的 channel_update
的校验和,如果该节点没有 channel_update
,则为 0。checksum_node_id_2
是 node_id_2
的 channel_update
的校验和,如果该节点没有 channel_update
,则为 0。channel_update
的校验和是 RFC3720 中指定的 CRC32C 校验和,不包括 signature
和 timestamp
字段。
这允许查询特定块中的通道。
query_channel_range
的发送者:
query_channel_range
并且未收到所有 reply_channel_range
回复,则必须不发送此消息。chain_hash
设置为唯一标识它希望 reply_channel_range
引用的链的 32 字节哈希值first_blocknum
设置为它想要了解通道的第一个块number_of_blocks
设置为 1 或更大。query_channel_range_tlv
,该 TLV 指定它想要接收的扩展信息的类型。query_channel_range
的接收者:
reply_channel_range
发送到来自此发送方的先前收到的 query_channel_range
:
warning
。reply_channel_range
:
chain_hash
等于 query_channel_range
的 chain_hash
,number_of_blocks
限制为结果可以容纳在 encoded_short_ids
中的最大块数reply_channel_range
拆分块内容。reply_channel_range
消息:
first_blocknum
设置为小于或等于 query_channel_range
中的 first_blocknum
first_blocknum
加上 number_of_blocks
设置为大于 query_channel_range
中的 first_blocknum
。reply_channel_range
消息:
first_blocknum
等于或大于先前的 first_blocknum
。reply_channel_range
,则 必须将 sync_complete
设置为 false
。reply_channel_range
消息:
first_blocknum
加上 number_of_blocks
等于或大于 query_channel_range
的 first_blocknum
加上 number_of_blocks
。sync_complete
设置为 true
。如果传入消息包含 query_option
,则接收者_可以_将附加信息附加到其回复中:
query_option_flags
中的第 0 位,则接收者_可以_附加一个 timestamps_tlv
,其中包含 encoded_short_ids
中所有 short_chanel_id
的 channel_update
时间戳query_option_flags
中的第 1 位,则接收者_可以_附加一个 checksums_tlv
,其中包含 encoded_short_ids
中所有 short_chanel_id
的 channel_update
校验和单个响应可能对于单个数据包来说太大,因此可能需要多个回复。我们希望允许对等方存储(例如)1000 个块范围的预置结果,因此回复可以超出请求的范围。但是,我们要求每个回复都相关(与请求的范围重叠)。
通过坚持回复按递增顺序排列,接收者可以轻松确定回复是否完成:只需检查 first_blocknum
加上 number_of_blocks
是否等于或超过它所要求的 first_blocknum
加上 number_of_blocks
。
时间戳和校验和字段的添加允许对等方省略查询冗余更新。
gossip_timestamp_filter
消息gossip_timestamp_filter
) (gossip_queries
)chain_hash
:chain_hash
]u32
:first_timestamp
]u32
:timestamp_range
]此消息允许节点将未来的 gossip 消息约束到特定范围。想要任何 gossip 消息的节点都必须发送此消息,否则 gossip_queries
协商意味着不会收到任何 gossip 消息。
请注意,此筛选器会替换任何先前的筛选器,因此可以多次使用它来更改来自对等方的 gossip。
发送者:
chain_hash
设置为唯一标识它希望 gossip 引用的链的 32 字节哈希值。接收者:
timestamp
大于或等于 first_timestamp
且小于 first_timestamp
加上 timestamp_range
的 gossip 消息。
timestamp
如何。timestamp
大于或等于 first_timestamp
且小于 first_timestamp
加上 timestamp_range
的消息。channel_announcement
没有相应的 channel_update
:
channel_announcement
。channel_announcement
的 timestamp
视为相应 channel_update
的 timestamp
。channel_update
后考虑是否发送 channel_announcement
。channel_announcement
:
channel_update
和 node_announcement
之前发送 channel_announcement
。由于 channel_announcement
没有时间戳,我们生成一个可能的时间戳。如果没有 channel_update
,则根本不会发送它,这在修剪通道的情况下最有可能发生。
否则,channel_announcement
通常会紧随其后的是 channel_update
。理想情况下,我们会指定第一个(最旧的)channel_update
的时间戳用作 channel_announcement
的时间,但网络上的新节点不会有这个时间戳,并且还需要存储第一个 channel_update
时间戳。相反,我们允许使用任何更新,这很简单。
如果仍然错过了 channel_announcement
,则可以使用 query_short_channel_ids
来检索它。
当节点有许多对等方时,可以使用 timestamp_filter
来减少其 gossip 负载(例如,在最初的几个对等方之后将 first_timestamp
设置为 0xFFFFFFFF
,假设传播是足够的)。这种对足够传播的假设不适用于节点本身直接生成的 gossip 消息,因此它们应该忽略筛选器。
如果节点需要 gossip 消息的初始同步,它将在 init
消息中通过一个特性标志进行标记(BOLT #9)。
请注意,如果通过 init
协商了 gossip_queries
特性,则 initial_routing_sync
特性将被覆盖(并且应被视为等于 0)。
请注意,gossip_queries
不适用于较旧的节点,因此 initial_routing_sync
的值对于控制与它们的交互仍然很重要。
一个节点:
gossip_queries
特性:
initial_routing_sync
标志设置为 1。initial_routing_sync
标志设置为 1 的 init
消息后:
initial_routing_sync
标志设置为 0,或者如果初始同步已完成:
接收节点:
channel_announcement
或 channel_update
或具有更新 timestamp
的 node_announcement
后:
一个节点:
gossip_queries
功能:
gossip_timestamp_filter
之前,必须不发送自己未生成的 gossip。init
中发送 networks
并且未指定此 gossip 消息的 chain_hash
的对等方。channel_announcement
消息,然后发送最新的 node_announcement
和 channel_update
消息。一旦 gossip 消息被处理,它就会被添加到传出消息列表中,这些消息将发送到处理节点的对等方,替换来自原始节点的任何较旧的更新。此 gossip 消息列表将定期刷新;这种存储和延迟转发广播称为 交错广播。此外,这种批处理形成了一种自然速率限制,且开销较低。
在重新连接时发送所有 gossip 消息很简单,但允许新节点进行引导,以及允许已离线一段时间的节点进行更新。gossip_queries
选项允许更精细的同步。
原始节点:
channel_update
之后,应在一段合理的时间内接受支付旧费用的 HTLC。
一个节点:
node_announcement
消息添加的节点。
node_announcement
依赖于它之前有一个 channel_announcement
的直接结果。一个节点:
channel_update
的 timestamp
早于两周(1209600 秒):
几种情况可能导致通道变得不可用,并且其端点无法为这些通道发送更新。例如,如果两个端点都无法访问其私钥,并且既不能签名 channel_update
也不能在链上关闭通道,则会发生这种情况。在这种情况下,通道不太可能是计算出的路由的一部分,因为它们将与网络的其余部分分离开来;但是,它们将保留在本地网络视图中,并将无限期地转发给其他对等方。
最旧的 channel_update
用于修剪通道,因为双方都需要处于活动状态才能使通道可用。这样做可以修剪通道,即使一方继续发送最新的 channel_update
,但另一方已消失。
在计算 HTLC 的路由时,需要考虑 cltv_expiry_delta
和费用:cltv_expiry_delta
会导致在最坏的情况下资金不可用的时间。这两个属性之间的关系尚不清楚,因为它取决于所涉及节点的可靠性。
如果路由是通过简单地路由到预期的接收者并对 cltv_expiry_delta
求和来计算的,那么中间节点可能会猜出它们在路由中的位置。了解 HTLC 的 CLTV、周围的网络拓扑和 cltv_expiry_delta
使攻击者可以猜测预期的接收者。因此,非常需要向预期接收者将收到的 CLTV 添加一个随机偏移量,这将提升沿路由的所有 CLTV。
为了创建一个合理的偏移量,原始节点 可以 在图上开始一个有限的随机游走,从预期的接收者开始,并对 cltv_expiry_delta
求和,并将结果总和用作偏移量。这实际上创建了一个 影子路由扩展 到实际路由,并提供比简单地选择一个随机偏移量更好的针对此攻击向量的保护。
其他更高级的考虑因素包括路由选择的多样化,以避免单一故障点和检测,以及本地通道的平衡。
考虑四个节点:
B
/ \
/ \
A C
\ /
\ /
D
每个节点在其每个通道的末端公布以下 cltv_expiry_delta
:
C 在请求付款时也使用 9 的 min_final_cltv_expiry
(默认值)。
此外,每个节点都有一套固定的费用方案,用于其每个通道:
网络将看到八个 channel_update
消息:
cltv_expiry_delta
= 10, fee_base_msat
= 100, fee_proportional_millionths
= 1000cltv_expiry_delta
= 10, fee_base_msat
= 100, fee_proportional_millionths
= 1000cltv_expiry_delta
= 20, fee_base_msat
= 200, fee_proportional_millionths
= 2000cltv_expiry_delta
= 40, fee_base_msat
= 400, fee_proportional_millionths
= 4000cltv_expiry_delta
= 20, fee_base_msat
= 200, fee_proportional_millionths
= 2000cltv_expiry_delta
= 40, fee_base_msat
= 400, fee_proportional_millionths
= 4000cltv_expiry_delta
= 30, fee_base_msat
= 300, fee_proportional_millionths
= 3000cltv_expiry_delta
= 30, fee_base_msat
= 300, fee_proportional_millionths
= 3000B->C. 如果 B 要将 4,999,999 毫聪直接发送给 C,它既不会向自己收取费用,也不会添加自己的 cltv_expiry_delta
,因此它将使用 C 请求的 min_final_cltv_expiry
9。据推测,它还会添加一个 影子路由 以提供额外的 CLTV 42。此外,它可以添加其他跳中的额外 CLTV delta,因为这些值表示最小值,但为了简单起见,这里选择不这样做:
amount_msat
: 4999999cltv_expiry
: current-block-height + 9 + 42onion_routing_packet
:
amt_to_forward
= 4999999outgoing_cltv_value
= current-block-height + 9 + 42A->B->C. 如果 A 要通过 B 将 4,999,999 毫聪发送给 C,它需要支付 B 在 B->C channel_update
中指定的费用,按照 HTLC 费用 计算:
fee_base_msat + ( amount_to_forward * fee_proportional_millionths / 1000000 )
200 + ( 4999999 * 2000 / 1000000 ) = 10199
类似地,它需要添加 B->C 的 channel_update
cltv_expiry_delta
(20)、C 请求的 min_final_cltv_expiry
(9) 和 影子路由 的成本 (42)。因此,A->B 的 update_add_htlc
消息将是:
amount_msat
: 5010198cltv_expiry
: current-block-height + 20 + 9 + 42onion_routing_packet
:
amt_to_forward
= 4999999outgoing_cltv_value
= current-block-height + 9 + 42B->C 的 update_add_htlc
将与上面 B->C 的直接付款相同。
A->D->C. 最后,如果由于某种原因 A 选择了通过 D 的更昂贵的路由,则 A->D 的 update_add_htlc
消息将是:
amount_msat
: 5020398cltv_expiry
: current-block-height + 40 + 9 + 42onion_routing_packet
:
amt_to_forward
= 4999999outgoing_cltv_value
= current-block-height + 9 + 42D->C 的 update_add_htlc
将再次与上面 B->C 的直接付款相同。

<br>
This work is licensed under a [Creative Commons Attribution 4.0 International License](http://creativecommons.org/licenses/by/4.0/).
- 原文链接: github.com/lightning/bol...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!