以太坊 Engine API:可视化执行层和共识层之间通信流程

本文深入探讨了以太坊节点执行层和共识层之间通信的关键接口——Engine API。文章详细解释了Engine API的主要流程,包括节点启动、区块构建和区块验证,并分析了每个流程中可能出现的错误情况,以及相应的处理方式。此外,还讨论了浅状态客户端的特殊情况以及验证器节点的生命周期。

Engine API 是一个接口,允许以太坊节点的执行层和共识层之间进行通信。自从合并以来,这个 API 已经成为以太坊架构中至关重要的一部分。它协调重要的任务,例如区块验证和区块提议。

尽管它很重要,但在学习 Engine API 时,除了 Engine API 规范 之外,很难找到其他资源。

仅仅通过阅读规范来理解 API 是一项具有挑战性的任务。因此,在本文中,我将解释 Engine API 中最重要的流程。我将指出这些流程可能失败的地方,规范中如何规定它们,以及它们如何组合在一起。

本文 是我的 EPF 项目 的一部分。

考虑事项

  • 这是基于 上海规范 的。
  • 验证器软件被视为共识层客户端的一部分。
  • 这里的每个流程都假定验证器在本地构建区块。
  • 如果你只想要渲染后的图表,可以在 这里 找到它们。

接口函数汇总

以下是我们将在整个过程中遇到的函数的简要说明:

  • engine_exchangeCapabilities:用于交换每个客户端支持的 Engine API 方法。
  • engine_forkchoiceUpdatedV2:更新执行层客户端的 fork choice。也用于启动 payload 构建过程。
  • engine_getPayloadV2:用于从过去的 engine_forkchoiceUpdatedV2 调用中启动的构建过程中检索 execution_payload
  • engine_newPayloadV2:共识层客户端使用它来将 execution_payload 发送到执行层客户端以进行验证。

节点启动

当你运行命令来启动信标节点时,会调用 两个 Engine API 端点engine_exchangeCapabilitiesengine_forkchoiceUpdatedV2

理想情况下,执行层客户端 (EL) 和 共识层客户端 (CL) 之间的通信如下:

sequenceDiagram
Title: Node Startup, Success
CL -> EL: engine_exchangeCapabilities(engine methods supported by CL)
EL --> CL: engine methods supported by EL
CL -> EL: engine_forkchoiceUpdatedV2(ForkchoiceState, null)
EL --> CL: {payloadStatus: {status: VALID, ...}, payloadId: null}

请注意 engine_forkchoiceUpdatedV2 的第二个参数是如何为空的。这告诉 EL 不要启动 payload 构建过程。

EL 正在同步

无法保证两个客户端会同时完成同步。因此,当 EL 仍在同步时,CL 可能会调用一个端点,这会导致以下情况:

sequenceDiagram
Title: Node Startup, EL is Syncing
CL -> EL: engine_exchangeCapabilities(engine methods supported by CL)
EL --> CL: engine methods supported by EL
CL -> EL: engine_forkchoiceUpdatedV2(ForkchoiceState, null)
Note over EL: I don't have all the necessary data available
EL --> CL: {payloadStatus: {status: SYNCING, ...}, payloadId: null}

区块构建

当选择一个验证器来提议一个区块时,两个客户端之间的理想通信如下:

sequenceDiagram
Title: Block Building, Success
Note over CL: Validator is assigned to propose
Note over CL: Fill BeaconBlock up to  execution_payload
CL -> EL: engine_forkchoiceUpdatedV2(ForkchoiceState, PayloadAttributes)
EL --> CL: {payloadStatus: {status: VALID, ...}, payloadId: buildProcessId}
Note over EL: Build execution_payload
CL -> EL: engine_getPayloadV2(PayloadId)
EL --> CL: {executionPayload, blockValue}
Note over CL: Put the ExecutionPayload on the BeaconBlock
Note over CL: Compute state_root
Note over CL: Propagate block

请注意,这次在调用 engine_forkChoiceUpdatedV2 时,将 PayloadAttribues 作为第二个参数传递。然后,payloadId 是响应的一部分,并且 payload 构建过程开始。

让我们看看这个过程如何失败。

engine_forkchoiceUpdatedV2 失败

