合并转换测试

本文档描述了以太坊合并转换的测试案例,重点关注了TERMINAL_TOTAL_DIFFICULTY和TERMINAL_BLOCK_HASH两个关键参数在以太坊从PoW到PoS过渡期间的作用。测试覆盖了EL和CL客户端在不同场景下的行为,包括链重组、无效终端块处理、以及在启用TERMINAL_BLOCK_HASH覆盖时的特定情况。这些测试旨在验证以太坊客户端在各种合并转换场景中的正确性和鲁棒性。

合并转换测试

本文档中描述的所有测试用例都从合并前的世界开始,并在执行过程中达到终端 PoW,以执行 PoS 转换。

<!-- START doctoc generated TOC please keep comment here to allow auto update --> <!-- 不要编辑此部分,请重新运行 doctoc 以更新 -->

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

TERMINAL_TOTAL_DIFFICULTY

EL 客户端测试

  • [x] [Hive] 重新组织为更高总难度 PoW 链 <details> <summary>点击查看详情</summary>

    • A: Genesis &lt;- B1, B: Genesis &lt;- B1'
    • EL 客户端 1 从 A 开始,EL 客户端 2 从 B 开始
    • 两个客户端具有相同的 TTD 配置
    • B1.totalDifficulty > TTD, B1'.totalDifficulty > TTD
    • B1'.totalDifficulty > B1.totalDifficulty
    • forkchoiceUpdated(head: B1)
    • EL 响应 {status: VALID, latestValidHash: B1, validationError: null}
    • forkchoiceUpdated(head: B1')
    • EL 响应 {status: SYNCING, latestValidHash: null, validationError: null}
    • newPayload(P1) + forkchoiceUpdated(head: P1),其中 B1' &lt;- P1
    • EL 最终同步 B1' 及其后代,并对 forkchoiceUpdated(head: Pn) 响应 {status: VALID, latestValidHash: Pn, validationError: null},其中 B1' &lt;- P1 &lt;- ... &lt;- Pn

    </details>

  • [x] [Hive PR] 合并后重新组织为更高总难度 PoW 链 <details> <summary>点击查看详情</summary>

    • A: Genesis &lt;- B1, B: Genesis &lt;- B1'
    • EL 客户端 1 从 A 开始,EL 客户端 2 从 B 开始
    • 两个客户端具有相同的 TTD 配置
    • B1.totalDifficulty > TTD, B1'.totalDifficulty > TTD
    • B1'.totalDifficulty > B1.totalDifficulty
    • forkchoiceUpdated(head: B1)
    • EL 响应 {status: VALID, latestValidHash: B1, validationError: null}
    • newPayload(P1) + forkchoiceUpdated(head: P1),其中 B1 &lt;- P1
    • forkchoiceUpdated(head: B1')
    • EL 响应 {status: SYNCING, latestValidHash: null, validationError: null}
    • newPayload(P1') + forkchoiceUpdated(head: P1'),其中 B1' &lt;- P1'
    • EL 最终同步 B1' 及其后代,并对 forkchoiceUpdated(head: Pn') 响应 {status: VALID, latestValidHash: Pn', validationError: null},其中 B1' &lt;- P1' &lt;- ... &lt;- Pn'

    </details>

  • [x] [Hive] 重新组织为更低总难度 PoW 链 <details> <summary>点击查看详情</summary>

    • A: Genesis &lt;- B1, B: Genesis &lt;- B1'
    • EL 客户端 1 从 A 开始,EL 客户端 2 从 B 开始
    • 两个客户端具有相同的 TTD 配置
    • B1.totalDifficulty > TTD, B1'.totalDifficulty > TTD
    • B1.totalDifficulty > B1'.totalDifficulty
    • forkchoiceUpdated(head: B1)
    • EL 响应 {status: VALID, latestValidHash: B1, validationError: null}
    • forkchoiceUpdated(head: B1')
    • EL 响应 {status: SYNCING, latestValidHash: null, validationError: null}
    • newPayload(P1) + forkchoiceUpdated(head: P1),其中 B1' &lt;- P1
    • EL 最终同步 B1' 及其后代,并对 forkchoiceUpdated(head: Pn) 响应 {status: VALID, latestValidHash: Pn, validationError: null},其中 B1' &lt;- P1 &lt;- ... &lt;- Pn

    </details>

  • [x] [Hive PR] 合并后重新组织为更低总难度 PoW 链 <details> <summary>点击查看详情</summary>

    • A: Genesis &lt;- B1, B: Genesis &lt;- B1'
    • EL 客户端 1 从 A 开始,EL 客户端 2 从 B 开始
    • 两个客户端具有相同的 TTD 配置
    • B1.totalDifficulty > TTD, B1'.totalDifficulty > TTD
    • B1.totalDifficulty > B1'.totalDifficulty
    • forkchoiceUpdated(head: B1)
    • EL 响应 {status: VALID, latestValidHash: B1, validationError: null}
    • newPayload(P1) + forkchoiceUpdated(head: P1),其中 B1 &lt;- P1
    • forkchoiceUpdated(head: B1')
    • EL 响应 {status: SYNCING, latestValidHash: null, validationError: null}
    • newPayload(P1') + forkchoiceUpdated(head: P1'),其中 B1' &lt;- P1'
    • EL 最终同步 B1' 及其后代,并对 forkchoiceUpdated(head: Pn') 响应 {status: VALID, latestValidHash: Pn', validationError: null},其中 B1' &lt;- P1' &lt;- ... &lt;- Pn'

    </details>

  • [x] [Hive] 双区块重新组织为更高总难度 PoW 链 <details> <summary>点击查看详情</summary>

    • A: Genesis &lt;- B1 &lt;- B2, B: Genesis &lt;- B1' &lt;- B2'
    • EL 客户端 1 从 A 开始,EL 客户端 2 从 B 开始
    • 两个客户端具有相同的 TTD 配置
    • B2.totalDifficulty > TTD, B2'.totalDifficulty > TTD
    • B2'.totalDifficulty > B2.totalDifficulty
    • forkchoiceUpdated(head: B2)
    • EL 响应 {status: VALID, latestValidHash: B2, validationError: null}
    • forkchoiceUpdated(head: B2')
    • EL 响应 {status: SYNCING, latestValidHash: null, validationError: null}
    • newPayload(P1) + forkchoiceUpdated(head: P1),其中 B2' &lt;- P1
    • EL 最终同步 B1'B2' 及其后代,并对 forkchoiceUpdated(head: Pn) 响应 {status: VALID, latestValidHash: Pn, validationError: null},其中 B1' &lt;- B2' &lt;- P1 &lt;- ... &lt;- Pn

    </details>

  • [x] [Hive] 双区块重新组织为更低总难度 PoW 链 <details> <summary>点击查看详情</summary>

    • A: Genesis &lt;- B1 &lt;- B2, B: Genesis &lt;- B1' &lt;- B2'
    • EL 客户端 1 从 A 开始,EL 客户端 2 从 B 开始
    • 两个客户端具有相同的 TTD 配置
    • B2.totalDifficulty > TTD, B2'.totalDifficulty > TTD
    • B2.totalDifficulty > B2'.totalDifficulty
    • forkchoiceUpdated(head: B2)
    • EL 响应 {status: VALID, latestValidHash: B2, validationError: null}
    • forkchoiceUpdated(head: B2')
    • EL 响应 {status: SYNCING, latestValidHash: null, validationError: null}
    • newPayload(P1) + forkchoiceUpdated(head: P1),其中 B2' &lt;- P1
    • EL 最终同步 B1'B2' 及其后代,并对 forkchoiceUpdated(head: Pn) 响应 {status: VALID, latestValidHash: Pn, validationError: null},其中 B1' &lt;- B2' &lt;- P1 &lt;- ... &lt;- Pn

    </details>

  • [x] [Hive] 重新组织为更高高度 PoW 链 <details> <summary>点击查看详情</summary>

    • A: Genesis &lt;- B1, B: Genesis &lt;- B1' &lt;- B2'
    • EL 客户端 1 从 A 开始,EL 客户端 2 从 B 开始
    • 两个客户端具有相同的 TTD 配置
    • B1.totalDifficulty > TTD, B2'.totalDifficulty > TTD
    • forkchoiceUpdated(head: B1)
    • EL 响应 {status: VALID, latestValidHash: B1, validationError: null}
    • forkchoiceUpdated(head: B2')
    • EL 响应 {status: SYNCING, latestValidHash: null, validationError: null}
    • newPayload(P1) + forkchoiceUpdated(head: P1),其中 B2' &lt;- P1
    • EL 最终同步 B1'B2' 及其后代,并对 forkchoiceUpdated(head: Pn) 响应 {status: VALID, latestValidHash: Pn, validationError: null},其中 B1' &lt;- B2' &lt;- P1 &lt;- ... &lt;- Pn

    </details>

  • [x] [Hive PR] 合并后重新组织为更高高度 PoW 链 <details> <summary>点击查看详情</summary>

    • A: Genesis &lt;- B1, B: Genesis &lt;- B1' &lt;- B2'
    • EL 客户端 1 从 A 开始,EL 客户端 2 从 B 开始
    • 两个客户端具有相同的 TTD 配置
    • B1.totalDifficulty > TTD, B2'.totalDifficulty > TTD
    • forkchoiceUpdated(head: B1)
    • EL 响应 {status: VALID, latestValidHash: B1, validationError: null}
    • newPayload(P1) + forkchoiceUpdated(head: P1),其中 B1 &lt;- P1
    • forkchoiceUpdated(head: B2')
    • EL 响应 {status: SYNCING, latestValidHash: null, validationError: null}
    • newPayload(P1') + forkchoiceUpdated(head: P1'),其中 B2' &lt;- P1'
    • EL 最终同步 B1'B2' 及其后代,并对 forkchoiceUpdated(head: Pn') 响应 {status: VALID, latestValidHash: Pn', validationError: null},其中 B1' &lt;- B2' &lt;- P1' &lt;- ... &lt;- Pn'

    </details>

  • [x] [Hive] 重新组织为更低高度 PoW 链 <details> <summary>点击查看详情</summary>

    • A: Genesis &lt;- B1 &lt;- B2, B: Genesis &lt;- B1'
    • EL 客户端 1 从 A 开始,EL 客户端 2 从 B 开始
    • 两个客户端具有相同的 TTD 配置
    • B2.totalDifficulty > TTD, B1'.totalDifficulty > TTD
    • forkchoiceUpdated(head: B2)
    • EL 响应 {status: VALID, latestValidHash: B2, validationError: null}
    • forkchoiceUpdated(head: B1')
    • EL 响应 {status: SYNCING, latestValidHash: null, validationError: null}
    • newPayload(P1) + forkchoiceUpdated(head: P1),其中 B1' &lt;- P1
    • EL 最终同步 B1' 及其后代,并对 forkchoiceUpdated(head: Pn) 响应 {status: VALID, latestValidHash: Pn, validationError: null},其中 B1' &lt;- P1 &lt;- ... &lt;- Pn

    </details>

  • [x] [Hive PR] 合并后重新组织为更低高度 PoW 链 <details> <summary>点击查看详情</summary>

    • A: Genesis &lt;- B1 &lt;- B2, B: Genesis &lt;- B1'
    • EL 客户端 1 从 A 开始,EL 客户端 2 从 B 开始
    • 两个客户端具有相同的 TTD 配置
    • B2.totalDifficulty > TTD, B1'.totalDifficulty > TTD
    • forkchoiceUpdated(head: B2)
    • EL 响应 {status: VALID, latestValidHash: B2, validationError: null}
    • newPayload(P1) + forkchoiceUpdated(head: P1),其中 B2 &lt;- P1
    • forkchoiceUpdated(head: B1')
    • EL 响应 {status: SYNCING, latestValidHash: null, validationError: null}
    • newPayload(P1') + forkchoiceUpdated(head: P1'),其中 B1' &lt;- P1'
    • EL 最终同步 B1' 及其后代,并对 forkchoiceUpdated(head: Pn') 响应 {status: VALID, latestValidHash: Pn', validationError: null},其中 B1' &lt;- P1' &lt;- ... &lt;- Pn'

    </details>

  • [x] [Hive PR] 重新组织为具有无效终端区块的链(Block.totalDifficulty &lt; TTD) <details> <summary>点击查看详情</summary>

    • A: Genesis &lt;- B1 &lt;- B2, B: Genesis &lt;- B1'
    • EL 客户端 1 从 A 开始,EL 客户端 2 从 B 开始
    • EL 客户端 1 具有比 EL 客户端 2 更高的 TTD 配置,因此:
    • 对于 EL 客户端 1,B2.totalDifficulty > TTD, B1.totalDifficulty &lt; TTD, B1'.totalDifficulty &lt; TTD
    • 对于 EL 客户端 2,B1.totalDifficulty > TTD, B1'.totalDifficulty > TTD
    • forkchoiceUpdated(head: B2)
    • EL 响应 {status: VALID, latestValidHash: B2, validationError: null}
    • forkchoiceUpdated(head: B1')
    • EL 响应 {status: SYNCING, latestValidHash: null, validationError: null}
    • newPayload(P1) + forkchoiceUpdated(head: P1),其中 B1' &lt;- P1
    • EL 最终同步 B1' 及其后代,但 head 仍然是 B2

    </details>

  • [x] [Hive PR] 重新组织为具有无效终端区块的链(Block.Parent.totalDifficulty > TTD) <details> <summary>点击查看详情</summary>

    • A: Genesis &lt;- B1, B: Genesis &lt;- B1' &lt;- B2'
    • EL 客户端 1 从 A 开始,EL 客户端 2 从 B 开始
    • EL 客户端 1 具有比 EL 客户端 2 更低的 TTD 配置,因此:
    • 对于 EL 客户端 1,B1.totalDifficulty > TTD, B1'.totalDifficulty > TTD
    • 对于 EL 客户端 2,B1.totalDifficulty &lt; TTD, B1'.totalDifficulty &lt; TTD, B2'.totalDifficulty > TTD
    • forkchoiceUpdated(head: B1)
    • EL 响应 {status: VALID, latestValidHash: B1, validationError: null}
    • forkchoiceUpdated(head: B2')
    • EL 响应 {status: SYNCING, latestValidHash: null, validationError: null}
    • newPayload(P1) + forkchoiceUpdated(head: P1),其中 B2' &lt;- P1
    • EL 最终同步 B1'B2' 及其后代,但 head 仍然是 B1

    </details>

  • [x] [Hive PR] 使用 PREVRANDAO 操作码交易重新组织为更低高度 PoW 链 <details> <summary>点击查看详情</summary>

    • A: Genesis &lt;- B1 &lt;- B2, B: Genesis &lt;- B1'
    • EL 客户端 1 从 A 开始,EL 客户端 2 从 B 开始
    • 两个客户端具有相同的 TTD 配置
    • B2.totalDifficulty > TTD, B1'.totalDifficulty > TTD
    • B1B1' 包含 Tx1
    • B2 包含 Tx2
    • Tx1Tx2DIFFICULTY 操作码保存到存储
    • forkchoiceUpdated(head: B2)
    • EL 响应 {status: VALID, latestValidHash: B2, validationError: null}
    • 存储匹配 DIFFICULTY 操作码
    • forkchoiceUpdated(head: B1')
    • EL 响应 {status: SYNCING, latestValidHash: null, validationError: null}
    • newPayload(P1) + forkchoiceUpdated(head: P1),其中 B1' &lt;- P1
    • P1 包含 Tx2
    • EL 最终同步 B1' 及其后代,并对 forkchoiceUpdated(head: Pn) 响应 {status: VALID, latestValidHash: Pn, validationError: null},其中 B1' &lt;- P1 &lt;- ... &lt;- Pn
    • 存储匹配 PREVRANDAO 操作码

    </details>

  • [x] [Hive] 停止跟随 PoW 链 <details> <summary>点击查看详情</summary>

    • Genesis &lt;- B1 &lt;- B2
    • EL 客户端 1 从区块 B1 之前的链开始,EL 客户端 2 从区块 B2 之前的链开始
    • EL 客户端 2 具有比 EL 客户端 1 更高的 TTD 配置
    • forkchoiceUpdated 未发送到 EL 客户端 1
    • 等待两分钟
    • EL 客户端 1 不会将 B2 合并到其链中

    </details>

  • [x] [Hive] 长 PoW 链同步 <details> <summary>点击查看详情</summary>

    • Genesis &lt;- B1 &lt;- ... &lt;- B1024
    • EL 客户端 1 从区块 B1 之前的链开始,EL 客户端 2 从区块 B1024 之前的链开始
    • 两个客户端具有相同的 TTD 配置
    • B1024.totalDifficulty > TTD
    • forkchoiceUpdated(head: B1024)
    • EL 响应 {status: SYNCING, latestValidHash: null, validationError: null}
    • newPayload(P1) + forkchoiceUpdated(head: P1),其中 B1024 &lt;- P1
    • EL 最终同步 B1 &lt;- ... &lt;- B1024,并对 forkchoiceUpdated(head: Pn) 响应 {status: VALID, latestValidHash: Pn, validationError: null},其中 B1 &lt;- ... &lt;- B1024 &lt;- P1 &lt;- ... &lt;- Pn

    </details>

  • [x] [Hive] 提议有效的转换区块 <details> <summary>点击查看详情</summary>

    • Genesis &lt;- ... &lt;- TBTB 是有效的终端区块
    • EL 从 TB 作为头部开始
    • forkchoiceUpdated(headBlockHash: TB.blockHash, payloadAttributes: mergeTransitionBlockAttributes)
    • EL 的头部设置为 headBlockHash
    • EL 返回 {status: VALID, payloadId: mergeTransitionPayloadId}
    • getPayload(mergeTransitionPayloadId)
    • EL 返回合并转换 payload
      • ommersHash == 0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347
      • difficulty == 0
      • mixHash == mergeTransitionBlockAttributes.prevRandao
      • nonce == 0x0000000000000000
    • newPayload(mergeTransitionPayload)
    • EL 返回 VALID

    </details>

  • [x] [Hive] 在错过 fcU 的有效链上转换 <details> <summary>点击查看详情</summary>

    • Genesis &lt;- ... &lt;- TB &lt;- P1 &lt;- P2 &lt;- P3TB 是有效的终端区块
    • EL 从 TB 作为头部开始
    • newPayload(P1)
    • EL 返回 {status: VALID, latestValidHash: payload.blockHash}
    • EL 的头部指向 TB
    • newPayload(P2)
    • EL 返回 {status: VALID, latestValidHash: payload.blockHash}
    • EL 的头部指向 TB
    • newPayload(P3)
    • EL 返回 {status: VALID, latestValidHash: payload.blockHash}
    • EL 的头部指向 TB
    • forkchoiceUpdated(head: P3, safe: P2, finalized: P1)
    • EL 的头部更新为 P3
    • eth_getBlockByNumber(safe) 返回 P2
    • eth_getBlockByNumber(finalized) 返回 P1

    </details>

  • [x] [Hive] 在无效终端区块之上构建 <details> <summary>点击查看详情</summary>

    • Genesis &lt;- ... &lt;- INV_TBINV_TB 是一个有效 PoW无效终端区块,即 INV_TB.TD &lt; TTDINV_TB.parent.TD >= TTD 无法轻松检查,因为 EL 不会处理此类区块
    • EL 从 INV_TB 作为头部开始
    • forkchoiceUpdated(headBlockHash: TB.blockHash, payloadAttributes: mergeTransitionBlockAttributes)
    • EL 返回 {status: INVALID, latestValidHash: 0x00..00, payloadId: null}
    • 如果 INV_TB.TD &lt; TTD,则 EL 的头部指向 INV_TB,如果 INV_TB.parent.TD >= TTD,则指向 INV_TB.parent

    </details>

  • [x] [Hive] 在有效链上转换 <details> <summary>点击查看详情</summary>

    • Genesis &lt;- ... &lt;- TB &lt;- P1 &lt;- P2 &lt;- P3TB 是有效的终端区块
    • EL 从 TB 作为头部开始
    • newPayload(P1)
    • EL 返回 {status: VALID, latestValidHash: payload.blockHash}
    • EL 的头部指向 TB
    • forkchoiceUpdated(head: P1, safe: 0x00..00, finalized: 0x00..00)
    • EL 返回 {status: VALID, latestValidHash: forkchoiceState.headBlockHash}
    • EL 的头部更新为 P1
    • eth_getBlockByNumber(safe) 返回 -39001: Unknown block 错误
    • eth_getBlockByNumber(finalized) 返回 -39001: Unknown block 错误
    • newPayload(P2) + forkchoiceUpdated(head: P2, safe: P1, finalized: 0x00..00)
    • EL 的头部更新为 P2
    • eth_getBlockByNumber(safe) 返回 -39001: Unknown block 错误
    • eth_getBlockByNumber(finalized) 返回 -39001: Unknown block 错误
    • newPayload(P3) + forkchoiceUpdated(head: P3, safe: P2, finalized: P1)
    • EL 的头部更新为 P3
    • eth_getBlockByNumber(safe) 返回 P2
    • eth_getBlockByNumber(finalized) 返回 P1

    </details>

  • [x] [Hive] 在无效链上转换 <details> <summary>点击查看详情</summary>

    • Genesis &lt;- ... &lt;- INV_TB &lt;- P1INV_TB 是一个有效 PoW无效终端区块:
    • [ ] INV_TB.TD &lt; TTD
    • [ ] INV_TB.parent.TD >= TTD -- 可能需要第二个具有更高 TTD 值的 EL
    • EL 从 INV_TB 作为头部开始
    • newPayload(P1)
    • EL 返回 {status: INVALID, latestValidHash: 0x00..00}
    • 如果 INV_TB.TD &lt; TTD,则 EL 的头部指向 INV_TB,如果 INV_TB.parent.TD >= TTD,则指向 INV_TB.parent

    </details>

  • [x] [Hive] 重新组织为具有无效转换区块的链 <details> <summary>点击查看详情</summary>

    • A: Genesis &lt;- ... &lt;- TB &lt;- P1 &lt;- P2, B: Genesis &lt;- ... &lt;- TB &lt;- INV_P1A.TBB.TB 是有效的终端区块
    • EL 从 A.TB 作为头部开始
    • newPayload(A.P1) + newPayload(A.P2)
    • EL 返回 {status: VALID, latestValidHash: payload.blockHash}
    • EL 的头部指向 A.TB
    • forkchoiceUpdated(head: A.P2, safe: A.P1, finalized: 0x00..00)
    • EL 的头部更新为 A.P2
    • newPayload(B.INV_P1)
    • EL 返回 {status: INVALID/ACCEPTED, latestValidHash: null/0x00..00}
    • EL 的头部指向 A.P2
    • forkchoiceUpdated(head: B.INV_P1, safe: 0x00..00, finalized: 0x00..00)
    • EL 返回 {status: INVALID, latestValidHash: 0x00..00}
    • EL 的头部指向 A.P2

    </details>

  • [x] [Hive] 与具有有效转换的链同步 <details> <summary>点击查看详情</summary>

    • Genesis &lt;- ... &lt;- Bn &lt;- TB &lt;- P1TB 是有效的终端区块
    • EL 从 Bn 作为头部开始
    • newPayload(P1) + forkchoiceUpdated(P1)
    • EL 返回 {status: SYNCING}
    • EL 的头部指向 Bn
    • EL 应该从网络中提取 TB &lt;- P1
    • 轮询 forkchoiceUpdated(P1)
    • EL 返回 {status: VALID, latestValidHash: null}
    • EL 的头部指向 P1

    </details>

  • [x] [Hive] 与具有无效终端区块的链同步 <details> <summary>点击查看详情</summary>

    • Genesis &lt;- ... &lt;- Bn &lt;- INV_TB &lt;- P1 &lt;- P2INV_TB 是一个有效 PoW无效终端区块:
    • [x] [Hive] INV_TB.TD &lt; TTD
    • [x] [Hive] INV_TB.parent.TD >= TTD -- 可能需要第二个具有更高 TTD 值的 EL
    • EL 从 Bn 作为头部开始
    • newPayload(P1) + forkchoiceUpdated(P1)
    • EL 返回 {status: SYNCING}
    • EL 的头部指向 Bn
    • EL 应该从网络中提取 INV_TB &lt;- P1
    • 轮询 forkchoiceUpdated(P1)
    • EL 返回 {status: INVALID, latestValidHash: 0x00..00}
    • EL 的头部指向 Bn

    </details>

  • [x] [Hive] 与终端区块对于 EE 无效的链同步 <details> <summary>点击查看详情</summary>

    • Genesis &lt;- ... &lt;- Bn &lt;- INV_TB &lt;- P1 &lt;- P2INV_TB 满足 TTD,但在执行方面无效
    • EL 从 Bn 作为头部开始
    • newPayload(P1) + forkchoiceUpdated(P1)
    • EL 返回 {status: SYNCING}
    • EL 的头部指向 Bn
    • EL 应该从网络中提取 INV_TB &lt;- P1
    • 轮询 forkchoiceUpdated(P1)
    • EL 返回 {status: INVALID, latestValidHash: 0x00..00}
    • EL 的头部指向 Bn

    </details>

  • [x] [Hive] 与具有无效转换区块的链同步 <* A: Genesis &lt;- ... &lt;- TB &lt;- P1 &lt;- P2, B: Genesis &lt;- ... &lt;- INV_TB &lt;- P1 &lt;- P2, B.INV_TB 是一个 有效的PoW 但一个 无效的终端 区块:

    • [x] [Hive] INV_TB.TD &lt; TTD
    • [x] [Hive] INV_TB.parent.TD >= TTD
    • 这个场景可能需要第二个EL具有更高的 TTD
    • EL 以 A.P2 作为头开始
    • newPayload(B.P2) + forkchoiceUpdated(P2)
    • EL 返回 {status: SYNCING}
    • EL 的头指向 A.P2
    • EL 应该从网络中拉取 B: INV_TB &lt;- P1 &lt;- P2
    • 轮询 forkchoiceUpdated(B.P2)
    • EL 返回 {status: INVALID, latestValidHash: 0x00..00}
    • EL 的头指向 A.P2

    </details>

  • [x] [Hive] 使用具有无效转换(transition)区块的链进行重组和同步 <details> <summary>点击查看详情</summary>

    • A: Genesis &lt;- ... &lt;- TB &lt;- P1 &lt;- P2, B: Genesis &lt;- ... &lt;- TB &lt;- INV_P1 &lt;- P2, B.TB 是一个有效的终端区块, B.INV_P1 是一个无效的 payload
    • EL 以 A.P2 作为头开始
    • newPayload(B.P2) + forkchoiceUpdated(B.P2)
    • EL 返回 {status: SYNCING}
    • EL 的头指向 A.P2
    • 轮询 forkchoiceUpdated(B.P2)
    • EL 返回 {status: INVALID, latestValidHash: 0x00..00}
    • EL 的头指向 A.P2

    </details>

  • [x] [Hive] 使用具有无效的后转换(post-transition)区块的链进行重组和同步 <details> <summary>点击查看详情</summary>

    • A: Genesis &lt;- ... &lt;- TB &lt;- P1 &lt;- P2 &lt;- P3, B: Genesis &lt;- ... &lt;- TB &lt;- P1 &lt;- INV_P2 &lt;- P3, B.TB 是一个有效的终端区块, B.INV_P2 是一个无效的 payload
    • EL 以 A.P2 作为头开始
    • newPayload(B.P3) + forkchoiceUpdated(B.P3)
    • EL 返回 {status: SYNCING}
    • EL 的头指向 A.P3
    • 轮询 forkchoiceUpdated(B.P3)
    • EL 返回 {status: INVALID, latestValidHash: B.P1.blockHash}
    • EL 的头指向 A.P3 (在这种情况下, fcU 将根本不会被应用, 因为它指向无效链上的一个区块, 因此不应该发生 forkchoice 状态的更新; 通常, 当获得 lvh = B.P1.blockHash 时, CL 会发送 fcU(B.P1), 并将规范链切换到 B, 并以 B.P1 作为头)

    </details>

  • [x] [Hive] 停止处理gossiped PoW 区块 <details> <summary>点击查看详情</summary>

    • PoW/Clique 矿工构建一个链并通过 gossip 广播它; 该链超出终端 PoW 区块
    • EL 连接到矿工并且不处理终端区块之后的区块, 也就是头指向终端区块

    </details>

  • [x] [Hive] 停止处理同步的 PoW 区块 <details> <summary>点击查看详情</summary>

    • PoW 客户端以 Genesis &lt;- ... &lt;- TB &lt;- B1 &lt;- B2 &lt;- ... &lt;- Bn 链开始, 其中 TB 是一个有效的终端区块
    • EL 连接到 PoW 客户端并与广播的链同步
    • EL 的头指向 TB

    </details>

  • [x] [Hive] 终端区块被gossiped <details> <summary>点击查看详情</summary>

    • PoW &lt;-> EL1 &lt;-> EL2 视为连接到 EL 客户端 EL1 的 PoW 客户端, 而 EL1 又连接到 EL 客户端 EL2EL2PoW 客户端之间没有直接连接。
    • PoW 客户端 gossip 一个终端区块 TB
    • EL2 客户端的头指向 TB
    • PoW 客户端 gossip TB 的后代
    • EL2 客户端的头指向 TB

    </details>

  • [x] [Hive] 通过 gossip 在多个终端区块之后构建 Payload <details> <summary>点击查看详情</summary>

    • PoW &lt;-> EL 视为连接到 EL 客户端 EL 的 PoW 客户端。
    • PoW 客户端生成并 gossip 多个终端区块 TB1, TB2, ... TBN
    • newPayload(TBN) 被发送到 EL
    • EL 立即返回 {status: VALID, latestValidHash: TBN.BlockHash}

    </details>

CL 客户端测试

  • [x] [Hive] 在有效的链上转换 <details> <summary>点击查看详情</summary>

    • EL: Genesis &lt;- ... &lt;- TB, TB 是一个有效的终端区块
    • CL: Genesis &lt;- ... &lt;- Bellatrix
    • EL 以 TB 作为头开始或者挖掘一条链直到 TB
    • CL 以 Genesis 开始并构建一条链直到 Bellatrix 并升级到 Bellatrix
    • CL 驱动 EL 通过转换并最终确定它
    • eth_getBlockByNumber(latest) 返回头
    • eth_getBlockByNumber(safe) 返回最近被证明是合理的区块
    • eth_getBlockByNumber(finalized) 返回最近被最终确定的区块

    </details>

  • [x] [Hive] 在无效的终端区块之上构建 <details> <summary>点击查看详情</summary>

    • Genesis &lt;- ... &lt;- Bn, Bn 是一个有效的 PoW 但一个 无效的终端 区块, 也就是 Bn.TD &lt; EL.TTD
    • Bn 是对 CL 观察来说的有效的终端区块, 也就是 CL 的 TERMINAL_TOTAL_DIFFICULTY 被配置成以下方式:
    • Bn.TD >= CL.TTD && Bn.parent.TD &lt; CL.TTD
    • EL 以 Bn 作为头开始
    • CL 以 Genesis 开始并构建一条链直到 Bellatrix
    • 转换永远不会发生, 也就是说 CL 的头区块总是包含归零的 payload (ExecutionPayload())

    </details>

  • [x] [Hive] 在具有无效的终端区块的链上转换 <details> <summary>点击查看详情</summary>

    • EL: Genesis &lt;- ... &lt;- Bn
    • 节点: builder, importer; importer 在两层都连接到 builder
    • Bn 是对 builder 观察来说的有效的终端区块, 也就是说 builder 的 TERMINAL_TOTAL_DIFFICULTY 被配置成以下方式:
    • Bn.TD >= TTD && Bn.parent.TD &lt; TTD
    • Bn 是对 importer 观察来说的 无效的 终端区块, 也就是说 builder 的 TERMINAL_TOTAL_DIFFICULTY 被配置成以下方式:
    • Bn.TD &lt; TTD
    • EL 以 Bn 作为头开始
    • builder 以 Genesis 开始并构建一条链直到 Bellatrix 甚至更远
    • Importer 从不导入转换区块并且它的头总是指向转换前的区块

    </details>

  • [x] [Hive] 在具有无效的转换区块的链上转换 <details> <summary>点击查看详情</summary>

    • EL: Genesis &lt;- ... &lt;- Bn
    • 节点: builder, importer; importer 在两层都连接到 builder
    • Bn 是对 builder 和 importer 观察来说的有效的终端区块
    • EL 以 Bn 作为头开始
    • CL 以 Genesis 开始并且 builder 构建一条链直到 Bellatrix 甚至更远
    • Importer 节点的 EL mock 对转换区块的 payload 返回 INVALID
    • Importer 从不导入转换区块并且它的头总是指向转换前的区块

    </details>

  • [x] [Hive] 与具有有效的转换区块的链同步 <details> <summary>点击查看详情</summary>

    • EL.A: Genesis &lt;- ... &lt;- Bn, EL.B: Genesis &lt;- ... &lt;- Bn; AB 具有相等的 TD
    • 节点: builder, importer; builder 开始与 EL.A 同步, importer 与 EL.B 同步, 两条链上相同的 TD 值阻止了 importer 提前拉取 builder 的链
    • builder 以 CL: Genesis 开始并且构建一条链直到 Bellatrix 甚至更远
    • Importer 将不得不从 builder 拉取 EL.A
    • Importer 区块处理暂停并等待 SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY 继续 -- 这可能值得检查
    • 在超时结束后, importer 最终同步并且通过转换

    </details>

  • [x] [Hive] 与具有无效的转换区块的链同步 <details> <summary>点击查看详情</summary>

    • EL.A: Genesis &lt;- ... &lt;- Bn, EL.B: Genesis &lt;- ... &lt;- Bn; AB 具有相等的 TD
    • 节点: builder, importer; builder 开始与 EL.A 同步, importer 与 EL.B 同步, 两条链上相同的 TD 值阻止了 importer 提前拉取 builder 的链
    • A.Bn 是对 builder 观察来说的有效的终端区块
    • A.Bn 可能是对 importer 观察来说的 无效的 终端区块, 但这不是必须的, 因为 EL mock 将模拟这种无效性
    • builder 以 CL: Genesis 开始并且构建一条链直到 Bellatrix 甚至更远
    • Importer 将不得不从 builder 拉取 EL.A
    • Importer 区块处理暂停并等待 SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY 继续 -- 在这个场景中可能不会被检查
    • Importer 的 EL mock 有意地持续返回 SYNCING 以便让 importer 的 CL 达到离转换区块更远的点
    • 一段时间后, EL mock 开始对每一个 newPayloadforkchoiceUpdated 调用使用 {status: INVALID, latestValidHash: 0x00..00} 进行响应 -- 如果终端区块或者转换区块是无效的, 这将会在现实生活中发生
    • Importer 必须使从转换区块开始的区块无效, 并且绝不能再次导入转换区块

    </details>

  • [x] [Hive] 与具有无效的后转换区块的链同步 <details> <summary>点击查看详情</summary>

    • EL.A: Genesis &lt;- ... &lt;- Bn, EL.B: Genesis &lt;- ... &lt;- Bn; AB 具有相等的 TD
    • 节点: builder, importer; builder 开始与 EL.A 同步, importer 与 EL.B 同步, 两条链上相同的 TD 值阻止了 importer 提前拉取 builder 的链
    • A.Bn 是对 builder 和 importer 观察来说的有效的终端区块
    • builder 以 CL: Genesis 开始并且构建一条链直到 Bellatrix 甚至更远
    • Importer 将不得不从 builder 拉取 EL.A
    • Importer 区块处理暂停并等待 SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY 继续 -- 在这个场景中可能不会被检查
    • Importer 的 EL mock 有意地持续返回 SYNCING 以便让 importer 的 CL 达到离 后转换 区块更远的点
    • 一段时间后, EL mock 开始对每一个 newPayloadforkchoiceUpdated 调用使用 {status: INVALID, latestValidHash: transitionBlock.blockHash} 进行响应 -- 如果终端区块或者转换区块是无效的, 这将会在现实生活中发生
    • Importer 必须使从后转换区块开始的区块无效, 并且绝不能再次导入后转换区块, 并且头必须指向转换区块

    </details>

  • [x] [Hive] 使用具有无效的终端区块的链进行重组和同步 <details> <summary>点击查看详情</summary>

    • EL.A: Genesis &lt;- ... &lt;- Bn, EL.B: Genesis &lt;- ... &lt;- Bn; AB 具有相等的 TD
    • 节点: 有效的 builder, 无效的 builder, importer; 有效的 builder 和 importer 开始与 EL.A 同步, 无效的 builder 和 EL.B 同步, 两条链上相同的 TD 值阻止了 importer 和有效的 builder 提前拉取无效的 builder 的链. 有效的 builder 具有 1/2n - x 的验证者, 无效的 builder 具有 1/2 + x 的验证者, 其中 x 是一个很小的数字
    • A.Bn 是对有效的 builder 和 importer 观察来说的有效的终端区块
    • B.Bn 是对 importer 的观察来说的 无效的 终端区块, 但是对无效的 builder 的观察来说是有效的
    • 两个 builder 以 CL: Genesis 开始并且构建一条链直到 Bellatrix 甚至更远. 他们应该保持在 Bellatrix 之前的共识, 然后从转换区块开始分离
    • Importer 的头最终和有效的 builder 一致
    • 理想情况下, 有效的 builder 在无效的 builder 做同样的事情 之前 提出一个转换区块 -- 这些可以通过使用验证者分配来实现
      • 然后由无效的 builder 提出的转换区块将不得不使用 SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY 超时进行乐观地应用 -- 这将给 importer 一个遵循有效链的时间
      • 无效的链被乐观地应用 (因为无效的 builder 具有更多的证明者并且它的链被 fork 选择规则优先选择) 并且 importer 的 EL 最终在这个链上使用 {status: INVALID, latestValidHash: 0x00..00} 进行响应
      • 期望 CL 从转换区块开始使无效的链区块无效, 并且切换回较小的, 但有效的链

    </details>

  • [x] [Hive] Bellatrix 之前的 TTD <details> <summary>点击查看详情</summary>

    • Genesis &lt;- ... &lt;- Bn
    • EL 客户端以 Bn 作为工作量证明链的头开始
    • Bn 对所有客户端来说是一个有效的终端区块
    • 在所有客户端合并了 Bn 之后, 到达 Bellatrix epoch
    • 当到达 Bellatrix 时, 因为 CL 客户端在 Bn 之上构建, 因此转换是成功的。

    </details>

[奖励] TERMINAL_BLOCK_HASH

本节中的场景涵盖了通过指定由 TERMINAL_BLOCK_HASH 参数指定的某个 blockHash 值来指定终端 PoW 区块的情况。

此方案也称为 TERMINAL_BLOCK_HASH 覆盖, 是一种紧急情况方案。在 EL 侧, 覆盖涉及指定以下参数 (按照 EIP-3675):

  • TERMINAL_BLOCK_HASH – 设置为某个区块的哈希,以成为终端 PoW 区块。
  • TERMINAL_BLOCK_NUMBER – 设置为由 TERMINAL_BLOCK_HASH 指定的区块的编号。
  • TERMINAL_TOTAL_DIFFICULTY – 设置为由 TERMINAL_BLOCK_HASH 指定的区块的总难度值。

在 CL 侧,必须通过更新 以下参数 来启用覆盖:

  • TERMINAL_BLOCK_HASH – 设置为某个区块的哈希,以成为终端 PoW 区块。
  • TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH - 设置为 Merge 转换将被激活的 epoch 。

EL 客户端测试

TBH (TERMINAL_BLOCK_HASH) 覆盖的 EL 客户端测试是根据以下 潜在的 实现设计的:

  • EL 总是在达到 TERMINAL_TOTAL_DIFFICULTY 时触发 Merge 升级
  • TERMINAL_BLOCK_HASH + TERMINAL_BLOCK_NUMBER 将规范链列入白名单

实际上,这种语义给出了想要的结果。但是,如果 EL 有两条已达到 TTD 的链,则 EL 可能不会拒绝建立在 blockHash != TBH 的区块之上的转换 payload。但是我们有 CL 侧来强制执行终端区块的 blockHash == TBH。这意味着省略此检查在 EL 侧是可以的。本节中的测试 假设 blockHash == TBH 由 EL 客户端实现强制执行。

考虑到以上想法,本节的目标是检查 TBH 覆盖是否会影响 TTD 区块条件。

  • [ ] 提出具有启用 TBH 覆盖的有效转换区块 <details> <summary>点击查看详情</summary>

    • A: Genesis &lt;- ... &lt;- TB, TB 是一个有效的终端区块, TB.blockHash == TBH
    • B: Genesis &lt;- ... &lt;- Bn, B 链比 A 更重, 除非激活 TBH 覆盖,否则将成为规范链
    • EL 以导入的 AB 开始
    • forkchoiceUpdated(headBlockHash: TB.blockHash, payloadAttributes: mergeTransitionBlockAttributes)
    • EL 的头被设置为 headBlockHash
    • EL 返回 {status: VALID, payloadId: mergeTransitionPayloadId}
    • getPayload(mergeTransitionPayloadId)
    • EL 返回 merge 转换 payload
    • newPayload(mergeTransitionPayload)
    • EL 返回 VALID

    </details>

  • [ ] 在启用 TBH 覆盖的情况下,在无效终端区块之上构建 <details> <summary>点击查看详情</summary>

    • Genesis &lt;- ... &lt;- INV_TB, INV_TB 是一个有效的 PoW 但一个无效的终端区块,即 INV_TB.TD &lt; TTD 并且 TBH != INV_TB.blockHash
    • EL 以 INV_TB 作为头开始
    • forkchoiceUpdated(headBlockHash: TB.blockHash, payloadAttributes: mergeTransitionBlockAttributes)
    • EL 返回 {status: INVALID, latestValidHash: 0x00..00, payloadId: null}
    • 如果 INV_TB.TD &lt; TTD,则 EL 的头指向 INV_TB,如果 INV_TB.parent.TD >= TTD,则指向 INV_TB.parent

    </details>

  • [ ] 使用启用的 TBH 覆盖,在有效链上转换 <details> <summary>点击查看详情</summary>

    • A: Genesis &lt;- ... &lt;- TB &lt;- P1 &lt;- P2 &lt;- P3, TB 是一个有效的终端区块, 也就是 TB.blockHash == TBH
    • B: Genesis &lt;- ... &lt;- Bn, B 链比 A 更重,除非激活 TBH 覆盖,否则将成为规范链
    • EL 以 A 导入到 TB 并且 B 导入到 Bn 开始
    • newPayload(A.P1)
    • EL 返回 {status: VALID, latestValidHash: payload.blockHash}
    • EL 的头指向 TB
    • forkchoiceUpdated(head: P1, safe: 0x00..00, finalized: 0x00..00)
    • EL 返回 {status: VALID, latestValidHash: forkchoiceState.headBlockHash}
    • EL 的头更新为 P1
    • eth_getBlockByNumber(safe) 返回 -39001: Unknown block 错误
    • eth_getBlockByNumber(finalized) 返回 -39001: Unknown block 错误
    • newPayload(P2) + forkchoiceUpdated(head: P2, safe: P1, finalized: 0x00..00)
    • EL 的头更新为 P2
    • eth_getBlockByNumber(safe) 返回 -39001: Unknown block 错误
    • eth_getBlockByNumber(finalized) 返回 -39001: Unknown block 错误
    • newPayload(P3) + forkchoiceUpdated(head: P3, safe: P2, finalized: P1)
    • EL 的头更新为 P3
    • eth_getBlockByNumber(safe) 返回 P2
    • eth_getBlockByNumber(finalized) 返回 P1

    </details>

  • [ ] 在启用 TBH 覆盖的情况下,在无效链上转换 <details> <summary>点击查看详情</summary>

    • Genesis &lt;- ... &lt;- INV_TB &lt;- P1, INV_TB 是一个 有效的 PoW 但一个 无效的终端 区块, 也就是说 TBH != INV_TB.blockHash 并且以下任何一个为真:
    • [ ] INV_TB.TD &lt; TTD
    • [ ] INV_TB.parent.TD >= TTD -- 这可能需要第二个具有更高 TTD 值的 EL
    • EL 以 INV_TB 作为头开始
    • newPayload(P1)
    • EL 返回 {status: INVALID, latestValidHash: 0x00..00}
    • 如果 INV_TB.TD &lt; TTD,则 EL 的头指向 INV_TB,如果 INV_TB.parent.TD >= TTD,则指向 INV_TB.parent

    </details>

  • [ ] 与具有有效转换的链同步,启用 TBH 覆盖 <details> <summary>点击查看详情</summary>

    • A: Genesis &lt;- ... &lt;- Bn &lt;- TB &lt;- P1, TB 是一个有效的终端区块,也就是 TB.blockHash == TBH
    • B: Genesis &lt;- ... &lt;- Bn, B 链比 A 更重,除非激活 TBH 覆盖,否则将成为规范链
    • EL 以导入的 B 开始
    • newPayload(P1) + forkchoiceUpdated(P1)
    • EL 返回 {status: SYNCING}
    • EL 的头指向 Bn
    • EL 应该从网络中拉取 TB &lt;- P1
    • 轮询 forkchoiceUpdated(P1)
    • EL 返回 {status: VALID, latestValidHash: null}
    • EL 的头指向 P1

    </details>

  • [ ] 停止处理启用了 TBH 覆盖的同步 PoW 区块 <details> <summary>点击查看详情</summary>

    • PoW 客户端以 Genesis &lt;- ... &lt;- TB &lt;- B1 &lt;- B2 &lt;- ... &lt;- Bn 链开始,其中 TB 是一个有效的终端区块,也就是 TB.blockHash == TBH
    • EL 连接到 PoW 客户端并与广播的链同步
    • EL 的头指向 TB

    </details>

CL 客户端测试

在本节中,builder 节点意味着在其上运行所有验证者并因此构建链的节点。 Importer 是一个导入由 builder 构建并从 builder 接收的链的节点。

  • [ ] 在启用 TBH 覆盖的情况下,在有效链上转换 <details> <summary>点击查看详情</summary>

    • EL.A: Genesis &lt;- ... &lt;- TB, TB 是一个有效的终端区块。也就是 TB.blockHash == TBH
    • EL.B: Genesis &lt;- ... &lt;- Bn, B 链比 A 更重,除非激活 TBH 覆盖,否则将成为规范链
    • CL: Genesis &lt;- ... &lt;- Bellatrix
    • EL 以导入的 AB 开始
    • TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH 大于 BELLATRIX_FORK_EPOCH
    • CL 以 Genesis 开始并构建一条链直到 Bellatrix 并升级到 Bellatrix
    • 合并转换仅在达到 TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH 时开始
    • CL 驱动 EL 通过转换并最终确定它
    • 终端区块是 EL.A.TB,并且 PoS 链建立在该区块之上
    • eth_getBlockByNumber(latest) 返回头
    • eth_getBlockByNumber(safe) 返回最近被证明是合理的区块
    • eth_getBlockByNumber(finalized) 返回最近被最终确定的区块

    </details>

  • [ ] 在 TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH 之前转换 <details> <summary>点击查看详情</summary>

    • EL: Genesis &lt;- ... &lt;- Bn
    • 节点: builder, importer; importer 在两层都连接到 builder
    • Bn 是对 builder 和 importer 观察来说的有效的终端区块,也就是说 TBH == Bn.blockHash
    • Importer 侧的 TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH 比 builder 侧的要大得多
    • EL 以 Bn 作为头开始
    • builder 以 Genesis 开始并构建一条链直到 Bellatrix 甚至更远
    • Importer 从不导入转换区块并且它的头总是指向转换前的区块

    </details>

  • [ ] 转换与不匹配的 TERMINAL_BLOCK_HASH <details> <summary>点击查看详情</summary>

    • EL: Genesis &lt;- ... &lt;- Bn
    • 节点: builder, importer; importer 在两层都连接到 builder
    • Bn 是对 builder 的观察来说的有效的终端区块,也就是说 builder 的 TBH == Bn.blockHash
    • Bn 是对 importer 的观察来说的 无效的 终端区块,也就是说 builder 的 TBH != Bn.blockHash
    • TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH 在 builder 和 importer 侧是相同的
    • EL 以 Bn 作为头开始
    • builder 以 Genesis 开始并构建一条链直到 Bellatrix 甚至更远
    • Importer 从不导入转换区块并且它的头总是指向转换前的区块

    </details>

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

0 条评论

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