查看原文
其他

以太坊协议共学|第二周内容回顾:Execution Layer

Chloe PlanckerDAO 2024-03-11

点击蓝字,关注我们

/ 内容整理(英文版):Chloe

/ 翻译&排版:Purple

/ 其他贡献者:Leo




0. 目录




1. 前言 

  • 演讲嘉宾 - @lightclients(@go_ethereum)

    • Ethereum 核心开发者,Geth 团队成员,还参与测试和 EIP 编辑等

  • 学习建议 - Leo

    虽然课程是讲执行层,但是一上来就进入到了具体的环节,包括交易发送,交易验证,组装块,EVM 执行这些。执行层和工时层分离是转 pos 发生的。所以会跟 pow 时期的一些文章有冲突。他现在讲的就是最新的架构,老的架构是和共识层在一起的。

    我推荐课前阅读: https://geth.ethereum.org/


2. 纪要

2.1 区块验证和区块构建概述

2.1.1 区块验证

  • 共识层(CL):从共识规范中,我们可以看到 CL 对 EL(执行层) 的看法

    • 它由信标链(beacon chain )执行,以验证块是否有效并推动 CL;
    • CL 将执行一些检查(包括父哈希、之前的 randao、时间戳、每个块的最大 blob 等),然后将有有效数据(payload)发送到 EL 进行进一步验证;
    • CL 和 EL 之间的通信是通过 execution_engine 进 行的;
    • 规范链接:https://github.com/ethereum/consensus-specs/blob/dev/specs/deneb/beacon-chain.md#modified-process_execution_payload


  • 函数 notify_new_payload

    • CL 并不执行,因为它只是将有效数据的执行发送到 execution_engine,然后执行客户端将执行状态转换功能。
    • 规范链接:https://github.com/ethereum/consensus-specs/blob/dev/specs/deneb/beacon-chain.md#modified-notify_new_payload


  • 执行层 (EL): 

用 Go 编写的简单插图
    • 状态转换函数(STF)
      • 所需参数

        • 父块(需要验证从父块到当前块的转换逻辑)
        • 当前区块
        • StateDB(最后一个已知的有效状态,存储与父块相关的所有状态数据)
      • 返回结果:
        • 更新了状态数据库(包括当前块的信息)
        • 错误(如果函数失败并且不更新状态数据库)
      • 第 1 步:验证头部信息
        • 如果出现以下情况,可能会发生错误
          • Gas limit 变化超过前一个区块的 1/1024
          • 区块的序号不连续
          • EIP-1559 基本费用未正确更新
          • ……
      • 第 2 步:如果头部信息正确,则开始交易
        • 遍历区块中的所有交易,通过虚拟机执行每笔交易,并且如果交易正确无误则更新状态
        • 错误可能发生在
          • 如果存在无效交易,则整个区块无效,状态不会被更新

  • 封装函数,比如 newPayload
    • 所需参数:
      • 执行有效数据
    • 返回结果:
      • 返回 bool 到信标链
      • 然后信标链会调用 STF

  • Q&A
    • 为什么将 block.header() 放入 vm.Run 中?
      • 执行交易时需要两部分上下文信息:
        • 状态信息:例如,合约代码、账户内的存储等;
        • 区块上下文:例如,父哈希、之前的 randao、基础费用等。
    • STF 被 CL 调用,并返回其是否有效。如果它不有效,CL 会发生什么?
      • 该区块将会被拒绝。


2.1.2 区块构建