forkchoiceState.headBlockHash 引用的 payload 无效

forkchoiceState 结构体有一个名为 headBlockHash 的字段。在更新其 fork choice 之前,EL 必须检查 headBlockHash 引用的 payload 是否有效。 定义 payload 有效性的过程在 Engine API Paris 规范 这里 中定义。

如果此过程得出结论,引用的 payload 无效,则会发生以下情况:

sequenceDiagram
Title: Block building fails, INVALID payload
Note over CL: Validator is assigned to propose
Note over CL: Fill BeaconBlock up to execution_payload
CL -> EL: engine_forkchoiceUpdatedV2(ForkchoiceState, PayloadAttributes)
Note over EL: Start payload validation process
Note over EL: Payload is invalid
EL --> CL: {payloadStatus: {status: INVALID, ...}, payloadId: null}

无效的 PayloadAttributes

ForkChoiceState 需要满足某些条件才能被认为是有效的一样,PayloadAttributes 必须满足一些要求才能有效。

来自 engine_forkchoiceUpdatedV1 规范 的第 7 点:

客户端软件必须确保 payloadAttributes.timestamp 大于 forkchoiceState.headBlockHash 引用的区块的时间戳。如果未满足此条件,客户端软件必须以 -38003: Invalid payload attributes 响应,并且不得开始 payload 构建过程。在这种情况下,forkchoiceState 更新不得回滚。

sequenceDiagram
Title: Block Building Fails, Invalid PayloadAttributes
Note over CL: Validator is assigned to propose
Note over CL: Fill BeaconBlock up to execution_payload
CL -> EL: engine_forkchoiceUpdatedV2(ForkchoiceState, PayloadAttributes)
Note over EL: PayloadAttributes is not valid
EL --> CL: -38003: Invalid payload attributes

错误的 PayloadAttributes 版本

除了有效之外,PayloadAttributes 需要是正确的版本。

来自 engine_forkchoiceUpdatedV2 规范:

  • PayloadAttributesV1 必须用于构建时间戳值低于上海时间戳的 payload,
  • PayloadAttributesV2 必须用于构建时间戳值大于或等于上海时间戳的 payload,
  • 如果在方法调用中使用了错误的结构版本,客户端软件必须返回 -32602: Invalid params 错误。

如果此检查失败,则会发生以下情况:

sequenceDiagram
Title: Block Building Fails, Wrong PayloadAttributes Version
Note over CL: Validator is assigned to propose
Note over CL: Fill BeaconBlock up to  execution_payload
CL -> EL: engine_forkchoiceUpdatedV2(ForkchoiceState, PayloadAttributes)
Note over EL: Wrong PayloadAttributes version
EL --> CL: -32602: Invalid params

ForkChoiceState 无效

作为 FCU 调用参数传递的 ForkChoiceState 需要满足某些条件才能被认为是有效的。 这些条件在 engine_forkchoiceupdatedV1 规范的第 6 点中说明:

如果 forkchoiceState.headBlockHash 引用的 payload 是 VALID,并且 forkchoiceState.finalizedBlockHashforkchoiceState.safeBlockHash 引用的 payload 不属于 forkchoiceState.headBlockHash 定义的链,则客户端软件必须返回 -38002: Invalid forkchoice 状态错误。

sequenceDiagram
Title: Block Building Fails, Invalid ForkChoiceState
Note over CL: Validator is assigned to propose
Note over CL: Fill BeaconBlock up to execution_payload
CL -> EL: engine_forkchoiceUpdatedV2(ForkchoiceState, PayloadAttributes)
Note over EL: ForkChoiceState is not valid
EL --> CL: -38002: Invalid forkchoice state

EL 正在同步

节点启动 中相同。

sequenceDiagram
Title: Block Building Fails, EL is Syncing
Note over CL: Validator is assigned to propose
Note over CL: Fill BeaconBlock up to execution_payload
CL -> EL: engine_forkchoiceUpdatedV2(ForkchoiceState, PayloadAttributes)
Note over EL: I don't have all the necessary data available
EL --> CL: {payloadStatus: {status: SYNCING, ...}, payloadId: null}

getPayloadV2 失败

