该文档是闪电网络 BOLT 7 规范,定义了P2P节点和通道发现的机制,包括节点如何广播其ID和地址,以及如何在网络中发现通道和路由。
本规范描述了简单的节点发现、通道发现和通道更新机制,这些机制不依赖于第三方来传播信息。
节点和通道发现服务于两个不同的目的:
为了支持通道和节点发现,支持三个 gossip 消息:
对于节点发现,节点交换 node_announcement
消息,这些消息提供有关节点的其他信息。可能存在
多个 node_announcement 消息,以便更新节点信息。
channel_announcement 消息,其中包含有关两个节点之间新
通道的信息。他们还可以交换 channel_update
消息,这些消息更新有关通道的信息。对于任何通道,只能有
一个有效的 channel_announcement,但预计至少有两条
channel_update 消息。short_channel_idannouncement_signatures 消息channel_announcement 消息node_announcement 消息channel_update 消息short_channel_idshort_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 消息包含有关通道的所有权信息。它将
每个链上比特币密钥与关联的闪电网络节点密钥相关联,反之亦然。
在至少一方使用 channel_update 宣布
其费用水平和到期时间之前,该通道实际上是不可用的。
证明 node_1 和 node_2 之间存在通道需要:
bitcoin_key_1 和
bitcoin_key_2node_1 拥有 bitcoin_key_1node_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 都同意该
公告消息:这是通过来自每个
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。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 在黑名单中:node_id_1 或 node_id_2 的有效 channel_announcement:node_id_1 和 node_id_2 以及此 node_id_1 和 node_id_2 列入黑名单,并忘记任何与它们关联的通道。channel_announcement。需要两个节点都签名以表明他们愿意通过此通道路由其他支付(即成为公共网络的一部分);需要他们的 比特币签名证明他们控制该通道。
将冲突节点列入黑名单不允许发布多个不同的 公告。任何 节点都不应广播此类冲突公告,因为这暗示密钥已泄漏。
虽然在通道具有足够的 depth 之前不应宣传通道,但 对重新广播的要求仅适用于 transaction 尚未移动到不同的块的情况。
为了避免存储过大的消息,但仍然允许合理的未来扩展,允许节点限制重新广播(可能在统计上)。
将来可能会出现新的通道功能:向后兼容(或 可选)功能将具有 奇数 特征位,而不兼容的功能将具有 偶数 特征位 ("It's OK to be odd!")。
node_announcement 消息此 gossip 消息允许节点指示与其关联的额外数据, 除了其公钥之外。为了避免琐碎的拒绝服务攻击, 会忽略与已知通道无关的节点。
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 允许 intelligence 服务分配
节点颜色,例如黑色和酷的昵称,例如“IRATEMONK”和“WISTFULTOLL”。
addresses 允许节点宣布其接受传入的网络连接的意愿:它包含一系列用于连接到
节点的 address descriptor。第一个字节描述地址类型,后跟
该类型的适当数量的字节。
定义了以下 address descriptor 类型:
1: ipv4; data = [4:ipv4_addr][2:port] (长度 6)2: ipv6; data = [16:ipv6_addr][2:port] (长度 18)3: 已弃用(长度 12)。用于包含 Tor v2 onion service。4: Tor v3 onion service; data = [35:onion_addr][2:port] (长度 37)
[32:32_byte_ed25519_pubkey] || [2:checksum] || [1:version],其中
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 是可路由地址。featuresflen 设置为容纳其设置的 features
位所需的最小长度。接收节点:
node_id 不是有效的压缩公钥:
warning。signature 不是有效签名(使用 node_id 对 signature 字段之后整个消息的 double-SHA256 进行签名,包括
附加到末尾的任何未来字段):
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 消息仅在 中继 支付的上下文中有效,而不是在 发送 支付的上下文中有效。在进行支付 A -> B -> C -> D 时,只有与通道 B -> C(由 B 宣布)和 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 秒才能传播每个 hop)。
签名验证的 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)对任何其他对等方都无效,并且会被丢弃。signature 设置为其自身的 node_id 使用 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。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(即使是非公共的),以便学习关联的发起节点的转发参数。signature 不是有效签名,则使用 signature 字段之后整个消息的 double-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 不存在或大于 channel capacity:
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。由于在比特币中,通道被限制为 2^32-1 毫聪,因此 htlc_maximum_msat 具有相同的限制。
反对冗余 channel_update 的建议最大程度地减少了垃圾邮件网络,但有时这是不可避免的。例如,与不可达的对等方的通道最终会导致 channel_update 指示该通道已禁用,而当对等方重新建立联系时,另一个更新会重新启用该通道。由于 gossip 消息是批处理的并替换以前的消息,因此结果可能是一个看似冗余的更新。
通过 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 feature bit 来表示它们支持扩展的 gossip 查询。
query_short_channel_ids/reply_short_channel_ids_end 消息1. type: 261 (`query_short_channel_ids`) (`gossip_queries`)
2. data:
* [`chain_hash`:`chain_hash`]
* [`u16`:`len`]
* [`len*byte`:`encoded_short_ids`]
* [`query_short_channel_ids_tlvs`:`tlvs`]
1. `tlv_stream`: `query_short_channel_ids_tlvs`
2. types:
1. type: 1 (`query_flags`)
2. data:
* [`byte`:`encoding_type`]
* [`...*byte`:`encoded_query_flags`]
encoded_query_flags 是一个 bitfield 数组,每个 bitfield 对应一个 bigsize,每个 short_channel_id 对应一个 bitfield。这些位具有以下含义: |
位位置 | 含义 |
|---|---|---|
| 0 | 发送者想要 channel_announcement |
|
| 1 | 发送者想要 节点 1 的 channel_update |
|
| 2 | 发送者想要 节点 2 的 channel_update |
|
| 3 | 发送者想要 节点 1 的 node_announcement |
|
| 4 | 发送者想要 节点 2 的 node_announcement |
查询标志必须以最小编码方式编码,这意味着一个标志将用单个字节编码。
reply_short_channel_ids_end) (gossip_queries)chain_hash:chain_hash]byte:full_information]这是一种通用机制,允许节点查询特定通道(通过 short_channel_id 标识)的
channel_announcement 和 channel_update 消息。这通常用于以下情况:
节点看到一个它没有 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_idschannel_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_updatechannel_announcement 的任何 node_announcementencoded_short_ids 中第 N 个 short_channel_id 的 query_flag 定义为已解码的 encoded_query_flags 的第 N 个 bigsize。query_flag 的第 0 位:channel_announcementquery_flag 的第 1 位并且它已经从 node_id_1 收到了一个 channel_update:node_id_1 的最新 channel_updatequery_flag 的第 2 位并且它已经从 node_id_2 收到了一个 channel_update:node_id_2 的最新 channel_updatequery_flag 的第 3 位并且它已经从 node_id_1 收到了一个 node_announcement:node_id_1 的最新 node_announcementquery_flag 的第 4 位并且它已经从 node_id_2 收到了一个 node_announcement:node_id_2 的最新 node_announcementquery_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 消息类型:263 (query_channel_range) (gossip_queries)
数据:
chain_hash:chain_hash]u32:first_blocknum]u32:number_of_blocks]query_channel_range_tlvs:tlvs]tlv_stream: query_channel_range_tlvs
类型:
query_option)bigsize:query_option_flags]query_option_flags 是一个位域,表示为最小编码的 bigsize。位的含义如下:
| 位位置 | 含义 |
|---|---|
| 0 | 发送者想要时间戳 |
| 1 | 发送者想要校验和 |
虽然这是可能的,但不要求时间戳而要求校验和是没有意义的:接收节点可能具有具有不同校验和的旧 channel_update,要求它是没有用的。并且如果 channel_update 校验和实际上为 0(这是非常不可能的),则不会对其进行查询。
类型:264 (reply_channel_range) (gossip_queries)
数据:
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
类型:
timestamps_tlv)byte:encoding_type]...*byte:encoded_timestamps]checksums_tlv)...*channel_update_checksums:checksums]对于单个 channel_update,时间戳编码如下:
channel_update_timestampsu32: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_checksumsu32: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,它指定了它想要接收的扩展信息的类型。query_channel_range 的接收方:
query_channel_range 发送所有 reply_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_blocknumfirst_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 要直接向 C 发送 4,999,999 毫聪,它既不会向自己收取费用,也不会添加自己的 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 向 C 发送 4,999,999 毫聪,则需要向 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>
本作品已获得 Creative Commons Attribution 4.0 International License 的许可。
- 原文链接: github.com/lightning/bol...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!