用 Go 编写的简单插图
  • 构建函数
    • 需要的参数
      • 环境:包括时间戳、区块编号、前一个区块、基础费用等
      • 交易池:维护按其价值排序的交易列表
      • 状态数据库(StateDB)
    • 返回结果
      • 区块
      • 更新后的状态数据库
      • 错误
    • 第一步:跟踪使用的 Gas 并存储进区块的交易
      • 交易可以持续被加入区块,直到达到 Gas 使用限制,目前主网的 Gas 限制大约为 3000 万
    • 第二步:从交易池中获取下一个最佳交易并执行它
      • 使用 Pop() 来获取下一个最佳交易,通过虚拟机(VM)执行它,并追加所有执行的交易;
      • 如果因交易无效而出现错误,过程将继续,直到没有 Gas 或交易池为空为止
    • 第三步:使用 Finalize 函数返回结果
      • Finalize 函数接收交易和有关区块的信息,并生成一个完全组装好的区块


  • Q&A
    • 交易池是否以某种方式排序?如果没有,我们如何在使用 pool.Pop 时确保最大利润?
      • 按照支付给构建者的最高交易费用排序
      • 每次调用 Pop() 时,你将获得按 Gas 价值计算的,给你带来最多价值的交易
    • 在构建区块时,执行层(EL)是否会在发送给共识层(CL)之前拒绝任何交易?
      • 唯一拒绝交易的时候是当它无效时,一般来说,交易池会验证交易是否有效,所以这种情况不会太频繁发生
    • 加密的交易池:1. 可行性有多少?2. 由于区块交易按 Gas 价格排序,那么在这种设计下 Gas 是否未加密?
      • 这是一个有难度的问题,有许多关于如何实现它的想法,有些可能会有未加密的 Gas,有些甚至可能有未加密的发送者信息,但这都会泄露某种信息;
      • 从以太坊的角度看,当找到一种有效的加密交易池的方法时,这个问题可能会在将来得到解决
    • 这里有没有任意删除情形需要担心?例如,交易池中的交易被包含在区块中,然后在你构建另一个区块之前被删除
      • 交易池应该进行交易验证,所以通常这里的交易是有效的,但是池子并不总是同步的,可能会导致某些交易无效,删除情形可能会发生。


2.2 进一步深入 STF、EVM 和 P2P 协议

2.2.1 状态转换函数

查看 geth 代码: https://github.com/ethereum/go-ethereum

  • newPayload 函数
    • 它由共识层(CL)调用,执行层(EL)将对区块信息进行一系列的完整性检查;
    • 这一过程一直延伸到 insertBlockWithoutSetHead 函数,事实上在那里我们开始将区块加入到我们的链中
    • 链接:https://github.com/ethereum/go-ethereum/blob/master/eth/catalyst/api.go


  • insertBlockWithoutSetHead 函数
    • 这个函数执行区块,运行验证,并然后将区块和交易状态持久化到数据库中。与 InsertChain 函数的主要区别在于它不会执行规范链更新。它仍然依赖额外的 SetCanonical 调用来完成整个过程。
    • 链接:https://github.com/ethereum/go-ethereum/blob/master/core/blockchain.go


  • insertChain 函数
    • 函数 verifyHeaders:检查一个区块头是否符合共识规则
      • 它将验证几项内容,例如区块头的 EIP-1559 属性(以确保 Gas 限制在允许的范围内)、Gas 限制、已使用的 Gas、时间戳等,确保所有字段都是正确的;
      • 一旦头部信息被验证,那么区块就可以被执行;
      • 链接:https://github.com/ethereum/go-ethereum/blob/master/consensus/beacon/consensus.go

  • Process 函数
    • 需要的参数包括区块、状态数据库(stateDB)、虚拟机配置(vm config)
    • Geth 中的状态转换是通过 state_processor 进行的
      • 流程与上述的状态转换函数(STF)概览类似,但有更多细节
    • 链接:https://github.com/ethereum/go-ethereum/blob/master/core/types.go
    • 链接:https://github.com/ethereum/go-ethereum/blob/master/core/state_processor.go
    • 一旦处理过程完成,区块链将更新更多指标,并最终将区块写入状态


  • Q&A
    • 什么是收据(Receipt)?
      • 收据是有关交易的信息,只能在执行交易后才能验证或确定。
      • 链接:https://github.com/ethereum/go-ethereum/blob/master/core/types/receipt.go
    • 关于多笔交易产生多笔其他交易的环境问题:你使用的上下文环境是什么?它是如何获取的?
      • EVM 环境:
        • 交易级别的上下文:可能在区块内变化,例如, Gas 价格、blob 等;
        • 区块级别的上下文:在整个区块中固定,例如,区块编号、基础费用、时间难度等。
        • 链接:https://github.com/ethereum/go-ethereum/blob/master/core/state_processor.go
      • EVM 编译器:
        • 作用域上下文(ScopeContext):在交易中变化,例如,memory、stack、contract
        • 链接:https://github.com/ethereum/go-ethereum/blob/master/core/vm/interpreter.go