在成功的 FCU 调用之后,EL 开始构建 payload 并将 PayloadId 返回给 CL。然后,CL 可以使用此 PayloadId 来请求 payload。 当然,如果 CL 调用尝试使用错误的 ID 检索 payload,则 EL 会返回错误。

sequenceDiagram
Title: Block Building Fails, Unknown Payload
Note over CL: Validator is assigned to propose
Note over CL: Fill BeaconBlock up to execution_payload
CL -> EL: engine_forkchoiceUpdatedV2(ForkchoiceState, PayloadAttributes)
EL --> CL: {payloadStatus: {status: VALID, ...}, payloadId: buildProcessId}
Note over EL: Build \n execution_payload
CL -> EL: engine_getPayloadV2(PayloadId)
Note over EL: There is no build \n process with that ID
EL --> CL: -38001: Unknown payload

区块验证

在本节中,对 engine_forkChoiceUpdatedV2 的调用不像其他流程中那样明确。请记住,节点应在每个 slot 开始时运行 FCU。因此,此处的任何流程都假定 EL 在最新的 fork choice 上运行。

如果一切顺利,节点会确定一个区块是 有效、无效或已接受

newPayloadV2.png

我制作了一个 流程图 , 如上图,解释了 EL 如何处理 engine_newPayloadV2

VALID

正如 engine_newPayloadV1 规范所说(这部分对于 newPayloadV2 保持不变):

客户端软件必须以下列方式响应此方法调用:

  • 如果在处理调用时已完全验证了 payload,则使用从 payload 验证过程中获得的 payload 状态

因此,对于 newPayloadV2 返回 VALID,被验证的 payload 必须在到达 验证过程 之前通过所有检查, 然后在验证过程中必须被认为是有效的。

sequenceDiagram
Title: Block Validation, VALID
Note over CL: Receive new beacon block
Note over CL: Extract ExecutionPayload \n from block
CL -> EL: engine_newPayloadV2(ExecutionPayload)
Note over EL: All requirements are \n met and the payload is \n considered valid
EL --> CL: {status: VALID, ...}

INVALID

对于 engine_newPayloadV2 返回 invalid,可能会发生两件事:

  • blockHash 无效,如 newPayloadV1 规范的 (1) 中定义的那样。(在 newPayloadV2 的规范中,声明 INVALID_BLOCK_HASH 状态值被 INVALID 取代。)
  • 验证过程 得出结论,payload 无效。
sequenceDiagram
title: Block Validation, INVALID
Note over CL: Receive new beacon \n block
Note over CL: Extract \n ExecutionPayload \n from block
CL -> EL: engine_newPayloadV2(ExecutionPayload)
Note over EL: BlockHash is invalid \n OR \n validation process concludes \n payload is invalid
EL --> CL: {status: INVALID, ...}

SYNCING

节点启动区块构建 中相同。

sequenceDiagram
Title: Block Validation Failed, Syncing EL
Note over CL: Receive new beacon \n block
Note over CL: Extract \n ExecutionPayload \n from block
CL -> EL: engine_newPayloadV2(ExecutionPayload)
Note over EL: I don't have all the \n necessary data available
EL --> CL: {status: SYNCING, ...}

Shallow State 客户端

这种类型的客户端仅保存一个版本的世界状态:规范链头部之后的 post-state 。 Erigon 是这种客户端的一个例子。

假设其中一个客户端正在运行链 A 并在 FCU 调用中获取链 B。 他们只会知道链 A 的状态,并且必须验证链 B 上的所有区块,然后才能完全验证。

在这种情况下,响应将为 VALID 或 INVALID。

sequenceDiagram
Title: Block Validation, Shallow State Client
Participant CL
Participant EL (shallow)
Note over EL (shallow): Last FCU nominated \n a non-canonical chain \n (chain B)
Note over CL, EL (shallow): Validate all blocks on Chain B
Note over CL: Receive new beacon \n block
Note over CL: Extract \n ExecutionPayload \n from block
CL -> EL (shallow): engine_newPayloadV2(ExecutionPayload)
EL (shallow) --> CL: {status: VALID, ...} OR {status: INVALID, ...}

ACCEPTED

这是 shallow-state 客户端收到非规范区块的情况。

在前面的示例中,这将意味着当它在运行链 A 时,它会获得链 B 的区块(在 FCU 调用之前)。

