以太坊协议共学|第二周内容回顾:Execution Layer
点击蓝字,关注我们
/ 内容整理(英文版):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):
状态转换函数(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 区块构建
构建函数 需要的参数 环境:包括时间戳、区块编号、前一个区块、基础费用等 交易池:维护按其价值排序的交易列表 状态数据库(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 共学群(扫描并添加小助手二维码进入),获取最及时的以太坊协议共学信息和回顾,快来一起学习吧!
推荐阅读
/ 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