前言
本文将深入介绍 Ethereum Opcode 的最新成员:PUSH0
在过去,我们只能使用 PUSH1 0x00(= 0x6000)来达成将数值 0 存入 EVM 的 Stack 中,而上海升级中包含诸多 EIP 之一的 EIP-3855 提案则引入了 PUSH0(= 0x5F)这项新的 Opcode 来达成相同的效果。透过节省 1 Byte 的指令空间(0x6000 → 0x5F),减少了智能合约编译后的 Bytecode 大小,进一步降低智能合约部署的成本。
而本文将会快速地带到 EVM、Bytecode 与 Opcode 的基本概念,并一步一步地带你了解 PUSH0 的核心意义和对现有合约的影响。
文章会从 EVM、Opcode 到 PUSH0 详细说明,若您为区块链开发人员,我们可用一句话函盖整篇文章:
EIP-3855 引入新 Opcode:PUSH0(0x5F)来取代 PUSH1 0x00(0x6000),因减少 1 Byte 指令空间,所以减少了部署智能合约的 200 Gas,所有合约加总一年大约可省 1,362.23 ETH。
EVM 与 Opcode 概念
在介绍 EIP-3855 之前,我们先来简单了解 Ethereum 中最重要的 EVM 概念!
EVM(Ethereum Virtual Machine)
我们为了让智能合约在独立且安全的环境中执行,而从 Ethereum 节点中会切出一块具有储存、执行等功能的运算环境,我们将之称之为 EVM 虚拟机。
而这个 EVM 具备执行智能合约所需的堆叠、记忆体、纪录 Gas 费用及储存下一行指令位址等各种必要元件,以便完成开发人员要求的各种计算。
Bytecode 与 Opcode
我们用 Solidity 等语言所撰写的智能合约,要先编译成一连串 EVM Opcode,这一连串 Opcode 称为 Bytecode。我们接着来进行一段简单的操作就会比较清楚!
有兴趣的读者可以透过 EtherVM 了解更多关于 Opocde 的介绍。
首先,我们先在 Remix 上编译下方这份简易的加法智能合约,编译完成后就可从 artifacts 资料夹中看到包含此智能合约 Bytecode 与 Opcode 的 Json 资讯档。
智能合约在线编译工具 Remix
与此同时,这个智能合约也已部署到 Goerli 测试网中,我们可点选在 Etherscan 上的【Switch To Opcodes View】钮从 Bytecode 切换到 Opcode 观看模式。这时,即可得知一连串 Opcode 称为 Bytecode。
这里就只截取这份合约部份的 Opcode 。
部分 Bytecode 与 Opcode 对照表
现在我们有了 EVM、Bytecode、Opcode 的概念后,那 EIP-3855 到底要改进什么问题呢?
前面的例子中,我们了解到若想要将一个零的值推到 Stack 中,这个动作会被编译成「PUSH 0x00」(两个 Byte,分别是 0x60 及 0x00,其中 0x00 代表零的值,所以 0x6000 整整占了 2 个 Byte 的 Storage 空间!
1 Byte Bytecode 空间需要支付 200 Gas 的费用。
另外,执行 1 个 PUSH1 的操作需支付 3 Gas,而 PUSH0 也是,所以 PUSH0 主要是减少编译后的程序码大小,但实际上执行时的 Gas 消耗和 PUSH1 是一样的。
我们来看一下以下关于 Push1 0x00 指令在 2020 年的统计数据:
Push1 0x00 指令占了 2020 年全年度智能合约 Bytecode 的 4.57% 之多(同时也为 PUSH 系列指令之首),它被如此常用,以致于几乎所有的智能合约中都可看到它的身影!
这时,如果我们新增一个 Opcode 来做和 PUSH1 0x00 完全一样的事,是否就可以再减少 1 个 200 Gas 的空间消耗?
答案是可以的! EIP-3855 即将会在这一次的上海升级中在 Opcode 中引入一条 PUSH0 新指令,而这个新指令的编号是 0x5F!
会选择 0x5F 做为 PUSH0 的原因,除了 0x5F 在上海升级前没有被使用外(空的),而且它也刚好是在 PUSH1(0x60)指令的前一个编号!是一个连续且有意义的指令空间。
0x5F 及 0x60 的 Opcode 指令编号,图片来源:https://ethervm.io/
EIP-3855 带来的改变
将 0x6000 指令瘦身至 0x5F 有一个最重要的改变,就是部署合约时的 Gas 费用变便宜了!程式码中每个使用到 PUSH1 0x00 的地方都可以因为这个改变而省下 200 Gas!
那我们一样以 2020 全年度使用 340,557,331 次 0x6000 指令来计算,大约需耗费 340,557,331 x 200 Gas = 68,111,466,200 Gas。接着若以 1 Gas 需支付 20 GWei 来计算,也就是可以为整个网路省下整整约 1,362.23 ETH,而且各个 Ethereum 节点也可省下不少储存空间。
此外,对于开发人员来说,也可减少使用一些特殊优化手段来减少 PUSH1 0x00 的 200 Gas 开销,例如使用 DUP 指令(将 Stack 中指定的值,如:0 值推到 Stack 中)等。使用 0x5F 代表着可减少开发人员进行各种优化成本与风险,也利于估算智能合约的 Gas 费用。
结论
带来的改变有以下两点。
- EIP-3855 将减少部署智能合约时的 Bytecode 大小
- 此外,EIP-3855 也可减少过去开发人员在进行各种优化上的成本与风险
本文介绍了 Ethereum 的 EVM(Ethereum Virtual Machine)及 Opcode 的概念,并且深入探讨了 EIP-3855 改进的重要意义与影响。一旦这次的上海升级完成,就可减少开发人员部署合约时所需的储存空间费用,而 EIP-3855 提案和新的特性,也将吸引更多的开发者加入到以太坊的生态中。感谢您阅读本文!
参考资料
- 中文
- 英文
- EIP-3855 提案
- EIP-3855 英文討論區
- EVM Codes 列表
- Ethereum Virtual Machine Opcodes
- PDF - ethereum-evm-illustrated
关于 EIP-3855 常见问题
1、EIP-3855 提案通过后是否可提升 TPS(每秒交易数)?
TPS 增长量不大,因为实际在主网上部署合约的交易并不多,假设在一个区块内有 10 笔部署合约交易(已是相当多)的状况,若每个合约部署只「一次性」省约 25 个 PUSH0(25 * 200 * 10 = 50,000 gas ),所以 TPS 可提升的有限。
2、一般 Solidity 开发人员如何应用此 EIP-3855 提案?
一般 Solidity 开发人员要等到上海升级完成才能享受到程式码大小变小的好处,EIP-3855 主要是让 Solidity 编译器(将 Solidity 程式码编译成 Bytecode,而此 Bytecode 可对应不同的 Opcode)将所有 PUSH1 0x00 改为 PUSH0,以节省空间、减少 gas 等好处。
2020 年「PUSH」系列指令數量統計表
此表格参考 EIP-3855 中作者之一 Hugo De la cruz 所統計的 2020 全年度 Ethereum Opcode 数量(区块高度:8,567,259 ~ 8,582,058 及 12,205,970 ~ 12,817,405)表格,并进行延伸计算。