BOLT 2:通道管理的对等协议

  • lightning
  • 发布于 2025-02-11 20:58
  • 阅读 30

本文档详细描述了闪电网络通道管理对等协议,该协议分为建立、正常运行和关闭三个阶段。

BOLT #2: 用于通道管理的对等协议

对等通道协议有三个阶段:建立、正常运行和关闭。

目录

通道

channel_id 的定义

一些消息使用 channel_id 来标识通道。它从 funding transaction(资金交易)派生而来,方法是将 funding_txidfunding_output_index 组合起来,使用大端序异或(即 funding_output_index 更改最后 2 个字节)。

在通道建立之前,使用 temporary_channel_id,这是一个随机 nonce。

请注意,由于来自不同对等点的重复 temporary_channel_id 可能存在,因此在创建资金交易之前,通过其通道 ID 引用通道的 API 本质上是不安全的。 在交换 funding_created 之前,协议提供的唯一通道标识符是 (source_node_id, destination_node_id, temporary_channel_id) 元组。 请注意,在资金交易确认之前,任何通过其通道 ID 引用通道的此类 API 也不是持久的——在你知道对应于资金输出的 script pubkey 之前,没有任何因素可以阻止重复的通道 ID。

通道建立

在进行身份验证和初始化连接后(分别参见 BOLT #8BOLT #1),可以开始通道建立。这包括资金节点(funder)发送 open_channel 消息,然后响应节点(fundee)发送 accept_channel。 锁定通道参数后,funder 可以创建 funding transaction 和两种版本的 commitment transaction,如 BOLT #3 中所述。 然后,funder 通过 funding_created 消息发送 funding output 的输出点(outpoint),以及 fundee 版本的 commitment transaction 的签名。 一旦 fundee 获知 funding outpoint,它就能够生成 funder 版本的 commitment transaction 的签名,并使用 funding_signed 消息发送它。

一旦通道 funder 收到 funding_signed 消息,它必须将 funding transaction 广播到比特币网络。 发送/接收 funding_signed 消息后,双方应等待 funding transaction 进入区块链并达到指定的深度(确认数)。 在双方都发送了 funding_locked 消息后,通道建立完成,可以开始正常运行。 funding_locked 消息包含将用于构建通道身份验证证明的信息。

        +-------+                              +-------+
        |       |--(1)---  open_channel  ----->|       |
        |       |<-(2)--  accept_channel  -----|       |
        |       |                              |       |
        |   A   |--(3)--  funding_created  --->|   B   |
        |       |<-(4)--  funding_signed  -----|       |
        |       |                              |       |
        |       |--(5)--- funding_locked  ---->|       |
        |       |<-(6)--- funding_locked  -----|       |
        +-------+                              +-------+

        - 其中节点 A 是 'funder',节点 B 是 'fundee'

如果在任何阶段失败,或者如果一个节点认为另一节点提供的通道条款不合适,则通道建立失败。

请注意,多个通道可以并行运行,因为所有通道消息都由 temporary_channel_id(在创建 funding transaction 之前)或 channel_id(从 funding transaction 派生)标识。

open_channel 消息

此消息包含有关节点的信息,并表明其希望设置新通道。这是创建 funding transaction 和两种版本的 commitment transaction 的第一步。

  1. 类型:32 (open_channel)

  2. 数据:

    • [chain_hash:chain_hash]
    • [32*byte:temporary_channel_id]
    • [u64:funding_satoshis]
    • [u64:push_msat]
    • [u64:dust_limit_satoshis]
    • [u64:max_htlc_value_in_flight_msat]
    • [u64:channel_reserve_satoshis]
    • [u64:htlc_minimum_msat]
    • [u32:feerate_per_kw]
    • [u16:to_self_delay]
    • [u16:max_accepted_htlcs]
    • [point:funding_pubkey]
    • [point:revocation_basepoint]
    • [point:payment_basepoint]
    • [point:delayed_payment_basepoint]
    • [point:htlc_basepoint]
    • [point:first_per_commitment_point]
    • [byte:channel_flags]
    • [open_channel_tlvs:tlvs]
  3. tlv_stream: open_channel_tlvs

  4. 类型:

    1. 类型:0 (upfront_shutdown_script)
    2. 数据:
      • [...*byte:shutdown_scriptpubkey]
    3. 类型:1 (channel_type)
    4. 数据:
      • [...*byte:type]

chain_hash 值表示打开的通道将驻留在的确切区块链。这通常是相应区块链的创世哈希。 chain_hash 的存在允许节点跨许多不同的区块链打开通道,以及与同一对等点打开多个区块链内的通道(如果它支持目标链)。

在建立 funding transaction 之前,temporary_channel_id 用于在每个对等点的基础上标识此通道,此时它将被从 funding transaction 派生的 channel_id 替换。

funding_satoshis 是发送方放入通道的金额。 push_msat 是发送方无条件给予接收方的初始资金金额。 dust_limit_satoshis 是低于该阈值不应为此节点的 commitment 或 HTLC transaction 生成输出的阈值(即,低于此金额加上 HTLC transaction 费用的 HTLC 在链上不可强制执行)。 这反映了微小输出不被认为是标准交易并且不会通过比特币网络传播的现实。 channel_reserve_satoshis 是另一个节点要保留的最小金额,作为直接付款。 htlc_minimum_msat 指示此节点将接受的最小价值 HTLC。

max_htlc_value_in_flight_msat 是未完成 HTLC 总价值的上限,允许节点限制其对 HTLC 的风险;同样,max_accepted_htlcs 限制了另一节点可以提供的未完成 HTLC 的数量。

feerate_per_kw 指示每 1000 权重(即,通常使用的“每 1000 vbytes 的 satoshi”的 1/4)的初始费用率,该费用为此侧将支付 commitment 和 HTLC transaction 的费用,如 BOLT #3 中所述(稍后可以通过 update_fee 消息进行调整)。

to_self_delay 是另一个节点的 to-self 输出必须延迟的块数,使用 OP_CHECKSEQUENCEVERIFY 延迟; 这是在发生故障时,它在赎回自己的资金之前必须等待的时间。

funding_pubkey 是 funding transaction 输出的 2-of-2 multisig 脚本中的公钥。

各种 _basepoint 字段用于派生唯一密钥 BOLT #3 中所述,用于每个 commitment transaction。 更改这些密钥可确保每个 commitment transaction 的 transaction ID 对于外部观察者来说是不可预测的,即使看到一个 commitment transaction 也是如此; 此属性对于在将惩罚 transaction 外包给第三方时保持隐私非常有用。

first_per_commitment_point 是要用于第一个 commitment transaction 的 per-commitment point。

目前仅定义了 channel_flags 的最低有效位:announce_channel。 这表明 funding flow 的发起者是否希望向网络公开宣传此通道,如 BOLT #7 中详细说明。

shutdown_scriptpubkey 允许发送节点承诺在相互关闭时资金将流向何处,即使节点稍后遭到破坏,远程节点也应强制执行该承诺。

option_support_large_channel 是一种功能,用于让每个人都知道此节点将接受大于或等于 2^24 的 funding_satoshis。由于它是在 node_announcement 消息中广播的,因此其他节点甚至可以在与它们交换 init 消息之前使用它来识别愿意接受大型通道的对等点。

定义的通道类型

通道类型是一个显式枚举:为了将来定义的方便,它们重用甚至特征位,但它们不是任意组合(它们表示影响通道操作的持久特征)。

当前定义的类型是:

  • 没有特征(没有设置位)
  • option_static_remotekey (bit 12)
  • option_anchor_outputsoption_static_remotekey(第 20 位和第 12 位)
  • option_anchors_zero_fee_htlc_txoption_static_remotekey(第 22 位和第 12 位)
要求

发送节点:

  • 必须确保 chain_hash 值标识它希望在其内打开通道的链。
  • 必须确保 temporary_channel_id 与同一对等点的任何其他通道 ID 是唯一的。
  • 如果两个节点都声明了 option_support_large_channel
    • 可以将 funding_satoshis 设置为大于或等于 2^24 satoshi。
  • 否则:
    • 必须将 funding_satoshis 设置为小于 2^24 satoshi。
  • 必须将 push_msat 设置为等于或小于 1000 * funding_satoshis
  • 必须将 funding_pubkeyrevocation_basepointhtlc_basepointpayment_basepointdelayed_payment_basepoint 设置为压缩格式的有效 secp256k1 公钥。
  • 必须将 first_per_commitment_point 设置为用于初始 commitment transaction 的 per-commitment point,如 BOLT #3 中指定的派生。
  • 必须将 channel_reserve_satoshis 设置为大于或等于 dust_limit_satoshis
  • 必须将 channel_flags 中的未定义位设置为 0。
  • 如果两个节点都声明了 option_upfront_shutdown_script 功能:
    • 必须包含 upfront_shutdown_script,其中包含 shutdown_scriptpubkey 所需的有效 shutdown scriptpubkey,或者零长度的 shutdown_scriptpubkey(即 0x0000)。
  • 否则:
    • 可以包含 upfront_shutdown_script
  • 如果它包含 open_channel_tlvs
    • 必须包含 upfront_shutdown_script
  • 如果协商了 option_channel_type
    • 必须设置 channel_type
  • 如果它包含 channel_type
    • 必须将其设置为表示其想要的类型的已定义类型。
    • 必须使用尽可能小的位图来表示通道类型。
    • 不应将其设置为包含未协商的特征的类型。

发送节点应该:

  • 设置足够的 to_self_delay,以确保发送方可以在接收方行为不端的情况下不可逆转地花费 commitment transaction 输出。
  • feerate_per_kw 设置为至少可以使其 transaction 立即包含在块中的估计速率。
  • dust_limit_satoshis 设置为足够的值,以允许 commitment transaction 通过比特币网络传播。
  • htlc_minimum_msat 设置为其愿意从此对等方接受的最小价值 HTLC。

接收节点必须:

  • 忽略 channel_flags 中的未定义位。
  • 如果在收到先前的 open_channel 之后,但在收到 funding_created 消息之前,连接已重新建立:
    • 接受新的 open_channel 消息。
    • 丢弃先前的 open_channel 消息。

如果满足以下条件,则接收节点可以使通道失败:

  • 协商了 option_channel_type,但消息不包含 channel_type
  • announce_channelfalse (0),但它希望公开声明该通道。
  • funding_satoshis 太小。
  • 它认为 htlc_minimum_msat 太大。
  • 它认为 max_htlc_value_in_flight_msat 太小。
  • 它认为 channel_reserve_satoshis 太大。
  • 它认为 max_accepted_htlcs 太小。
  • 它认为 dust_limit_satoshis 太大。

如果满足以下条件,则接收节点必须使通道失败:

  • chain_hash 值设置为接收方未知的链的哈希值。
  • push_msat 大于 funding_satoshis * 1000。
  • to_self_delay 非常大。
  • max_accepted_htlcs 大于 483。
  • 它认为 feerate_per_kw 对于及时处理来说太小或非常大。
  • funding_pubkeyrevocation_basepointhtlc_basepointpayment_basepointdelayed_payment_basepoint 不是压缩格式的有效 secp256k1 公钥。
  • dust_limit_satoshis 大于 channel_reserve_satoshis
  • dust_limit_satoshis 小于 354 satoshis(参见 BOLT 3)。
  • 初始 commitment transaction 的 funder 金额不足以支付全部 费用
  • 初始 commitment transaction 的 to_localto_remote 金额都小于或等于 channel_reserve_satoshis(参见 BOLT 3)。
  • funding_satoshis 大于或等于 2^24 并且接收方不支持 option_support_large_channel
  • 它支持 channel_typechannel_type 已设置,并且 type 不合适。

接收节点不得:

  • 在 funding transaction 达到足够的深度之前,认为使用 push_msat 收到的资金已收到。
理由

在实施尚未被认为是稳定时,要求 funding_satoshis 小于 2^24 satoshi 是一个临时的自我施加的限制,可以通过声明 option_support_large_channel 来取消。

通道储备金由对等方的 channel_reserve_satoshis 指定:建议占通道总额的 1%。 通道的每一方都维护此储备金,因此如果它尝试广播旧的、被撤销的 commitment transaction,它总是会损失一些东西。 最初,可能无法满足此储备金,因为只有一方拥有资金; 但该协议确保始终朝着满足此储备金取得进展,并且一旦满足,就会保持不变。

发送方可以使用非零 push_msat 无条件地向接收方提供初始资金,但即使在这种情况下,我们也要确保 funder 有足够的剩余资金来支付费用,并且一方可以花费一定的金额(这也意味着至少有一个非尘埃输出)。 请注意,像任何其他链上 transaction 一样,只有在 funding transaction 得到充分确认(在此之前存在双重支出的风险)之后,此付款才是确定的,并且可能需要单独的方法来通过链上确认来证明付款。

feerate_per_kw 通常只与发送方(支付费用的人)有关,但 HTLC transaction 也会支付费用; 因此,非常大的费用率也会惩罚接收方。

htlc_basepointpayment_basepoint 分开可以提高安全性:节点需要与 htlc_basepoint 关联的密钥才能生成协议的 HTLC 签名,但 payment_basepoint 的密钥可以存储在冷存储中。

根据 dust_limit_satoshischannel_reserve_satoshis 不被认为是尘埃的要求消除了所有输出都将作为尘埃消除的情况。 accept_channel 中类似的 requirements 确保双方的 channel_reserve_satoshis 都高于 dust_limit_satoshis

接收方不应接受大的 dust_limit_satoshis,因为这可能用于 griefing 攻击,在该攻击中,对等方发布其 commitment,其中包含大量尘埃 htlc,这些 htlc 实际上变成了矿工费用。

有关如何处理通道故障的详细信息,请参见 BOLT 5:通道故障

accept_channel 消息

此消息包含有关节点的信息,并表明其接受新通道。这是创建 funding transaction 和两种版本的 commitment transaction 的第二步。

  1. 类型:33 (accept_channel)

  2. 数据:

    • [32*byte:temporary_channel_id]
    • [u64:dust_limit_satoshis]
    • [u64:max_htlc_value_in_flight_msat]
    • [u64:channel_reserve_satoshis]
    • [u64:htlc_minimum_msat]
    • [u32:minimum_depth]
    • [u16:to_self_delay]
    • [u16:max_accepted_htlcs]
    • [point:funding_pubkey]
    • [point:revocation_basepoint]
    • [point:payment_basepoint]
    • [point:delayed_payment_basepoint]
    • [point:htlc_basepoint]
    • [point:first_per_commitment_point]
    • [accept_channel_tlvs:tlvs]
  3. tlv_stream: accept_channel_tlvs

  4. 类型:

    1. 类型:0 (upfront_shutdown_script)
    2. 数据:
      • [...*byte:shutdown_scriptpubkey]
    3. 类型:1 (channel_type)
    4. 数据:
      • [...*byte:type]
要求

temporary_channel_id 必须与 open_channel 消息中的 temporary_channel_id 相同。

发送方:

  • 应该将 minimum_depth 设置为它认为合理的块数,以避免对 funding transaction 进行双重支出。
  • 必须将 channel_reserve_satoshis 设置为大于或等于 open_channel 消息中的 dust_limit_satoshis
  • 必须将 dust_limit_satoshis 设置为小于或等于 open_channel 消息中的 channel_reserve_satoshis
  • 如果协商了 option_channel_type
    • 必须将 channel_type 设置为 open_channel 中的 channel_type

接收方:

  • 如果 minimum_depth 非常大:
    • 可以拒绝该通道。
  • 如果 channel_reserve_satoshis 小于 open_channel 消息中的 dust_limit_satoshis
    • 必须拒绝该通道。
  • 如果 open_channel 消息中的 channel_reserve_satoshis 小于 dust_limit_satoshis
    • 必须拒绝该通道。
  • 如果 channel_type 已设置,并且 open_channel 中已设置 channel_type,并且它们的类型不相等:
    • 必须拒绝该通道。
  • 如果协商了 option_channel_type,但消息不包含 channel_type
    • 可以拒绝该通道。

其他字段具有与其在 open_channel 中的对应字段相同的 requirements。

funding_created 消息

此消息描述了 funder 为初始 commitment transaction 创建的 outpoint。 在通过 funding_signed 收到对等方的签名后,它将广播 funding transaction。

  1. 类型:34 (funding_created)
  2. 数据:
    • [32*byte:temporary_channel_id]
    • [sha256:funding_txid]
    • [u16:funding_output_index]
    • [signature:signature]
要求

发送方必须设置:

  • temporary_channel_idopen_channel 消息中的 temporary_channel_id 相同。
  • funding_txid 设置为非可延展 transaction 的 transaction ID,
    • 并且不得广播此 transaction。
  • funding_output_index 设置为与资金 transaction 输出相对应的 transaction 的输出编号,如 BOLT #3 中所定义。
  • signature 设置为使用其 funding_pubkey 对于初始 commitment transaction 的有效签名,如 BOLT #3 中所定义。

发送方:

  • 在创建资金 transaction 时:
    • 应该仅使用 BIP141(隔离见证)输入。
    • 应该确保资金 transaction 在接下来的 2016 个区块中得到确认。

接收方:

  • 如果 signature 不正确或不符合 LOW-S 标准规则<sup>LOWS</sup>:
    • 必须发送一个 warning 并关闭连接,或者发送一个 error 并使通道失败。
理由

funding_output_index 只能是 2 个字节,因为这就是它被打包到 channel_id 中并在整个 gossip 协议中使用的方式。 65535 个输出的限制不应过于繁重。

具有所有隔离见证输入的 transaction 不可延展,因此建议使用资金 transaction。

funder 可以在找零输出上使用 CPFP,以确保资金 transaction 在 2016 个区块之前得到确认,否则 fundee 可能会忘记该通道。

funding_signed 消息

此消息为 funder 提供了第一个 commitment transaction 所需的签名,因此它可以广播该 transaction,知道资金可以在需要时赎回。

此消息引入了 channel_id 来标识通道。它从资金 transaction 派生而来,方法是将 funding_txidfunding_output_index 组合起来,使用大端序异或(即 funding_output_index 更改最后 2 个字节)。

  1. 类型:35 (funding_signed)
  2. 数据:
    • [channel_id:channel_id]
    • [signature:signature]
要求

双方:

  • 如果 channel_typeopen_channelaccept_channel 中都存在:
    • 这是 channel_type(它们必须相等,以上要求)
  • 否则:
    • 如果协商了 option_anchors_zero_fee_htlc_tx
      • channel_typeoption_anchors_zero_fee_htlc_txoption_static_remotekey(第 22 位和第 12 位)
    • 否则,如果协商了 option_anchor_outputs
      • channel_typeoption_anchor_outputsoption_static_remotekey(第 20 位和第 12 位)
    • 否则,如果协商了 option_static_remotekey
      • channel_typeoption_static_remotekey(第 12 位)
    • 否则:
      • channel_type 为空
  • 必须将该 channel_type 用于所有 commitment transaction。

发送方必须设置:

  • channel_id 通过异或 funding_created 消息中的 funding_txidfunding_output_index 来设置。
  • signature 设置为有效签名,使用其 funding_pubkey 对于初始 commitment transaction 的有效签名,如 BOLT #3 中所定义。

接收方:

  • 如果 signature 不正确或不符合 LOW-S 标准规则<sup>LOWS</sup>:
    • 必须发送一个 warning 并关闭连接,或者发送一个 error 并使通道失败。
  • 在收到有效的 funding_signed 之前,不得广播资金 transaction。
  • 在收到有效的 funding_signed 后:
    • 应该广播资金 transaction。
理由

我们在此刻决定 option_static_remotekeyoption_anchor_outputsoption_anchors_zero_fee_htlc_tx,此时我们必须首先生成 commitment transaction。 在当前连接的 init 消息交换中传达的特征位确定了通道承诺格式在通道的整个生命周期内。 即使以后的重新连接没有协商此参数,此通道也将继续使用 option_static_remotekeyoption_anchor_outputsoption_anchors_zero_fee_htlc_tx; 我们不支持“降级”。

option_anchors_zero_fee_htlc_tx 被认为优于 option_anchor_outputs,而 option_anchor_outputs 又被认为优于 option_static_remotekey,如果协商了多个,则优先选择更好的一个。

funding_locked 消息

此消息指示资金 transaction 已达到 accept_channel 中要求的 minimum_depth。 一旦两个节点都发送了此消息,通道就会进入正常运行模式。

  1. 类型:36 (funding_locked)
  2. 数据:
    • [channel_id:channel_id]
    • [point:next_per_commitment_point]
要求

发送方必须:

  • 除非由 funding_created 消息中的 funding_txidfunding_output_index 给出的 outpoint 恰好将 funding_satoshis 支付给 BOLT #3 中指定的 scriptpubkey,否则不得发送 funding_locked
  • 在发送此消息之前,等待资金 transaction 达到 minimum_depth
  • next_per_commitment_point 设置为将用于以下 commitment transaction 的 per-commitment point,如 BOLT #3 中指定的派生。

非资金节点(fundee):

  • 如果在超时 2016 个区块后没有看到正确的资金 transaction,则应该忘记该通道。

从等待 funding_locked 的那一刻起,如果未在合理的超时后收到另一节点的必要响应,任何节点都可以发送 error 并使通道失败。

理由

非 funder 可以简单地忘记该通道曾经存在过,因为没有资金处于风险之中。 如果 fundee 永远记住该通道,这将构成拒绝服务风险; 因此,建议忘记它(即使 push_msat 的承诺意义重大)。

如果 fundee 在确认之前忘记了该通道,则 funder 将需要广播 commitment transaction 以取回其资金并打开一个新通道。 为避免这种情况,funder 应确保资金 transaction 在接下来的 2016 个区块中得到确认。

通道关闭

节点可以协商相互关闭连接,这与单方面关闭不同,它允许他们立即访问他们的资金,并且可以通过较低的费用进行协商。

关闭分为两个阶段:

  1. 一方表示它想要清除该通道(因此将不接受任何新的 HTLC)
  2. 一旦所有 HTLC 都已解决,最终的通道关闭协商就开始了。
        +-------+                              +-------+
        |       |--(1)-----  shutdown  ------->|       |
        |       |&lt;-(2)-----  shutdown  --------|       |
        |       |                              |       |
        |       | &lt;完成所有挂起的 HTLC> |       |
        |   A   |                 ...          |   B   |
        |       |                              |       |
        |       |--(3)-- closing_signed  F1--->|       |
        |       |&lt;-(4)-- closing_signed  F2----|       |
        |       |              ...             |       |
        |       |--(?)-- closing_signed  Fn--->|       |
        |       |&lt;-(?)-- closing_signed  Fn----|       |
        +-------+                              +-------+

关闭启动:shutdown

任何节点(或两个节点)都可以发送 shutdown 消息以启动关闭,以及它想要支付的 scriptpubkey

  1. 类型:38 (shutdown)
  2. 数据:
    • [channel_id:channel_id]
    • [u16:len]
    • [len*byte:scriptpubkey]
要求

发送节点:

  • 如果它尚未发送 funding_created(如果它是 funder)或 funding_signed(如果它是 fundee):
    • 不得发送 shutdown
  • 可以在 funding_locked 之前发送 shutdown,即在资金 transaction 达到 minimum_depth 之前。
  • 如果接收节点的 commitment transaction 上有待处理的更新:
    • 不得发送 shutdown
  • shutdown 之后,不得发送 update_add_htlc
  • 如果任何一个 commitment transaction 中都没有剩余的 HTLC:
    • shutdown 之后,不得发送任何 update 消息。
  • 应该无法路由在发送 shutdown 之后添加的任何 HTLC。
  • 如果它在 open_channelaccept_channel 中发送了非零长度的 shutdown_scriptpubkey
    • 必须在 scriptpubkey 中发送相同的值。
  • 必须以以下形式之一设置 scriptpubkey

    1. OP_0 20 20 字节(版本 0 pay to witness pubkey 哈希),或者
    2. OP_0 32 32 字节(版本 0 pay to witness script 哈希),或者
    3. 如果(且仅当)协商了 option_shutdown_anysegwit
      • OP_1OP_16(含),后跟 2 到 40 个字节的单个推送 (见证程序版本 1 到 16) 一个接收节点:
  • 如果它还没有收到 funding_signed (如果它是一个 funder) 或 funding_created (如果它是一个 fundee):
    • 应该发送一个 error 并使通道失败。
  • 如果 scriptpubkey 不在上述形式之一中:
    • 应该发送一个 warning
  • 如果它还没有发送 funding_locked
    • 可以shutdown 回复 shutdown 消息
  • 一旦 peer 上没有未完成的更新,除非它已经发送了一个 shutdown
    • 必须用 shutdown 回复 shutdown 消息
  • 如果两个节点都声明了 option_upfront_shutdown_script 功能,并且接收节点在 open_channelaccept_channel 中接收到了非零长度的 shutdown_scriptpubkey,并且 shutdown_scriptpubkey 不等于 scriptpubkey
    • 可以 发送一个 warning
    • 必须使连接失败。
理由

如果通道状态在关闭开始时总是 "干净的"(没有挂起的更改),则避免了在状态不干净时如何操作的问题:发送方总是首先发送一个 commitment_signed

由于关闭意味着终止的意愿,它意味着不会添加或接受新的 HTLC。一旦任何 HTLC 被清除,peer 可以立即开始关闭协商,因此我们禁止进一步更新 commitment transaction(特别是,否则 update_fee 是可能的)。

scriptpubkey 表格仅包括比特币网络接受的标准隔离见证表格,这确保了生成的交易将传播给矿工。然而,旧节点可能会发送非隔离见证脚本,为了向后兼容性,可以接受这些脚本(但需要注意,如果此输出不满足 dust relay 要求,则强制关闭)。

option_upfront_shutdown_script 功能意味着节点想要预先提交到 shutdown_scriptpubkey,以防它以某种方式受到损害。这是一个微弱的承诺(一个恶意的实现往往会忽略像这样的规范!),但它通过要求接收节点的合作来更改 scriptpubkey,从而提供了安全性的增量改进。

shutdown 响应要求意味着节点在回复之前发送 commitment_signed 以提交任何未完成的更改;然而,理论上它可以重新连接,这将简单地消除所有未决的未提交更改。

关闭协商: closing_signed

一旦关闭完成并且通道中没有 HTLC,最终的当前 commitment transaction 将没有任何 HTLC,并且开始关闭费用协商。funder 选择它认为公平的费用,并使用来自 shutdown 消息的 scriptpubkey 字段(以及它选择的费用)签署 closing transaction 并发送签名;然后另一个节点以类似的方式回复,使用它认为公平的费用。这个交换一直持续到双方都同意相同的费用或一方使通道失败。

在现代方法中,funder 发送其允许的费用范围,并且非 funder 必须在此范围内选择费用。如果非 funder 选择相同的值,则协商在两条消息后完成,否则 funder 将回复相同的值(在三条消息后完成)。

1. type: 39 (`closing_signed`)
2. data:
   * [`channel_id`:`channel_id`]
   * [`u64`:`fee_satoshis`]
   * [`signature`:`signature`]
   * [`closing_signed_tlvs`:`tlvs`]
1. `tlv_stream`: `closing_signed_tlvs`
2. types:
    1. type: 1 (`fee_range`)
    2. data:
        * [`u64`:`min_fee_satoshis`]
        * [`u64`:`max_fee_satoshis`]
要求

funding 节点:

  • 在收到 shutdown 之后,并且在两个 commitment transaction 中都没有剩余 HTLC:
    • 应该发送一个 closing_signed 消息。

发送节点:

  • 应该根据其对包含在区块中的成本的估计来设置初始 fee_satoshis
  • 应该根据它准备为 close transaction 支付的最低和最高费用来设置 fee_range
  • 如果在合理的时间后没有收到 closing_signed 响应:
    • 必须使通道失败
  • 如果它不是 funder:
    • 应该将 max_fee_satoshis 设置为至少等于接收到的 max_fee_satoshis
    • 应该将 min_fee_satoshis 设置为一个相当低的值
  • 必须将 signature 设置为 close transaction 的比特币签名,如 BOLT #3 中指定的那样。

接收节点:

  • 如果 signature 对于 BOLT #3 中指定的 closing transaction 的任何变体无效,或者不符合 LOW-S-standard 规则<sup>LOWS</sup>:
    • 必须发送一个 warning 并关闭连接,或者发送一个 error 并使通道失败。
  • 如果 fee_satoshis 等于其先前发送的 fee_satoshis
    • 应该签署并广播最终的 closing transaction。
    • 可以 关闭连接。
  • 如果 fee_satoshis 与其先前发送的 fee_range 匹配:
    • 应该使用 fee_satoshis 来签署并广播最终的 closing transaction
    • 如果它与先前发送的 fee_satoshis 值不同,则应该回复一个具有相同 fee_satoshis 值的 closing_signed
    • 可以 关闭连接。
  • 如果消息包含一个 fee_range
    • 如果该 fee_range 与它自己的 fee_range 之间没有重叠:
      • 应该发送一个 warning
      • 如果在合理的时间后没有收到令人满意的 fee_range,则必须使通道失败
    • 否则:
      • 如果它是 funder:
      • 如果 fee_satoshis 不在发送和接收的 fee_range 之间的重叠部分:
        • 必须使通道失败
      • 否则:
        • 必须回复相同的 fee_satoshis
      • 否则(它不是 funder):
      • 如果它已经发送了一个 closing_signed
        • 如果 fee_satoshis 与它发送的值不同:
        • 必须使通道失败
      • 否则:
        • 必须在接收和(即将)发送的 fee_range 之间的重叠部分中提出一个 fee_satoshis
  • 否则,如果 fee_satoshis 不是严格介于其上次发送的 fee_satoshis 和其先前接收的 fee_satoshis 之间,除非它此后重新连接:
    • 应该发送一个 warning 并关闭连接,或者发送一个 error 并使通道失败。
  • 否则,如果接收者同意该费用:
    • 应该回复一个具有相同 fee_satoshis 值的 closing_signed
  • 否则:
    • 必须提出一个 "严格介于" 接收到的 fee_satoshis 和其先前发送的 fee_satoshis 之间的值。

接收节点:

  • 如果 closing transaction 中的一个输出低于其 scriptpubkey 的 dust limit(参见 BOLT 3):
    • 必须使通道失败
理由

当未提供 fee_range 时,"严格介于" 要求确保取得进展,即使每次只增加一个 satoshi。为了避免保持状态并处理断开连接和重新连接之间的极端情况,协商会在重新连接时重新开始。

请注意,如果 closing transaction 被延迟,风险有限,但它将很快被广播;因此,通常没有理由为快速处理支付溢价。

注意,非 funder 不支付费用,因此它没有理由设置最大 feerate。但是,它可能需要一个最小 feerate,以确保交易传播。如有必要,它总是可以使用 CPFP 稍后加速确认,因此该最小值应较低。

可能会发生 closing transaction 不符合比特币的默认 relay 策略的情况(例如,当为低于 546 satoshis 的输出使用非隔离见证 shutdown 脚本时,如果 dust_limit_satoshis 低于 546 satoshis,则可能发生这种情况)。在这种情况下,没有资金处于风险之中,但通道必须被强制关闭,因为 closing transaction 可能永远不会到达矿工。

正常运行

一旦两个节点交换了 funding_locked(并且可选地交换了 announcement_signatures),该通道就可以用于通过 Hashed Time Locked Contracts 进行支付。

更改以批处理方式发送:在 commitment_signed 消息之前发送一条或多条 update_ 消息,如下图所示:

        +-------+                               +-------+
        |       |--(1)---- update_add_htlc ---->|       |
        |       |--(2)---- update_add_htlc ---->|       |
        |       |&lt;-(3)---- update_add_htlc -----|       |
        |       |                               |       |
        |       |--(4)--- commitment_signed --->|       |
        |   A   |&lt;-(5)---- revoke_and_ack ------|   B   |
        |       |                               |       |
        |       |&lt;-(6)--- commitment_signed ----|       |
        |       |--(7)---- revoke_and_ack ----->|       |
        |       |                               |       |
        |       |--(8)--- commitment_signed --->|       |
        |       |&lt;-(9)---- revoke_and_ack ------|       |
        +-------+                               +-------+

与直觉相反,这些更新应用于另一个节点的 commitment transaction;只有当远程节点通过 revoke_and_ack 消息确认它已应用这些更新时,该节点才会将这些更新添加到它自己的 commitment transaction 中。

因此,每个更新都经历以下状态:

  1. 在接收方挂起
  2. 在接收方的最新 commitment transaction 中
  3. ... 并且接收方之前的 commitment transaction 已被撤销,并且更新在发送方挂起
  4. ... 并且在发送方的最新 commitment transaction 中
  5. ... 并且发送方之前的 commitment transaction 已被撤销

由于两个节点的更新是独立的,因此两个 commitment transaction 可能会无限期地不同步。这并不令人担忧:重要的是双方是否已不可撤销地提交了特定的更新(上述最终状态)。

转发 HTLC

一般来说,节点提供 HTLC 有两个原因:发起自己的支付,或转发另一个节点的付款。在转发的情况下,必须小心确保传出 HTLC 无法被兑换,除非传入 HTLC 可以被兑换。以下要求确保始终如此。

当满足以下条件时,HTLC 的相应添加/移除 被认为是不可撤销地提交的

  1. 双方都提交了带有/不带有它的 commitment transaction,并且之前不带有/带有它的任何 commitment transaction 已被撤销,或者
  2. 带有/不带有它的 commitment transaction 已不可逆转地提交到区块链。
要求

一个节点:

  • 在传入 HTLC 被不可撤销地提交之前:
    • 必须不提供与该传入 HTLC 对应的传出 HTLC (update_add_htlc)。
  • 在传出 HTLC 的移除被不可撤销地提交之前,或者直到链上传出 HTLC 输出已通过 HTLC-timeout transaction 花费(具有足够的深度):
    • 必须不使与该传出 HTLC 对应的传入 HTLC (update_fail_htlc) 失败。
  • 一旦传入 HTLC 的 cltv_expiry 达到,或者如果 cltv_expiry 减去 current_height 小于相应传出 HTLC 的 cltv_expiry_delta
    • 必须使该传入 HTLC (update_fail_htlc) 失败。
  • 如果传入 HTLC 的 cltv_expiry 在不合理的未来:
    • 应该使该传入 HTLC (update_fail_htlc) 失败。
  • 在收到传出 HTLC 的 update_fulfill_htlc 之后,或者在从链上 HTLC 支出中发现 payment_preimage 之后:
    • 必须实现与该传出 HTLC 对应的传入 HTLC。
理由

一般来说,必须先处理交换的一方,然后再处理另一方。实现 HTLC 是不同的:preimage 的知识,根据定义,是不可撤销的,传入 HTLC 应尽快实现以减少延迟。

具有不合理长 expiry 的 HTLC 是一种拒绝服务向量,因此不允许。请注意,"不合理" 的确切值目前不明确,并且可能取决于网络拓扑。

cltv_expiry_delta 选择

HTLC 超时后,它可以被实现或超时;必须小心处理此转换,无论是对于提供的 HTLC 还是接收的 HTLC。

考虑以下场景,其中 A 向 B 发送 HTLC,B 转发给 C,C 在收到付款后立即交付货物。

  1. C 需要确保来自 B 的 HTLC 不会超时,即使 B 变得无响应;即,C 可以在 B 在链上使其超时之前在链上实现传入 HTLC。

  2. B 需要确保如果 C 实现来自 B 的 HTLC,它可以实现来自 A 的传入 HTLC;即,B 可以从 C 获取 preimage 并在 A 在链上使其超时之前在链上实现传入 HTLC。

此处的关键设置是 BOLT #7 中的 cltv_expiry_deltaBOLT #11 中相关的 min_final_cltv_expirycltv_expiry_delta 是转发情况(B)中 HTLC CLTV 超时的最小差异。min_final_cltv_expiry 是终端情况(C)中 HTLC CLTV 超时与当前区块高度之间的最小差异。

请注意,如果节点在一个通道中接受 HTLC 并在另一个通道中提供 HTLC,并且 CLTV 超时之间的差异太小,则该节点存在风险。因此,传出 通道的 cltv_expiry_delta 用作节点上的 delta。

假设一些假设,可以得出传出和传入 HTLC 解决方案之间最坏情况的区块数:

  • 最坏情况的重组深度 R 个区块
  • 在放弃无响应的 peer 并放弃链之前,HTLC 超时后的宽限期 G 个区块
  • 交易广播到交易包含在区块中的区块数 S

最坏的情况是转发节点 (B) 花费尽可能长的时间来发现传出 HTLC 实现,并且还花费尽可能长的时间来在链上赎回它:

  1. B->C HTLC 在区块 N 超时,B 等待 G 个区块,直到放弃等待 C。B 或 C 提交到区块链,而 B 花费 HTLC,这需要 S 个区块才能被包含。
  2. 不利情况:C 赢得比赛(刚刚好)并实现 HTLC,B 仅在看到区块 N+G+S+1 时看到该交易。
  3. 最坏情况:存在 R 深度的重组,其中 C 获胜并实现。B 仅在 N+G+S+R 处看到交易。
  4. B 现在需要实现传入的 A->B HTLC,但是 A 无响应:B 再等待 G 个区块,然后放弃等待 A。A 或 B 提交到区块链。
  5. 不利情况:B 在区块 N+G+S+R+G+1 中看到 A 的 commitment transaction,并且必须花费 HTLC 输出,这需要 S 个区块才能被挖掘。
  6. 最坏情况:存在另一个 R 深度的重组,A 使用该重组来花费 commitment transaction,因此 B 在区块 N+G+S+R+G+R 中看到 A 的 commitment transaction,并且必须花费 HTLC 输出,这需要 S 个区块才能被挖掘。
  7. B 的 HTLC 支出需要在超时之前至少 R 深度,否则另一个重组可能允许 A 使交易超时。

因此,最坏的情况是 3R+2G+2S,假设 R 至少为 1。请注意,对于 2 个或更多的 R,其中另一个节点赢得所有交易的三个重组的几率很低。由于使用了很高的费用(并且 HTLC 支出可以使用几乎任意的费用),因此在正常运行期间 S 应该很小;但是,鉴于区块时间不规则,仍然会发生空区块,费用可能会发生很大变化,并且无法在 HTLC 交易上增加费用,因此应将 S=12 视为最小值。S 也是在攻击下变化最大的参数,因此当有不可忽略的金额处于风险中时,可能需要更高的值。宽限期 G 可以很低(1 或 2),因为要求节点尽快超时或履行;但是,如果 G 太低,则会增加由于网络延迟导致的不必要的通道关闭的风险。

需要得出四个值:

  1. 通道的 cltv_expiry_delta3R+2G+2S:如有疑问,至少 34 的 cltv_expiry_delta 是合理的 (R=2, G=2, S=12)。

  2. 提供的 HTLC 的截止日期:通道必须失败并在链上超时之后的截止日期。这是 HTLC 的 cltv_expiry 之后的 G 个区块:1 或 2 个区块是合理的。

  3. 此节点已实现的收到的 HTLC 的截止日期:通道必须失败并且 HTLC 在其 cltv_expiry 之前在链上实现的截止日期。请参阅上面的步骤 4-7,这意味着在 cltv_expiry 之前的 2R+G+S 个区块的截止日期:18 个区块是合理的。

  4. 终端付款接受的最小 cltv_expiry:终端节点 C 的最坏情况是 2R+G+S 个区块(因为,同样,上面的步骤 1-3 不适用)。BOLT #11 中的默认值为 18,与此计算匹配。

要求

一个提供节点:

  • 必须估计其提供的每个 HTLC 的超时截止日期。
  • 必须不提供在 cltv_expiry 之前的超时截止日期的 HTLC。
  • 如果由其提供的 HTLC 位于任一节点的当前 commitment transaction 中,并且超过了此超时截止日期:
    • 应该向接收 peer 发送一个 error(如果已连接)。
    • 必须使通道失败。

一个履行节点:

  • 对于它尝试履行的每个 HTLC:
    • 必须估计一个履行截止日期。
  • 必须使已经超过履行截止日期的 HTLC 失败(并且不转发)。
  • 如果它已履行的 HTLC 位于任一节点的当前 commitment transaction 中,并且超过了此履行截止日期:
    • 应该向提供 peer 发送一个 error(如果已连接)。
    • 必须使通道失败。

添加 HTLC:update_add_htlc

任一节点都可以发送 update_add_htlc 以向另一个节点提供 HTLC,该 HTLC 可以通过支付 preimage 来兑换。金额以毫聪为单位,尽管链上强制执行仅适用于大于 dust limit 的完整聪金额(在 commitment transaction 中,这些金额会向下舍入,如 BOLT #3 中指定的那样)。

onion_routing_packet 部分的格式指示支付的目的地,在 BOLT #4 中进行了描述。

1. type: 128 (`update_add_htlc`)
2. data:
   * [`channel_id`:`channel_id`]
   * [`u64`:`id`]
   * [`u64`:`amount_msat`]
   * [`sha256`:`payment_hash`]
   * [`u32`:`cltv_expiry`]
   * [`1366*byte`:`onion_routing_packet`]
要求

发送节点:

  • 如果它_负责_支付比特币费用:
    • 如果在将该 HTLC 添加到其 commitment transaction 后,它无法以当前的 feerate_per_kw 支付本地或远程 commitment transaction 的费用,同时维持其通道储备(请参阅 更新费用),则不得提供 amount_msat
    • 如果 option_anchors 适用于此 commitment transaction 并且发送节点是 funder:
      • 除了其储备之外,必须能够额外支付 to_local_anchorto_remote_anchor 的费用。
    • 如果在将该 HTLC 添加到其 commitment transaction 后,其剩余余额不允许它在接收或发送未来的其他非 dust HTLC 时支付 commitment transaction 费用,同时维持其通道储备,则不应提供 amount_msat。建议此 "费用峰值缓冲区" 可以处理当前 feerate_per_kw 的两倍,以确保实现之间的可预测性。
  • 如果它_不负责_支付比特币费用:
    • 如果一旦远程节点将该 HTLC 添加到其 commitment transaction,它无法以当前的 feerate_per_kw 支付更新后的本地或远程交易的费用,同时维持其通道储备,则不应提供 amount_msat
  • 必须提供大于 0 的 amount_msat
  • 必须不提供低于接收节点 htlc_minimum_msatamount_msat
  • 必须设置小于 500000000 的 cltv_expiry
  • 如果结果将是在远程 commitment transaction 中提供超过远程节点的 max_accepted_htlcs HTLC:
    • 必须不添加 HTLC。
  • 如果提供的 HTLC 的总和将超过远程节点的 max_htlc_value_in_flight_msat
    • 必须不添加 HTLC。
  • 对于它提供的第一个 HTLC:
    • 必须将 id 设置为 0。
  • 必须为每个连续的 offer 将 id 的值增加 1。

在更新完成后(即在收到 revoke_and_ack 之后),id 不得 重置为 0。必须改为继续递增。

接收节点:

  • 接收到的 amount_msat 等于 0,或者小于其自己的 htlc_minimum_msat
    • 应该发送一个 warning 并关闭连接,或者发送一个 error 并使通道失败。
  • 接收到的 amount_msat 是发送节点无法以当前的 feerate_per_kw 负担的(同时维持其通道储备以及任何 to_local_anchorto_remote_anchor 成本):
    • 应该发送一个 warning 并关闭连接,或者发送一个 error 并使通道失败。
  • 如果发送节点向其本地 commitment transaction 添加了超过接收节点 max_accepted_htlcs HTLC,或者向其本地 commitment transaction 添加了超过接收节点 max_htlc_value_in_flight_msat 价值的提供的 HTLC:
    • 应该发送一个 warning 并关闭连接,或者发送一个 error 并使通道失败。
  • 如果发送节点将 cltv_expiry 设置为大于或等于 500000000:
    • 应该发送一个 warning 并关闭连接,或者发送一个 error 并使通道失败。
  • 必须允许具有相同 payment_hash 的多个 HTLC。
  • 如果发送方先前没有确认对该 HTLC 的承诺:
    • 必须忽略重新连接后重复的 id 值。
  • 如果发生其他 id 违规:
    • 可以 发送一个 warning 并关闭连接,或者发送一个 error 并使通道失败。

onion_routing_packet 包含沿路径的每个 hop 的混淆的 hop 列表和指令。 它通过将 payment_hash 设置为关联数据来提交 HTLC,即在 HMAC 的计算中包括 payment_hash。 这可以防止重放攻击,该攻击将重用具有不同 payment_hash 的先前 onion_routing_packet

理由

无效的金额是明显的协议违反,表明发生了故障。

如果节点不接受具有相同支付哈希的多个 HTLC,则攻击者可以探测以查看节点是否具有现有的 HTLC。这种处理重复项的要求导致了单独的标识符的使用;假设 64 位计数器永远不会环绕。

明确允许为重新连接目的重传未确认的更新;允许在其他时间进行简化了接收方代码(尽管严格检查可能有助于调试)。

max_accepted_htlcs 限制为 483,以确保即使双方都发送了最大数量的 HTLC,commitment_signed 消息仍将小于最大消息大小。它还确保单个 penalty transaction 可以花费整个 commitment transaction,如 BOLT #5 中计算的那样。

等于或大于 500000000 的 cltv_expiry 值将指示以秒为单位的时间,并且该协议仅支持以区块为单位的 expiry。

负责 支付比特币费用的节点应在其储备之上保持 "费用峰值缓冲区",以适应未来的费用增加。 如果没有此缓冲区,负责 支付比特币费用的节点可能会达到无法发送或接收任何非 dust HTLC 的状态,同时维持其通道储备(因为 commitment transaction 的权重增加),从而导致通道降级。有关更多详细信息,请参阅 #728

移除 HTLC:update_fulfill_htlcupdate_fail_htlcupdate_fail_malformed_htlc

为简单起见,节点只能移除由另一个节点添加的 HTLC。移除 HTLC 有四个原因:提供了支付 preimage,它已超时,它无法路由或它格式不正确。

要提供 preimage:

1. type: 130 (`update_fulfill_htlc`)
2. data:
   * [`channel_id`:`channel_id`]
   * [`u64`:`id`]
   * [`32*byte`:`payment_preimage`]

对于超时或路由失败的 HTLC:

1. type: 131 (`update_fail_htlc`)
2. data:
   * [`channel_id`:`channel_id`]
   * [`u64`:`id`]
   * [`u16`:`len`]
   * [`len*byte`:`reason`]

reason 字段是一个不透明的加密blob,为了原始 HTLC 发起人的利益,如 BOLT #4 中所定义; 然而,对于 peer 无法解析它的情况,有一个特殊的格式错误的失败变体:在这种情况下,当前节点采取行动,将其加密到 update_fail_htlc 中以进行转发。

对于无法解析的 HTLC:

1. type: 135 (`update_fail_malformed_htlc`)
2. data:
   * [`channel_id`:`channel_id`]
   * [`u64`:`id`]
   * [`sha256`:`sha256_of_onion`]
   * [`u16`:`failure_code`]
要求

一个节点:

  • 应该尽快移除 HTLC。
  • 应该使超时的 HTLC 失败。
  • 在相应的 HTLC 在双方的 commitment transaction 中被不可撤销地提交之前:
    • 必须不发送 update_fulfill_htlcupdate_fail_htlcupdate_fail_malformed_htlc

接收节点:

  • 如果 id 与其当前 commitment transaction 中的 HTLC 不对应:
    • 必须发送一个 warning 并关闭连接,或者发送一个 error 并使通道失败。
  • 如果 update_fulfill_htlc 中的 payment_preimage 值 没有 SHA256 哈希到相应的 HTLC payment_hash
    • 必须发送一个 warning 并关闭连接,或者发送一个 error 并使通道失败。
  • 如果 failure_code 中没有为 update_fail_malformed_htlc 设置 BADONION 位:
    • 必须发送一个 warning 并关闭连接,或者发送一个 error 并使通道失败。
  • 如果 update_fail_malformed_htlc 中的 sha256_of_onion 与其发送的 onion 不匹配:
    • 可以 重试或选择备用错误响应。
  • 否则,由于 update_fail_malformed_htlc 取消了传出 HTLC 的接收节点:
    • 必须在使用给定的 failure_code 并将数据设置为 sha256_of_onion 的情况下,在发送到最初发送 HTLC 的链接的 update_fail_htlc 中返回错误。
理由

不使 HTLC 超时的节点有通道失败的风险(请参阅 cltv_expiry_delta 选择)。

发送 update_fulfill_htlc 的节点在发送方之前也已 提交到 HTLC,并冒着损失资金的风险。

如果 onion 格式错误,上游节点将无法提取共享密钥以生成响应 - 因此需要特殊的失败消息,使此节点执行此操作。

该节点可以检查上游抱怨的 SHA256 是否与其发送的 onion 匹配,这可能使其能够检测到随机位错误。但是,如果不重新检查发送的实际加密数据包,它将不知道错误是它自己的还是远程的;因此,此类检测留作选项。

提交更新:commitment_signed

当一个节点有远程 commitment 的更改时,它可以应用它们,签署生成的交易(如 BOLT #3 中所定义),然后发送一个 commitment_signed 消息。

1. type: 132 (`commitment_signed`)
2. data:
   * [`channel_id`:`channel_id`]
   * [`signature`:`signature`]
   * [`u16`:`num_htlcs`]
   * [`num_htlcs*signature`:`htlc_signature`]
要求

发送节点:

  • 必须不发送不包含任何 更新的 commitment_signed 消息。
  • 可以 发送仅更改费用的 commitment_signed 消息。
  • 可以 发送除了新的 revocation number 之外没有 改变 commitment transaction 的 commitment_signed 消息(由于 dust、相同的 HTLC 替换或不重要的或多次费用更改)。
  • 必须为与 commitment transaction 的顺序对应的每个 HTLC 交易包含一个 htlc_signature(请参阅 BOLT #3)。
  • 如果它最近没有收到来自远程节点的消息:
    • 应该在使用 ping 之前并等待回复 pong,然后再发送commitment_signed。 接收节点:
  • 一旦所有待处理的更新都被应用:
    • 如果 signature 对于其本地承诺交易无效,或者不符合 LOW-S-standard 规则 <sup>LOWS</sup>:
      • 必须发送一个 warning 并关闭连接,或者发送一个 error 并使通道失效。
    • 如果 num_htlcs 不等于本地承诺交易中 HTLC 输出的数量:
      • 必须发送一个 warning 并关闭连接,或者发送一个 error 并使通道失效。
  • 如果任何 htlc_signature 对于相应的 HTLC 交易无效,或者不符合 LOW-S-standard 规则 <sup>LOWS</sup>:
    • 必须发送一个 warning 并关闭连接,或者发送一个 error 并使通道失效。
  • 必须回复一个 revoke_and_ack 消息。
理由

提供垃圾更新没什么意义:这意味着存在错误。

num_htlcs 字段是冗余的,但使得数据包长度检查完全自包含。

建议要求最近的消息,这认识到网络是不可靠的这一现实:节点可能在发送 commitment_signed 之后才意识到它们的对等节点已离线。 一旦 commitment_signed 被发送,发送方认为自己受到这些 HTLC 的约束,并且在输出 HTLC 完全解决之前无法使相关的传入 HTLC 失效。

请注意,在提供的 HTLC 超时或收到的 HTLC 被花费的情况下,htlc_signature 隐式地执行了时间锁定机制。这样做是为了通过创建比在 HTLC 输出上显式声明时间锁定的更小的脚本来减少费用。

option_anchors 允许 HTLC 交易通过附加其他输入和输出来“自带费用”,因此修改了签名标志。

完成到更新状态的转换:revoke_and_ack

一旦 commitment_signed 的接收者检查了签名并知道它有一个有效的新承诺交易,它将回复 revoke_and_ack 消息中先前承诺交易的承诺preimage。

此消息也隐式地充当对收到 commitment_signed 的确认,因此这是 commitment_signed 发送者应用(对其自身的承诺)在 commitment_signed 之前发送的任何挂起更新的合理时间。

密钥派生的描述在 BOLT #3 中。

  1. type: 133 (revoke_and_ack)
  2. data:
    • [channel_id:channel_id]
    • [32*byte:per_commitment_secret]
    • [point:next_per_commitment_point]
要求

发送节点:

  • 必须将 per_commitment_secret 设置为用于为先前的承诺交易生成密钥的secret。
  • 必须将 next_per_commitment_point 设置为其下一个承诺交易的值。

接收节点:

  • 如果 per_commitment_secret 不是有效的secret key,或者没有生成先前的 per_commitment_point
    • 必须发送一个 error 并使通道失效。
  • 如果 per_commitment_secret 不是由 BOLT #3 中的协议生成的:
    • 可以发送一个 warning 并关闭连接,或者发送一个 error 并使通道失效。

一个节点:

  • 不得广播旧的(已撤销的)承诺交易,
    • 注意: 这样做将允许另一个节点获取所有通道资金。
  • 不应签署承诺交易,除非它即将广播它们(由于连接失败),
    • 注意: 这是为了降低上述风险。

更新费用: update_fee

update_fee 消息由支付比特币费用的节点发送。与任何更新一样,它首先被提交到接收者的承诺交易,然后(一旦确认)被提交到发送者的承诺交易。与 HTLC 不同,update_fee 永远不会关闭,而只是被替换。

存在竞争的可能性,因为接收者可以在收到 update_fee 之前添加新的 HTLC。在这种情况下,一旦 update_fee 最终被接收者确认,发送者可能无法承担自己承诺交易的费用。在这种情况下,费用将低于费用率,如 BOLT #3 中所述。

用于从费用率导出费用的确切计算在 BOLT #3 中给出。

  1. type: 134 (update_fee)
  2. data:
    • [channel_id:channel_id]
    • [u32:feerate_per_kw]
要求

负责支付比特币费用的节点:

  • 应该发送 update_fee 以确保当前费用率足以(以很大的幅度)及时处理承诺交易。

不负责支付比特币费用的节点:

  • 不得发送 update_fee

接收节点:

  • 如果 update_fee 太低而无法及时处理,或者非常大:
    • 必须发送一个 warning 并关闭连接,或者发送一个 error 并使通道失效。
  • 如果发送者不负责支付比特币费用:
    • 必须发送一个 warning 并关闭连接,或者发送一个 error 并使通道失效。
  • 如果发送者无法承担接收节点当前承诺交易的新费用率:
    • 应该发送一个 warning 并关闭连接,或者发送一个 error 并使通道失效。
      • 但可以延迟此检查,直到 update_fee 被提交。
理由

比特币费用是单方面关闭生效所必需的。 对于 option_anchorsfeerate_per_kw 不再像传统承诺格式中那样对保证确认至关重要,但它仍然需要足以进入内存池(满足最小 relay fee 和内存池最小费用)。

对于传统的承诺格式,广播节点没有使用 child-pays-for-parent 来增加其有效费用的一般方法。

鉴于费用的差异,以及该交易将来可能被花费的事实,费用支付者最好保持良好的保证金(比如预期费用要求的 5 倍)对于传统的承诺交易; 但是,由于费用估算方法不同,因此未指定确切的值。

由于费用目前是单方面的(请求创建通道的一方始终支付承诺交易的费用),因此最简单的方法是仅允许它设置费用水平; 但是,由于相同的费用率适用于 HTLC 交易,因此接收节点也必须关心费用的合理性。

消息重传

因为通信传输是不可靠的,并且可能需要不时地重新建立,所以传输的设计已与协议显式分离。

然而,假设我们的传输是有序且可靠的。 重新连接会引入对已接收内容的怀疑,因此在此时存在显式确认。

在通道建立和关闭的情况下,这是非常简单的,因为消息具有显式顺序,但在正常操作期间,更新的确认会延迟到 commitment_signed / revoke_and_ack 交换; 因此不能假定已收到更新。 这也意味着接收节点只需要在收到 commitment_signed 时存储更新。

请注意,BOLT #7 中描述的消息独立于特定通道; 它们的传输要求在那里涵盖,并且除了在 init 之后传输(如所有消息一样)之外,它们与此处的任何要求无关。

  1. type: 136 (channel_reestablish)
  2. data:
    • [channel_id:channel_id]
    • [u64:next_commitment_number]
    • [u64:next_revocation_number]
    • [32*byte:your_last_per_commitment_secret]
    • [point:my_current_per_commitment_point]

next_commitment_number: 承诺编号是每个承诺交易的 48 位递增计数器; 计数器对于通道中的每个对等方都是独立的,并且从 0 开始。它们仅在重新建立的情况下显式地转发到另一个节点,否则它们是隐式的。

要求

资金节点:

  • 断开连接时:
    • 如果它已广播funding交易:
      • 必须记住该通道以进行重新连接。
    • 否则:
      • 不应记住该通道以进行重新连接。

非资金节点:

  • 断开连接时:
    • 如果它已发送 funding_signed 消息:
      • 必须记住该通道以进行重新连接。
    • 否则:
      • 不应记住该通道以进行重新连接。

一个节点:

  • 必须处理新加密传输上先前通道的延续。
  • 断开连接时:
    • 必须撤消另一方发送的任何未提交的更新(即,没有收到 commitment_signed 的所有以 update_ 开头的消息)。
      • 注意:节点可能已经使用了 update_fulfill_htlc 中的 payment_preimage 值,因此 update_fulfill_htlc 的效果并没有完全撤销。
  • 重新连接时:
    • 如果通道处于错误状态:
      • 应该重新传输错误数据包并忽略该通道的任何其他数据包。
    • 否则:
      • 必须为每个通道传输 channel_reestablish
      • 必须等待接收另一个节点的 channel_reestablish 消息,然后才能发送该通道的任何其他消息。

发送节点:

  • 必须将 next_commitment_number 设置为它期望接收的下一个 commitment_signed 的 承诺编号。
  • 必须将 next_revocation_number 设置为它期望接收的下一个 revoke_and_ack 消息的 承诺编号。
  • 如果 option_static_remotekey 适用于承诺交易:
    • 必须将 my_current_per_commitment_point 设置为有效点。
  • 否则:
    • 必须将 my_current_per_commitment_point 设置为其从通道对等方收到的最后一次签名承诺的承诺点(即,发送方将用于单方面关闭的承诺交易对应的 commitment_point)。
  • 如果 next_revocation_number 等于 0:
    • 必须将 your_last_per_commitment_secret 设置为全零
  • 否则:
    • 必须将 your_last_per_commitment_secret 设置为其收到的最后一个 per_commitment_secret

一个节点:

  • 如果在其发送和接收的 channel_reestablish 中,next_commitment_number 都为 1:
    • 必须重新传输 funding_locked
  • 否则:
    • 不得重新传输 funding_locked
  • 重新连接时:
    • 必须忽略收到的任何冗余 funding_locked
  • 如果 next_commitment_number 等于接收节点已发送的最后一个 commitment_signed 消息的承诺编号:
    • 必须为其下一个 commitment_signed 重用相同的承诺编号。
  • 否则:
    • 如果 next_commitment_number 不比接收节点已发送的最后一个 commitment_signed 消息的承诺编号大 1:
      • 应该发送一个 error 并使通道失效。
    • 如果它没有发送 commitment_signed,并且 next_commitment_number 不等于 1:
      • 应该发送一个 error 并使通道失效。
  • 如果 next_revocation_number 等于接收节点已发送的最后一个 revoke_and_ack 的承诺编号,并且接收节点尚未收到 closing_signed
    • 必须重新发送 revoke_and_ack
    • 如果它先前发送了一个需要重新传输的 commitment_signed
      • 必须以与最初传输时相同的相对顺序重新传输 revoke_and_ackcommitment_signed
  • 否则:
    • 如果 next_revocation_number 不比接收节点已发送的最后一个 revoke_and_ack 的承诺编号大 1:
      • 应该发送一个 error 并使通道失效。
    • 如果它没有发送 revoke_and_ack,并且 next_revocation_number 不等于 0:
      • 应该发送一个 error 并使通道失效。

接收节点:

  • 如果 option_static_remotekey 适用于承诺交易:
    • 如果 next_revocation_number 大于上面预期的值,并且 your_last_per_commitment_secret 对于该 next_revocation_number 减 1 是正确的:
      • 不得广播其承诺交易。
      • 应该发送一个 error 以请求对等方使通道失效。
    • 否则:
      • 如果 your_last_per_commitment_secret 与预期值不匹配:
      • 应该发送一个 error 并使通道失效。
  • 否则,如果它支持 option_data_loss_protect
    • 如果 next_revocation_number 大于上面预期的值,并且 your_last_per_commitment_secret 对于该 next_revocation_number 减 1 是正确的:
      • 不得广播其承诺交易。
      • 应该发送一个 error 以请求对等方使通道失效。
      • 应该存储 my_current_per_commitment_point 以检索资金,以防发送节点在链上广播其承诺交易。
    • 否则(your_last_per_commitment_secretmy_current_per_commitment_point 与预期值不匹配):
      • 应该发送一个 error 并使通道失效。

一个节点:

  • 不得假定先前传输的消息已丢失,
    • 如果它已发送先前的 commitment_signed 消息:
      • 必须处理在任何时候由另一方广播相应承诺交易的情况,
      • 注意:如果节点不只是重新传输先前发送的完全相同的 update_ 消息,这一点尤其重要。
  • 重新连接时:
    • 如果它已发送先前的 shutdown
      • 必须重新传输 shutdown

理由

上述要求确保了开放阶段几乎是原子的:如果它没有完成,它会重新开始。 唯一的例外是 funding_signed 消息已发送但未收到。 在这种情况下,资助者将忘记该通道,并可能在重新连接时打开一个新通道; 同时,由于从未收到 funding_locked 或在链上看到 funding 交易,因此另一个节点最终会忘记原始通道。

error 没有确认,因此如果发生重新连接,最好在再次断开连接之前重新传输; 但是,这不是必须的,因为在某些情况下,节点可以完全忘记通道。

closing_signed 也没有确认,因此必须在重新连接时重新传输(尽管协商会在重新连接时重新开始,因此不需要完全重新传输)。 shutdown 的唯一确认是 closing_signed,因此需要重新传输其中一个或另一个。

更新的处理方式类似地是原子的:如果提交未被确认(或未发送),则会重新发送更新。 但是,并不坚持要求 它们必须相同:它们的顺序可能不同,涉及不同的费用,甚至可能缺少现在太旧而无法添加的 HTLC。 要求它们是相同的实际上意味着发送方在每次传输时都要写入磁盘,而此处的方案鼓励为每个发送或接收的 commitment_signed 进行一次持久写入磁盘。 但是,如果你需要重新传输 commitment_signedrevoke_and_ack,则必须保留这两者的相对顺序,否则将导致通道关闭。

在收到 closing_signed 之后,永远不应要求重新传输 revoke_and_ack,因为这将意味着已完成关闭--这只能在远程节点收到 revoke_and_ack 之后发生。

请注意,next_commitment_number 从 1 开始,因为承诺编号 0 是在打开期间创建的。 在发送对承诺编号 1 的 commitment_signed 并收到对承诺编号 0 的撤消之前,next_revocation_number 将为 0。

funding_locked 通过正常操作的开始来隐式确认,已知该操作在收到 commitment_signed 之后开始 - 因此,测试 next_commitment_number 大于 1。

先前的草案坚持认为,资助者“如果已广播 funding 交易,则必须记住......否则不得记住”:这实际上是不可能的要求。 节点必须首先提交到磁盘并其次广播交易,反之亦然。 新的语言反映了这一现实:记住一个尚未广播的通道肯定比忘记一个已经广播的通道更好! 同样,对于被资助者的 funding_signed 消息:记住一个从未打开(并超时)的通道比让资助者在被资助者忘记它的情况下打开它更好。

添加了 option_data_loss_protect 以允许以某种方式落后的节点(例如,已从旧备份还原)检测到它已落后。 落后的节点必须知道它不能广播其当前的承诺交易——这将导致资金的完全损失——因为远程节点可以证明它知道撤销 preimage。 落后的节点返回的 error 应该使另一个节点放弃其当前对链的承诺交易。 另一个节点应该等待该 error,以便使落后的节点有机会首先修复其状态(例如,通过使用不同的备份重新启动)。 如果落后的节点没有最新的备份,那么至少这将允许它恢复非 HTLC 资金(如果 my_current_per_commitment_point 有效)。 但是,这也意味着落后的节点已经透露了这一事实(尽管无法证明:它可能在撒谎),并且另一个节点可以使用它来广播先前的状态。

option_static_remotekey 移除了正在更改的 to_remote 密钥,因此 my_current_per_commitment_point 是不必要的,因此被忽略(为了解析简单起见,它仍然存在并且必须是一个有效的点,但是); 然而,先前密钥的泄露仍然允许检测到已经落后。 但是,实现可以同时提供两者,并且如果未协商 option_static_remotekey,则回退到 option_data_loss_protect 行为。

作者

[ 待定:插入作者列表 ]

Creative Commons License <br> 本作品已获得 Creative Commons Attribution 4.0 International License 许可。

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

0 条评论

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