2.2.2 EVM

  • EVM 结构
    • 想象该区域是 EVM 调用框架,它在整个交易过程中发生变化。在 EVM 调用框架内,有:
      • Code(代码);
      • PC(程序计数器):如果程序计数器为 0,编译器将加载代码中索引为 0 的指令,然后执行它,它将更新1 个字节;
      • Stack;
      • Memory;
      • 剩余 Gas;
      • Block context & Tx context;
      • State(状态)。
    • 栈机模拟示例:https://www.evm.codes/playground


  • EVM 内不同类型的指令
    • 算术指令:例如,加法、减法、乘方等
    • 位运算指令:例如,Bigbang 等
    • 环境指令:提供对区块上下文和交易上下文的访问
    • 控制流指令:用于更复杂的程序,例如,分支(EIP 4788)
      • 链接:https://github.com/lightclient/4788asm
    • 栈操作指令:例如,push、pop、swap 等
    • 系统指令:例如,call、create、return、sstorage 等
    • 内存指令:例如,mload、mstore、mstore8 等


  • Q&A
    • 各种指令的成本是如何确定的?它们是为了减少网络负担的激励措施吗?
      • 有一个每秒 Gas 目标,这是确定成本的基准。今天,许多指令的 Gas 成本就是以其他类似类型指令的 Gas 成本为基准。


2.2.3  P2P 协议

执行层(EL)运行在 devp2p 上,这是以太坊的专有协议。

  • devp2p 协议命名的有趣过去:
    • 现在我们处于 eth/68,正在向 eth/69 进发。早期,协议从 eth/1 快速迭代到 eth/6,然后变为 eth/60、/61、/62,现在是 /68。逻辑上很有趣😂😂
    • 链接:https://github.com/ethereum/devp2p


  • P2P 协议的责任:访问历史数据、待处理交易和状态
    • 链接:https://github.com/ethereum/devp2p/blob/master/caps/eth.md
    • 历史数据:从以太坊获取历史数据的三种方法
      • GetBlockHeaders:要求对等端返回一个 blockheaders 消息;
      • GetBlockBodies:通过哈希请求区块数据;
      • GetReceipts:要求对等端返回包含给定区块哈希的收据消息。
    • 待处理交易(尚未打包入区块)
      • Transactions:指定对等端应确保其交易队列中包含的交易
      • NewPooledTransactionHashes:向对等端发送交易类型、大小、哈希列表;如果对等端之前未见过这些交易哈希,则它将调用 GetPooledTransactions 函数
        • 目标是通过仅向对等端的平方根发送完整交易,而不是每个对等端,来减少执行客户端的带宽
      • GetPooledTransactions:池子将回应完整的交易值
    • 状态
      • 链接:https://github.com/ethereum/devp2p/blob/master/caps/snap.md
      • Snap sync可以被视为一个两阶段协议,第一阶段是连续状态检索,第二阶段是为了同步状态树的修复阶段。

  • Q&A
    • 你如何知道你没有从错误的链下载错误的数据?
      • 过程将是:
        • 从弱主观性检查点开始,它将给出一个根;
        • 获取与哈希关联的区块;
        • 针对该区块状态开始 snap 同步。
      • 你正在与之 Snap sync 的状态根是由你认证的,所以你假设它是正确的并下载状态。你拿回的数据将是针对根的见证。
      • 唯一可能发生的事情是状态计算错误,你下载了链已默认的损坏状态。这种情况发生的可能性很小,因为很多投资这个链的人都在认真检查,确保一切正常。




Week2 英文版回顾材料:https://epf.wiki/#/eps/week2

欢迎关注 PlanckerDAO,关注 EIP Fun 官方推特(@EIPFun),并加入我们和 OpenBuild 联合发起的 EPS 共学群(扫描并添加小助手二维码进入),获取最及时的以太坊协议共学信息和回顾,快来一起学习吧!

推荐阅读

以太坊协议共学|第一周内容回顾:以太坊协议 101

AAStar 技术分享|漫谈 Layer123

EIP Fun 周刊 #36 | EIP-6110

ZK-Score:ZK 硬件排名标准

ZKP 底层 | # MSM - Barrett Reduction 公式推导

Plancker 之以太坊基金支持|揭秘哪些项目更受青睐?

/ About  Plancker


PlanckerDAO 是一个专注建设以太坊生态的社区,我们为开发者、产品经理和研究员提供多方面支持,致力于与以太坊共建人类的数字化美好未来。

Website:https://plancker.org/

Forum:http://forum.plancker.org/

Telegram:https://t.me/PlanckerDAO

Notion:https://planckerdao.notion.site/

Twitter:https://twitter.com/PlanckerDAO

继续滑动看下一个

以太坊协议共学|第二周内容回顾:Execution Layer

Chloe PlanckerDAO
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存