You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
笔者最近在结合以太坊黄皮书读以太坊源码,结合自己的理解解析下黄皮书内的公式,与大家共同学习进步,若大家在阅读以太坊黄皮书时,对公式产生理解上的困惑,可以参阅本文一起看。文章基于当前(2022/1/10)的黄皮书,版本号为 BERLIN VERSION fabef25 ,若有不准确之处,欢迎指出。由于该黄皮书内除附录外有 183 个公式,为了让文章篇幅不过长,该解析会由三部分组成一个系列,每个系列解析约 60 个公式,本文为中篇。
公式 (79) 定义了创建合约时的参数 salt 是可选的,若提供 slat,需要是一个长度为 32 的字节。
Λ 为创建合约函数。
σ, A, s, o, g, p, v, i, e, ζ, w 为创建合约函数的参数,分别是:状态,子状态,发送者,原始发送者(考虑通过合约创建的情况),GasLimit,GasPrice,EVM 初始代码化代码,创建合约的调用所处的当前栈深度,salt 和 对状态进行修改的许可。
σ', g', A', z, o 为函数返回值,分别是:新的状态,剩余 Gas ,新的子状态,状态码,输出(output)内容。
公式 (80) 给出了创建合约函数的定义(输入,输出)。
\
s 为交易发起者(sender)地址。
n 为发起交易的 nonce 。
ζ 已在公式 (79) 定义。
i 为 EVM 初始化代码。
公式 (81) - (83) 为合约地址的产生规则。首先定义了函数 LA ,若未提供 salt ,则输出 n 与 s 的 RLP 编码结果,否则输出 (255) · s · ζ · KEC(i)(公式(83)),这也意味着同一个账户,对于同一个合约代码,可以创建可预见的合约地址。然后将 LA 的输出结果经过 Keccak-256 哈希后,取右边 160 位(公式(82))即可得到地址(公式(82))。公式 (81) 则是描述了需带入的实际参数。
σ, A, s, o, r, c, g, p, v, v˜ d, e, w 为消息调用函数参数,分别为:执行当前世界状态,子状态,发送人(sender)地址,原始发送者(考虑通过合约创建的情况),接收地址,执行代码位置(通常与 r 一致),GasLimit,GasPrice,发送的以太币数量(msg.value),通过 DELEGATECALL 而出现在新的执行上下文中的以太币数量,调用入参 input data 数据,当前堆栈深度和对状态进行修改的许可。
σ', g', A', z, o 为函数返回值,分别是:新的状态,剩余 Gas ,新的子状态,状态码,输出(output)内容。
前言与版本
笔者最近在结合以太坊黄皮书读以太坊源码,结合自己的理解解析下黄皮书内的公式,与大家共同学习进步,若大家在阅读以太坊黄皮书时,对公式产生理解上的困惑,可以参阅本文一起看。文章基于当前(2022/1/10)的黄皮书,版本号为
BERLIN VERSION fabef25
,若有不准确之处,欢迎指出。由于该黄皮书内除附录外有 183 个公式,为了让文章篇幅不过长,该解析会由三部分组成一个系列,每个系列解析约 60 个公式,本文为中篇。公式解析
公式 (61) 定义了一个交易的起始状态,即会被扣除预付款(公式(62)),并且 nonce + 1 (公式(63))。
公式 (66) 定义了 g 为交易的 GasLimit 减去执行交易的基本费用。
公式 (64) 表达了交易后的临时状态 σp,根据是合约创建还是普通交易,而又不同的入参。公式 (65) 定义当前入参子状态是公式 (55) 中定义的空子状态中各值与当前交易子状态中各值的与集(and)。
公式 (67) 表示,在交易过程中,调用者若有通过调用
selfdestruct(addr);
销毁合约,则合约内的以太币会累计到退还余额中。公式 (68) 定义了总退还 Gas 数量的计算,与执行交易后剩余 Gas 数量 g' ,执行合约花费的 Gas 数量 (Tg - g')和销毁合约退还数量 A'r 相关。
公式 (69) - (72) 定义了从交易临时中间状态到预备最终状态的转换。首先在交易者的余额中加上应退还的数量(公式(70))。在矿工的余额中加上消耗的以太币数量(公式(71))。并将矿工收益记录在区块的 beneficiary 值上(公式(72))。
公式 (73) - (75) 定义了交易从预备最终状态到最终状态的转换。会先删除需要自毁的合约(公式(74)),再删除接触到的死合约(公式(75)),死合约的定义在公式 (15)。
公式 (76) - (78) 给出了三个交易相关状态的定义。
公式 (79) 定义了创建合约时的参数 salt 是可选的,若提供 slat,需要是一个长度为 32 的字节。
公式 (80) 给出了创建合约函数的定义(输入,输出)。
\
公式 (81) - (83) 为合约地址的产生规则。首先定义了函数 LA ,若未提供 salt ,则输出 n 与 s 的 RLP 编码结果,否则输出 (255) · s · ζ · KEC(i)(公式(83)),这也意味着同一个账户,对于同一个合约代码,可以创建可预见的合约地址。然后将 LA 的输出结果经过 Keccak-256 哈希后,取右边 160 位(公式(82))即可得到地址(公式(82))。公式 (81) 则是描述了需带入的实际参数。
公式 (84) 则表示新创建的合约地址,会被存入 Aa 交易子状态中(于公式 (54) 中定义)。
公式 (89) 定义了 v' ,若地址在之前就有余额,则会继承。
公式 (85) 定义了新的世界状态,在创建的地址上会出现一个新的合约,nonce 为 1, 余额为 v' 加上创建交易传入的以太币,以及空的 storageRoot 和 codeHash (公式(86))。创建者的地址上会扣除发送的以太币(公式(88)),然后保存其状态(公式(87))。
公式 (90) 定义了初始化代码执行函数的输入与输出。
公式 (91) - (99) 定义了参数 I 所包含的项。
公式 (100) 表示合约创建开销,与合约代码大小成正比。
公式 (105) 定义了创建失败的一些场景:原地址不为空,且有 codeHash 或有 nonce;创建合约代码为空;gas 费不足;代码过大;
公式 (104) 定义了状态码 z ,如果创建失败,则为 0 ,成功则为 1 。
公式 (102) (103) 定义了,若创建失败,则不更新状态和子状态。
公式 (101) 定义了若创建失败,则不会收取代码创建开销。
公式 (107) 定义了 a1 这个交易中的第一个临时状态:除非发送者和接收者是同一个地址,否则跟随交易发送的以太币(msg.value)会被发送。
公式 (108) - (113) 描述的是公式 (107) 的具体流程。如果账户 a1[r] 是一个新地址,则会对账户进行状态初始化(公式(112)),并且在余额中加上交易转入的以太币(公式(113)),然后更新到临时状态(公式(111))。相应地,在发送人那边也减少对应的以太币(公式(110)),更新临时状态(公式(108))。
公式 (118) 定义了执行消息调用函数 Ξ ,将会输出 σ** (执行后世界状态),g**(执行后剩余 Gas),A**(执行后子状态码)和 o (调用结果输出(output))。公式 (127) 描述了定义在地址 1 - 8 上的预留函数(参阅黄皮书附录 E),以及常规执行函数。公式 (120) 表达了客户端在部署完合约后,会在本地存储交易调用代码的地址及其哈希。公式 (114) - (117) 则表达了会根据 Ξ 函数输出的 σ** (执行后世界状态)是否为空来判断是否要用 Ξ 函数的其他输出来更新自身对应状态。
公式 (129) 定义了公式 (127) 中的地址集合为 π 。
The text was updated successfully, but these errors were encountered: