EIP-3855 如何讓合約更精簡

(Cover designed by jannoon028 / Freepik)

作者:Irara @ imToken Labs
校稿:NicCyanChangWu @ imToken Labs;FengYi @ imToken Wallet

本文將深入介紹 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。

1、EVM 與 Opcode 概念

在介紹 EIP-3855 之前,我們先來簡單了解 Ethereum 中最重要的 EVM 概念!

EVM(Ethereum Virtual Machine)

我們為了讓智能合約在獨立且安全的環境中執行,而從 Ethereum 節點中會切出一塊具有儲存、執行等功能的運算環境,我們將之稱之為 EVM 虛擬機

而這個 EVM 具備執行智能合約所需的堆疊、記憶體、紀錄 Gas 費用及儲存下一行指令位址等各種必要元件,以便完成開發人員要求的各種計算

EVM 架構,原圖連結:https://ethereum.org/en/developers/docs/evm/
EVM 架構,原圖連結:https://ethereum.org/en/developers/docs/evm/

Bytecode 與 Opcode

我們用 Solidity 等語言所撰寫的智能合約,要先編譯成一連串 EVM Opcode,這一連串 Opcode 稱為 Bytecode。我們接著來進行一段簡單的操作就會比較清楚!

有興趣的讀者可以透過 EtherVM 了解更多關於 Opocde 的介紹。

首先,我們先在 Remix 上編譯下方這份簡易的加法智能合約,編譯完成後就可從 artifacts 資料夾中看到包含此智能合約 Bytecode 與 Opcode 的 Json 資訊檔

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.2 <0.9.0;

contract AddContract {
    function addFunction(uint256 x, uint256 y) public pure returns (uint256) {
        return x + y;
    }
}
Remix 智能合約線上編譯工具
Remix 智能合約線上編譯工具

與此同時,這個智能合約也已部署到 Goerli 測試網中,我們可點選在 Etherscan 上的【Switch To Opcodes View】鈕從 Bytecode 切換到 Opcode 觀看模式。這時,即可得知一連串 Opcode 稱為 Bytecode

Etherscan 區塊鏈瀏覽器
Etherscan 區塊鏈瀏覽器

這裡就只截取這份合約部份的 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 年的統計數據:

表格截取自下方附錄 2
表格截取自下方附錄 2

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/
0x5F 及 0x60 的 Opcode 指令編號,圖片來源:https://ethervm.io/

3、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 費用

4、文章重點整理與結論

重點整理

  • EIP-3855 將減少部署智能合約時的 Bytecode 大小

  • 此外,EIP-3855 也可減少過去開發人員在進行各種優化上的成本與風險

結論

本文介紹了 Ethereum 的 EVM(Ethereum Virtual Machine)及 Opcode 的概念,並且深入探討了 EIP-3855 改進的重要意義與影響。一旦這次的上海升級完成,就可減少開發人員部署合約時所需的儲存空間費用,而 EIP-3855 提案和新的特性,也將吸引更多的開發者加入到以太坊的生態中。感謝您閱讀本文!

5、參考資料與附錄

參考資料

附錄 1:關於 EIP-3855 常見問題

EIP-3855 提案通過後是否可提升 TPS(每秒交易數)?

TPS 增長量不大,因為實際在主網上部署合約的交易並不多,假設在一個區塊內有 10 筆部署合約交易(已是相當多)的狀況,若每個合約部署只「一次性」省約 25 個 PUSH0(25 * 200 * 10 = 50,000 gas ),所以 TPS 可提升的有限。

一般 Solidity 開發人員如何應用此 EIP-3855 提案?

一般 Solidity 開發人員要等到上海升級完成才能享受到程式碼大小變小的好處,EIP-3855 主要是讓 Solidity 編譯器(將 Solidity 程式碼編譯成 Bytecode,而此 Bytecode 可對應不同的 Opcode)將所有 PUSH1 0x00 改為 PUSH0,以節省空間、減少 gas 等好處。

附錄 2:2020 年「PUSH」系列指令數量統計表

此表格參考 EIP-3855 中作者之一 Hugo De la cruz 所統計的 2020 全年度 Ethereum Opcode 數量(區塊高度:8,567,259 ~ 8,582,058 及 12,205,970 ~ 12,817,405)表格,並進行延伸計算

2020 年 PUSH 系列指令數量統計表
2020 年 PUSH 系列指令數量統計表
Subscribe to imToken Labs
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.