引擎API测试
本文档描述了 Engine API 的测试用例,这些测试用例都是从合并后的世界开始的,即 Genesis 是一个终端PoW块。
Engine API 测试
本文档中描述的所有测试用例都始于合并后的世界,即 Genesis 是一个最终的 PoW 区块。
<!-- START doctoc generated TOC please keep comment here to allow auto update --> <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
EL 客户端测试
-
[Hive] 无效的
block_hash<details> <summary>点击查看详情</summary>- [Hive] 测试应涵盖当 EL 处于
SYNCING状态以及不在SYNCING状态时block_hash的验证,以确保同步不会影响验证 - 测试应涵盖
block_hash所有可能的不一致情况,这些情况很容易发生,例如随机哈希、如果区块是有效的 PoW 区块时的哈希等 - [Hive] EL 响应
{status: INVALID_BLOCK_HASH, latestValidHash: null, validationError: errorMessage | null}
</details>
- [Hive] 测试应涵盖当 EL 处于
-
[Hive]
VALID规范 链 payload <details> <summary>点击查看详情</summary>P是扩展规范链的VALIDpayloadnewPayload(P)forkchoiceUpdated(headBlock: P)
</details>
-
[Hive PR] 不一致的
ForkchoiceState<details> <summary>点击查看详情</summary>A: Genesis <- P1 <- P2 <- P3,B: Genesis <- P1' <- P2' <- P3'- EL 客户端以完全导入的
A和B启动 forkchoiceUpdated(finalized: A.P1, safe: A.P2, head: A.P3)- EL 成功地重新组织为
A.P3,finalized和safe区块符合预期
- EL 成功地重新组织为
forkchoiceUpdated(finalized: A.P1, safe: A.P2, head: B.P3')forkchoiceUpdated(finalized: A.P1, safe: B.P2', head: A.P3)forkchoiceUpdated(finalized: B.P1', safe: A.P2, head: A.P3)- 在上面列出的所有情况下,都返回
{error: {code: -38002, message: "Invalid forkchoice state"}}
- 在上面列出的所有情况下,都返回
forkchoiceUpdated(finalized: B.P1', safe: B.P2', head: B.P3')- EL 成功地重新组织为
B.P3,finalized和safe区块符合预期
- EL 成功地重新组织为
</details>
-
[Hive]
INVALID规范 链 payload <details> <summary>点击查看详情</summary>INV_P是扩展规范链的INVALIDpayloadINV_P具有有效的block_hash,但由于以下无效属性而被无效:stateRoot无效receiptsRoot无效blockNumber小于或等于parent.blockNumber或大于parent.blockNumber+1gasLimit大于parent.gasLimit + parent.gasLimit / 1024或小于parent.gasLimit - parent.gasLimit / 1024gasUsed不等于包含的交易使用的 gastimestamp小于或等于parent.timestampbaseFeePerGas与parent.baseFeePerGas和parent.gasUsed不一致transactions具有以下任一情况:- 不完整的交易
- 额外的交易
- 本质上无效的交易
newPayload(INV_P){status: INVALID, latestValidHash: P.parentHash, validationError: errorMessage | null}- 无法通过
eth_getBlockByHash访问INV_P
</details>
-
[Hive] 具有未知父级的 Payload <details> <summary>点击查看详情</summary>
A: Genesis <- P1 <- P2 <- P3,B: Genesis <- P1' <- P2' <- P3'- EL 客户端以
Genesis区块和状态启动 forkchoiceUpdated(A.P1){status: SYNCING}
newPayload(A.P1) + forkchoiceUpdated(A.P1)- 轮询
forkchoiceUpdated(A.P1),直到它响应{status: VALID},head 设置为A.P1
- 轮询
newPayload(B.P2'){status: SYNCING}
newPayload(B.P1') + newPayload(B.P2') + forkchoiceUpdated(B.P2')- 轮询
forkchoiceUpdated(B.P2'),直到它响应{status: VALID},head 设置为B.P2'
- 轮询
forkchoiceUpdated(A.P1)- 重新组织回
A.P1
- 重新组织回
newPayload(A.P3){status: SYNCING}
newPayload(A.P2) + newPayload(A.P3) + forkchoiceUpdated(A.P3)- 轮询
forkchoiceUpdated(A.P3),直到它响应{status: VALID},head 设置为A.P3'
- 轮询
</details>
-
[Hive]
SYNCING具有无效链 <details> <summary>点击查看详情</summary>A: Genesis <- P1 <- P2 <- P3 <- P4,B: Genesis <- P1' <- INV_P2' <- P3' <- P4',INV_P2'是无效的 payloadINV_P2'具有有效的block_hash,但由于以下无效属性而被无效:stateRoot无效receiptsRoot无效blockNumber小于或等于parent.blockNumber或大于parent.blockNumber+1gasLimit大于parent.gasLimit + parent.gasLimit / 1024或小于parent.gasLimit - parent.gasLimit / 1024gasUsed不等于包含的交易使用的 gastimestamp小于或等于parent.timestampbaseFeePerGas与parent.baseFeePerGas和parent.gasUsed不一致transactions具有以下任一情况:- 不完整的交易
- 额外的交易
- 本质上无效的交易
- EL 客户端以
A: P4区块和状态启动 newPayload(INV_P2') + forkchoiceUpdated(head: INV_P2')- EL 响应
{status: SYNCING, latestValidHash: null, validationError: null}
- EL 响应
- EL 从网络上的远程对等点拉取
P1' newPayload(P3')- 轮询
newPayload(P3'),直到响应为INVALID,且latestValidHash: P1'.blockHash finalized、safe和 head 区块没有改变,即来自A链
- 轮询
newPayload(P2') + forkchoiceUpdated(head: P2')- EL 从网络上的远程对等点拉取
P1' - 轮询
forkchoiceUpdated(P2'),直到响应为INVALID,且latestValidHash: P1'.blockHash
- EL 从网络上的远程对等点拉取
</details>
-
[Hive] 构建一个 payload <details> <summary>点击查看详情</summary>
Genesis <- P1- EL 客户端以
Genesis区块和状态启动 newPayload(P1)- 成功
getPayload(payloaId: random){error: {code: -38001, message: "Unknown payload"}}
forkchoiceUpdated(P1, payloadAttributes: {validTimestamp, validPrevRandao, validFeeRecipient})- 记住从此调用返回的
existingPayloadId
- 记住从此调用返回的
getPayload(payloaId: random){error: {code: -38001, message: "Unknown payload"}}
getPayload(payloaId: existingPayloadId)- 记住从此调用返回的
returnedPayload
- 记住从此调用返回的
newPayload(returnedPayload){status: VALID}
forkchoiceUpdated(returnedPayload){status: VALID},returnedPayload变为 head
forkchoiceUpdated(returnedPayload, payloadAttributes: {validTimestamp, validPrevRandao, validFeeRecipient})- 记住从此调用返回的
existingPayloadId2
- 记住从此调用返回的
getPayload(payloaId: existingPayloadId2)- 记住从此调用返回的
returnedPayload2
- 记住从此调用返回的
newPayload(returnedPayload2){status: VALID}returnedPayload仍为 head
- 等待 60 秒
getPayload(payloaId: existingPayloadId2){error: {code: -38001, message: "Unknown payload"}}
</details>
-
[Hive] 无效的
PayloadAttributes<details> <summary>点击查看详情</summary>Genesis <- P1- EL 客户端以
Genesis区块和状态启动 newPayload(P1)- 成功
forkchoiceUpdated(P1, payloadAttributes: {timestamp: 0, validPrevRandao, validFeeRecipient}){error: {code: -38003, message: "Invalid payload attributes"}}- head 设置为
P1
</details>
-
[Hive]
VALID侧 链 payload <details> <summary>点击查看详情</summary>P'是扩展侧链的VALIDpayloadP和P'包含相同的交易,该交易使用PREVRANDAO来修改存储P和P'具有不同的prevRandao值newPayload(P')- 注意:EL 可能会响应
ACCEPTED或VALID
- 注意:EL 可能会响应
forkchoiceUpdated(headBlock: P')- EL 响应
{payloadStatus: {status: VALID, latestValidHash: forkchoiceState.headBlockHash, validationError: null}, payloadId: null} - EL 将 head 设置为
P' - 存储已使用
P'.prevRandao正确更新
- EL 响应
</details>
-
[Hive] 更新规范链上的 finalized 和 safe 区块 <details> <summary>点击查看详情</summary>
Genesis <- P1 <- P2 <- P3 <- P4是扩展规范链的有效 payload 的子链newPayload(P1) + forkchoiceUpdated(finalized: Genesis, safe: Genesis, head: P1)newPayload(P2) + forkchoiceUpdated(finalized: Genesis, safe: P1, head: P2)- EL 将
safe设置为P1,将 head 设置为P2,finalized == Genesis
- EL 将
newPayload(P3) + forkchoiceUpdated(finalized: P1, safe: P2, head: P3)- EL 将
finalized设置为P1,将safe设置为P2,将 head 设置为P3
- EL 将
newPayload(P4) + forkchoiceUpdated(finalized: P2, safe: P3, head: P4)- EL 将
finalized设置为P2,将safe设置为P3,将 head 设置为P4
- EL 将
</details>
-
[Hive] 更新侧链上的 safe 区块 <details> <summary>点击查看详情</summary>
A: Genesis <- P1 <- P2 <- P3是扩展规范链的有效 payload 的子链,B: Genesis <- P1 <- P2' <- P3'是扩展侧链的有效 payload 的子链- 导入
A并调用forkchoiceUpdated(finalized: P1, safe: P2, head: P3)- EL 将
finalized设置为P1,将safe设置为P2,将 head 设置为P3
- EL 将
- 通过调用
newPayload(P2') + newPayload(P3')导入B并调用forkchoiceUpdated(finalized: P1, safe: P2', head: P3')- 请注意,此测试可能需要
forkchoiceUpdated轮询,因为 EL 可能会响应正在同步 - EL 将
finalized设置为P1,将safe设置为P2',将 head 设置为P3'
- 请注意,此测试可能需要
</details>
-
[Hive]
SYNCING具有有效链 <details> <summary>点击查看详情</summary>Genesis <- P1 <- P2 <- P3 <- ... <- Pn- EL 客户端以
Genesis区块和状态启动 newPayload(Pn) + forkchoiceUpdated(head: Pn)- EL 响应
{status: SYNCING, latestValidHash: null, validationError: null}
- EL 响应
- EL 客户端应从远程对等点拉取
P1 <- P2 <- P3 <- ... <- Pn-1并成功完成同步过程 newPayload(Pn+1) + forkchoiceUpdated(head: Pn+1)- 使用新的 payload 轮询
newPayload + forkchoiceUpdated,直到响应为VALID finalized、safe和 head 区块已相应设置
- 使用新的 payload 轮询
</details>
-
[Hive PR] 在
SYNCING时重新组织回规范链 <details> <summary>点击查看详情</summary>A: Genesis <- P1 <- P2 <- P3 <- P4,B: Genesis <- P1' <- P2' <- P3' <- P4'- EL 客户端同步到
A.P3区块,即A.P3是 head newPayload(B.P4') + forkchoiceUpdated(head: B.P4')- EL 响应
{status: SYNCING, latestValidHash: null, validationError: null} - 请注意,
B链的其余部分应不可用,以使 EL 无法完成其同步过程
- EL 响应
newPayload(A.P4) + forkchoiceUpdated(A.P4)- 轮询
forkchoiceUpdated(finalized: P2, safe: P3, head: P4),直到响应为VALID finalized、safe和 head 区块已相应设置
- 轮询
</details>
-
[Hive] 重新组织为删除交易的侧链 <details> <summary>点击查看详情</summary>
A: Genesis <- P1,B: Genesis <- P1'P1和P1'是有效的 payloadP1包含交易Tx1,而P1'不包含任何交易newPayload(A.P1) + forkchoiceUpdated(head: A.P1)- EL 响应
{status: VALID, latestValidHash: A.P1, validationError: null}
- EL 响应
- 使用 JSON-RPC 请求
Tx1收据- 客户端返回
Tx1收据
- 客户端返回
newPayload(B.P1') + forkchoiceUpdated(head: B.P1')- EL 响应
{status: VALID, latestValidHash: B.P1', validationError: null}
- EL 响应
- 使用 JSON-RPC 请求
Tx1收据- 客户端返回错误且没有
Tx1收据
- 客户端返回错误且没有
</details>
-
[Hive] 具有现有规范链 payload 的
newPayload<details> <summary>点击查看详情</summary>Genesis <- P1 <- P2 <- P3 <- ... <- PnnewPayload(P1) + forkchoiceUpdated(head: P1)到newPayload(Pn) + forkchoiceUpdated(head: Pn)- EL head 设置为
Pn
- EL head 设置为
newPayload(P1)到newPayload(Pn)- EL 返回
VALID且没有错误
- EL 返回
newPayload(Pn+1) + forkchoiceUpdated(head: Pn+1)- 客户端继续构建规范链,没有问题
</details>
-
[Hive] 导入并重新组织到先前验证的 payload <details> <summary>点击查看详情</summary>
Genesis <- P1 <- P2 <- P3 <- P4- EL 以
head: P4, safe: P3, finalized: P2启动 newPayload(P3)- EL 返回
{status: VALID, latestValidHash: P3.blockHash}
- EL 返回
forkchoiceUpdated(head: P3, safe: P2, finalized: P1)- EL 返回
{status: VALID, latestValidHash: P3.blockHash}
- EL 返回
</details>
-
[Hive] 导入并重新组织到侧链上先前验证的 payload <details> <summary>点击查看详情</summary>
A: Genesis <- P1 <- P2 <- P3 <- P4,B: Genesis <- P1 <- P2 <- P3 <- P4- EL 以导入的
A和B链以及作为 head 的A.P4启动 newPayload(B.P3)- EL 返回
{status: VALID, latestValidHash: B.P3.blockHash}
- EL 返回
forkchoiceUpdated(head: B.P3, payloadAttributes: buildProcessAttributes)- EL 返回
payloadStatus: {status: VALID, latestValidHash: B.P3.blockHash}, payloadId: buildProcessId
- EL 返回
getPayload(payloadId: buildProcessId)- EL 返回
builtPayload
- EL 返回
newPayload(builtPayload)- EL 返回
{status: VALID, latestValidHash: builtPayload.blockHash}
- EL 返回
</details>
CL 客户端测试
-
[Hive]
QUANTITY字段值已正确编码 <details> <summary>点击查看详情</summary>- Payload
P1具有大于255的所有QUANTITY字段值 - CL 处理
BeaconBlock(P1)- 所有
P1QUANTITY字段都是大端序
- 所有
</details>
- Payload
-
[Hive]
INVALID规范 链 payload <details> <summary>点击查看详情</summary>INV_P是扩展规范链的INVALIDpayload- CL 导入
BeaconBlock(INV_P)- EL 模拟人为地返回
INVALID - 无法通过
GET /eth/v1/beacon/headers/{block_id}访问BeaconBlock(INV_P)
- EL 模拟人为地返回
</details>
-
[Hive] 无效的
Timestamp<details> <summary>点击查看详情</summary>INV_P是由 EL 生成的扩展规范链的INVALIDpayload,其中timestamp值超出当前 slot 时间范围- CL 拒绝
INV_P
</details>
-
[Hive] 无效的
PrevRandao<details> <summary>点击查看详情</summary>INV_P是由 EL 生成的扩展规范链的INVALIDpayload,其中prevRandao值不是给定 slot 的预期 randao 混合值- CL 拒绝
INV_P
</details>
-
[Hive] 无效的
block_hash<details> <summary>点击查看详情</summary>- Payload
P响应INVALID_BLOCK_HASH- EL 模拟人为地返回此状态
- CL 丢弃
BeaconBlock(P)
</details>
- Payload
-
[Hive]
SYNCING具有无效链 <details> <summary>点击查看详情</summary>Genesis <- P1 <- P2 <- P3 <- P4- CL 逐个导入
BeaconBlock(P1) ... BeaconBlock(P4)区块- EL 模拟应在
newPayload(P2)上响应SYNCING - EL 模拟应在
newPayload(P4)上响应status: INVALID, latestValidHash: P1.blockHash
- EL 模拟应在
- CL 的 head 必须是
BeaconBlock(P1) - EL 必须收到
forkchoiceUpdated(P1) finalized、safe和 head 区块符合预期
</details>
-
[Hive]
baseFee编码检查 <details> <summary>点击查看详情</summary>- EL 生成一个 payload
P1,其中baseFee字段大于或等于 256 - CL 接受、广播并验证具有正确 endianess 的 payload
BeaconBlock(P1)已并入链中
</details>
- EL 生成一个 payload
-
[Hive] 超时 <details> <summary>点击查看详情</summary>
P是扩展规范链的VALIDpayload- CL 导入
BeaconBlock(P)- EL 模拟人为地暂停
newPayload和forkchoiceUpdated,在响应之前暂停timeout - 1s秒 BeaconBlock(P)是 CL 链的 headP是 EL 链的 head
- EL 模拟人为地暂停
</details>
-
[Hive PR] 由于乐观同步而没有可行的 head <details> <summary>点击查看详情</summary>
- 配置
SLOTS_PER_EPOCH = 32
... <- P0 <- P1 <- P2 <- ... <- PnBeaconBlock(P0).slot % SLOTS_PER_EPOCH == 0BeaconBlock(P0).slot >= 128距离 Genesis 足够远
- CL builder1 和 CL importer 处理
BeaconBlock(P0), ..., BeaconBlock(P10)- EL 模拟返回
status: VALID
- EL 模拟返回
- CL builder1
BeaconBlock(P11), ..., BeaconBlock(P33):EL 模拟返回status: VALID
- CL importer
BeaconBlock(P10), ..., BeaconBlock(P32):EL 模拟返回status: SYNCINGBeaconBlock(P33)- 第一种变体
newPayload(P33):{status: INVALID, latestValidHash: P10.blockHash}
- 第二种变体
newPayload(P33):{status: SYNCING}forkChoiceUpdated(P33):{status: INVALID, latestValidHash: P10.blockHash}
- 第一种变体
- 检查节点是否保持乐观,即 Beacon API 响应中的
execution_optimistic标志是否为true
- CL builder1 关闭
- CL builder2
- 启动,与 CL importer 对等并同步到
BeaconBlock(P10) BeaconBlock(P11, slot34), ..., BeaconBlock(Pn, slotN):EL 模拟返回status: VALID
- 启动,与 CL importer 对等并同步到
- CL importer
BeaconBlock(P11, slot34), ..., BeaconBlock(Pn, slotN):EL 模拟返回status: VALIDslotN应该足够高,以便 importer 可以从乐观状态恢复,N >= 96- 检查节点是否不再乐观,并且
BeaconBlock(Pn, slotN)是 head
</details>
- 配置
- 原文链接: github.com/txrx-research...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~