sequenceDiagram
Title: Block Validation, ACCEPTED
Participant CL
Participant EL (shallow)
Note over CL: Receive new beacon  block
Note over CL: Extract ExecutionPayload from block
CL -> EL (shallow): engine_newPayloadV2(ExecutionPayload)
Note over EL (shallow): Payload is \n non-canonical
EL (shallow) --> CL: {status: ACCEPTED, ...}

验证失败

在这种情况下,此过程可能会失败。

错误的 ExecutionPayload 版本

非常类似于在 区块构建 中使用错误的 PayloadAttributes 版本引起的错误。 我们尝试验证的 ExecutionPayload 的版本必须与上海时间戳一致。

  • 如果时间戳值低于上海时间戳,则必须使用 ExecutionPayloadV1,
  • 如果时间戳值大于或等于上海时间戳,则必须使用 ExecutionPayloadV2,
  • 如果在方法调用中使用了错误的结构版本,客户端软件必须返回 -32602: Invalid params 错误。
sequenceDiagram
Title: Block Validation Failed: Wrong ExecutionPayload Version
Note over CL: Receive new beacon block
Note over CL: Extract  ExecutionPayload  from block
CL -> EL: engine_newPayloadV2(ExecutionPayload)
Note over EL: Wrong \n ExecutionPayload version
EL --> CL: -32602: Invalid params

整合

现在,这是一个图表,显示了验证器节点的完整生命周期。 这里需要注意的是相对于 slot 开始或结束的调用的时机。

即使未选择验证器进行提议,对带有 PayloadParamsengine_forkchoiceUpdatedV2 的调用也可能看起来很奇怪。 这是验证器可以进行的一种优化,可以有更多的时间来构建区块(因此,构建一个更有利可图的区块)。

sequenceDiagram
    title Validator lifetime
    Note over CL, EL: Node is started
    CL->>EL: engine_exchangeCapabilities(engine methods supported by CL)
    EL-->>CL: engine methods supported by EL
    CL->>EL: engine_forkchoiceUpdatedV2(ForkchoiceState, null)
    Note right of EL: I don't have all the<br/>necessary data available,<br/>syncing
    EL-->>CL: {payloadStatus: {status: SYNCING, ...}, payloadId: null}
    Note over CL, EL: ...
    Note right of EL: Sync is completed
    CL->>EL: engine_forkchoiceUpdatedV2(ForkchoiceState, null)
    EL-->>CL: {payloadStatus: {status: VALID, ...}, payloadId: null}
    Note over CL, EL: Slot where validator doesn't have to propose starts
    CL->>EL: engine_forkchoiceUpdatedV2(ForkchoiceState, null)
    EL-->>CL: {payloadStatus: {status: VALID, ...}, payloadId: null}
    Note over CL: Receive new<br/>beacon block
    Note over CL: Extract<br/>ExecutionPayload<br/>from block
    CL->>EL: engine_newPayloadV2(ExecutionPayload)
    Note right of EL: All requirements<br/>are met and the<br/>payload is considered<br/>valid
    EL-->>CL: {status: VALID, ...}
    CL->>EL: engine_forkchoiceUpdatedV2(ForkchoiceState, PayloadAttributes)
    EL-->>CL: {payloadStatus: {status: VALID, ...}, payloadId: buildProcessId}
    Note right of EL: Start building<br/>execution_payload
    Note over CL, EL: Slot where validator doesn't have to propose ends
    Note over CL, EL: Slot where validator has to propose starts
    CL->>EL: engine_forkchoiceUpdatedV2(ForkchoiceState, null)
    EL-->>CL: {payloadStatus: {status: VALID, ...}, payloadId: null}
    Note over CL: Fill BeaconBlock<br/>up to<br/>execution_payload
    CL->>EL: engine_getPayloadV2(PayloadId)
    EL-->>CL: {executionPayload, blockValue}
    Note over CL: Put the<br/>ExecutionPayload<br/>on the BeaconBlock
    Note over CL: Compute state_root
    Note over CL: Propagate block
    Note over CL, EL: Slot where validator has to propose ends
    Note over CL, EL: More slots of both types would happen again until the node is shutdown
  • 原文链接: hackmd.io/@danielrachi/e...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

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