• 【超完整懶人包】認識比特幣!原理與應用全面解析|動區新手村
  • Account
  • Account
  • BlockTempo Beginner – 動區新手村
  • Change Password
  • Forgot Password?
  • Home 3
  • Login
  • Login
  • Logout
  • Members
  • Password Reset
  • Register
  • Register
  • Reset Password
  • User
  • 不只加密貨幣,談談那些你不知道的區塊鏈應用|動區新手村
  • 動區動趨 BlockTempo – 最有影響力的區塊鏈新聞媒體 (比特幣, 加密貨幣)
  • 所有文章
  • 最完整的「區塊鏈入門懶人包」|動區新手村
  • 服務條款 (Terms of Use)
  • 關於 BlockTempo
  • 隱私政策政策頁面 / Privacy Policy
動區動趨-最具影響力的區塊鏈新聞媒體
  • 所有文章
  • 搶先看
  • 🔥動區專題
  • 🔥Tempo 30 Award
  • 加密貨幣市場
    • 市場分析
    • 交易所
    • 投資分析
    • 創投
    • RootData
  • 區塊鏈商業應用
    • 金融市場
    • 銀行
    • 錢包
    • 支付
    • defi
    • 區塊鏈平台
    • 挖礦
    • 供應鏈
    • 遊戲
    • dApps
  • 技術
    • 比特幣
    • 以太坊
    • 分散式帳本技術
    • 其他幣別
    • 數據報告
      • 私人機構報告
      • 評級報告
  • 法規
    • 央行
    • 管制
    • 犯罪
    • 稅務
  • 區塊鏈新手教學
  • 人物專訪
    • 獨立觀點
  • 懶人包
    • 比特幣概念入門
    • 從零開始認識區塊鏈
    • 區塊鏈應用
  • 登入
No Result
View All Result
  • 所有文章
  • 搶先看
  • 🔥動區專題
  • 🔥Tempo 30 Award
  • 加密貨幣市場
    • 市場分析
    • 交易所
    • 投資分析
    • 創投
    • RootData
  • 區塊鏈商業應用
    • 金融市場
    • 銀行
    • 錢包
    • 支付
    • defi
    • 區塊鏈平台
    • 挖礦
    • 供應鏈
    • 遊戲
    • dApps
  • 技術
    • 比特幣
    • 以太坊
    • 分散式帳本技術
    • 其他幣別
    • 數據報告
      • 私人機構報告
      • 評級報告
  • 法規
    • 央行
    • 管制
    • 犯罪
    • 稅務
  • 區塊鏈新手教學
  • 人物專訪
    • 獨立觀點
  • 懶人包
    • 比特幣概念入門
    • 從零開始認識區塊鏈
    • 區塊鏈應用
  • 登入
No Result
View All Result
動區動趨-最具影響力的區塊鏈新聞媒體
No Result
View All Result
Home 技術 以太坊

以太坊柏林硬分叉|搞懂「Gas開銷計算」改變,新增「訪問清單」功能又如何使用?

以太坊愛好者 by 以太坊愛好者
2021-04-19
in 以太坊, 專欄作者
A A
以太坊柏林硬分叉|搞懂「Gas開銷計算」改變,新增「訪問清單」功能又如何使用?
213
SHARES
分享至Facebook分享至Twitter

以太坊用戶期盼已久的「柏林」硬分叉終於上線,究竟柏林為現有的 GAS 消耗帶來了什麼改變,新引入的訪問清單(Access List)又該如何使用?本文源自於 Franco Victorio 文章《Understanding gas costs after Berlin》,由專欄作者 以太坊愛好者 編譯、撰稿及整理。
(前情提要:以太坊「柏林硬分叉」完成!卻遇客戶端共識錯誤;ETH續刷歷史新高達 2,548 美元)
(事件背景:1 年後終於到來!以太坊「柏林硬分叉」宣布 4月中上線,四個 EIP 提案將納入升級)

本文目錄

  • 摘要
  • 「柏林」升級以前的 Gas 開銷
    • 「柏林」以前的 SLOAD
    • 「柏林」以前的 SSTORE
  • EIP-2929 之後的 Gas 消耗量
    • 「柏林」以後的 SLOAD
    • 「柏林」以後的 SSTORE
  • 總結一下
  • EIP-2930:可選「訪問清單」的事務類型
  • 訪問過的地址
  • eth_createAccessList RPC 方法
  • 防止合約變磚
  • 自己驗證
  • 參考文獻
    • 礦工號召大反攻!「愚人節」遷移算力「51%持續51小時」,武力逼宮以太坊 EIP-1559
    • V神:以太坊 Rollups「快來了」!無須分片就可擴容百倍,最高達4,000TPS
    • 解讀|除了美圖秀秀,為何上市公司狂買BTC不愛以太坊?機構會是ETH新支柱嗎?

 

「柏林」硬分叉在 4 月 15 日啟動,該硬分叉所包含的兩個 EIP (EIP-2929 和 EIP-2930)都會影響事務的 Gas 開銷。

本文會解釋 「柏林」啟動之前,一些指令碼的 Gas 消耗量是如何計算的,而 EIP-2929 對此有何影響,以及 2930 引入的訪問清單(Access List)功能應如何使用。

摘要

這篇文章很長,你如果只想知道結論,看完這部分就可以把網頁關掉了:

  • 柏林硬分叉改變了某些指令碼的 Gas 開銷。如果你在自己的應用中硬編寫了一些指令可使用的 Gas 數量,這些指令可能會卡死。如果真的出現了這種情況,而你的智能合約又是沒法升級的,用戶就需要使用「訪問清單」功能來使用你的應用。
  • 訪問清單功能可略微減少 Gas 開銷,但有些時候也可能會提高 Gas 的總消耗量。
  • geth 客戶端引入了一種新的 RPC 方法,叫做 eth_createAccessList 來簡化訪問清單的生成。

「柏林」升級以前的 Gas 開銷

EVM 所執行的每一個指令碼都有一個對應的 Gas 消耗量。大部分指令碼的消耗量都是固定的:PUSH1 總是消耗 3 gas,而 MUL 消耗 5 gas,等等。有一些指令碼的消耗量是可變的:舉個例子,SHA3 指令碼的開銷由輸入值的長度決定。

我們先了解 SLOAD 和 SSTORE 指令碼,因為這兩個指令碼受 「柏林」 影響最大。後面我們會再談談那些以地址為目標的指令,比如所有的 EXT* 類指令碼和 CALL* 類指令碼,因為它們的 Gas 開銷也被改變了。

「柏林」以前的 SLOAD

在 EIP-2929 實施前,SLOAD 開銷的計算方式很簡單:總是消耗 800 gas。

「柏林」以前的 SSTORE

要講到 Gas 消耗量的計算,SSTORE 指令碼可能是最複雜的了。因為消耗多少取決於該儲存槽當下的值、要寫入的新值、該儲存項是否已經修改過。我們只會分析少數幾種場景,了解個大概。如果你想了解更多,請閱讀本文末尾所附的 EIP 連結。

  • 如果儲存項的值從 0 改為 1(或者任意非零的值),Gas 消耗量是 20000
  • 如果儲存項的值從 1 改為 2(或者任意非零的值),Gas 消耗量是 5000
  • 如果儲存項的值從 1(或任意非零的值) 改為 0,消耗量也是 5000,但你會在事務執行結束後獲得 gas 補貼。我們這裡也不討論 gas 返還機制,因為它不會受到柏林的影響
  • 在一筆事務中,如果儲存項已不是第一次修改,則後續每一次 SSTORE 都消耗 800 gas

細節在這裡並不重要,重要的是,SSTORE 是昂貴的,具體消耗多少 gas 則依賴於多個因素。

延伸閱讀:以太坊有救?改革「ETH手續費市場機制」的 EIP 1559,今年 7月「倫敦硬分叉」將推出

EIP-2929 之後的 Gas 消耗量

EIP-2929 改變了所有這些數值。但在開始之前,我們要先談談該 EIP 引入的一個重要概念:被訪問過的地址和被訪問過的儲存項的鍵(storage key)。

當一個地址或者一個儲存項的鍵,在一筆事務中被 「使用過」 之後,在該筆交易剩下的執行過程中,這個地址(或者這個鍵)都會被當成「已被訪問過的」處理。

舉個例子,如果你在一筆事務中 CALL (調用)另一個合約,那麼該合約的地址就會被標記為 「訪問過的」。類似地,如果你 SLOAD 或者 SSTORE 過一些儲存項槽 ,在該筆事務剩下的執行過程裡,這些槽也會被當成已經訪問過的。

和使用的指令碼無關,即使你只 SLOAD 過某個槽,接下來使用 SSTORE 時,該槽也會被當成已訪問過的。

要注意的是:儲存項的鍵是「內在於」某些地址中的,正如該 EIP 所解釋:

執行事務時,維持單一集合: accessed_addresses: Set[Address] 以及 accessed_storage_keys: Set[Tuple[Address, Bytes32]]

也就是說,當我們說某個儲存槽已被訪問過了,我們的實際意思是:(address, storageKey) 已被訪問過了。

搞清楚了這個概念,我們來談談新的 Gas 消耗量計算模式。

「柏林」以後的 SLOAD

升級前,SLOAD 的 Gas 消耗量是固定的 800。但升級後,Gas 消耗量要看這個儲存槽是否已經被訪問過。

還沒訪問過的,消耗量就是 2100 gas;訪問過的,就是 100 gas。所以,如果某個儲存項槽已經在「已訪問過的儲存項鍵」的集合裡了,就可以省掉 2000 gas。

「柏林」以後的 SSTORE

我們一個一個對比一下,在 EIP-2929 實施後,上面的幾個例子會發生什麼樣的變化:

  • 如果儲存項的值從 0 改為 1(或者任意非零的值),Gas 消耗量是 20000
    • 如果該儲存項鍵還未訪問過,消耗 22100 gas
    • 若已訪問過,消耗 20000 gas
  • 如果儲存項的值從 1 改為 2(或者任意非零的值),Gas 消耗量是 5000
    • 如果該儲存項鍵還未訪問過,消耗 5000 gas
    • 若已訪問過,消耗 2900 gas
  • 如果儲存項的值從 1(或任意非零的值) 改為 0,消耗量保持不變,gas 返還機制也不變
  • 在一筆事務中,如果儲存項已不是第一次修改,則後續每一次 SSTORE 都消耗 100 gas

由此可見,如果某個槽此前已訪問過,則對它的第一次 SSTORE 指令會節省 2100 gas(相比於從未訪問過)。

延伸閱讀:鏈上數據|一文看懂「以太坊世界的現況、各個趨勢、未來」,Defi Eth2 NFT 正加速擴張

總結一下

上面的文字實在囉嗦,我們就直接做一張表,把上面提到的值都匯總一下:

指令碼 「柏林」前 「柏林」後
未訪問過 訪問過
SLOAD 800 2100 100
SSTORE from 0 to 1 20000 22100 20000
SSTORE from 1 to 2 5000 5000 2900
SLOAD+SSTORE* 5800 5000 3000
SSTORE*+SLOAD 5800 5100 3000
SSORE一個已經被寫過的槽 800 100
*從一個非 0 值改為另一個非 0 值,如第三行

注意看最後一行:此時已不再需要區分它到底有沒有被訪問過,因為如果在此之前已被寫入,則必定已被訪問過。

EIP-2930:可選「訪問清單」的事務類型

另一個「柏林」升級包含的 EIP 是 2930。

該 EIP 加入了一種新類型的事務,可以在事務的負載中包含一個「訪問清單」,意思是,你可以在事務執行前就聲明哪些地址和儲存槽應被認為是「訪問過的」 。

舉個例子,對一個未訪問過的槽執行 SLOAD 需要耗費 2100 gas,但如果該儲存槽被包含在了事務的「訪問清單」中,則指令的消耗量機會降為 100 gas。

但如果只要地址和槽被當成「已訪問過的」就可以降低指令的 Gas 消耗量;而訪問清單可以把地址和槽標記為「已訪問過的」;那豈不是說我們可以把這些東西都放在訪問清單中,來獲得 Gas 消耗量的減免?真棒,天賜 Gas!

並不完全如此,因為你每添加一個地址或儲存項鍵,都要支付額外的 Gas。舉個例子。假如我們要向合約 A 發送了一條事務。我們編寫了一條這樣的訪問清單:

accessList: [{
 address: "<address of A>",
 storageKeys: [
  "0x0000000000000000000000000000000000000000000000000000000000000000"
 ]
}]

如果我們發送了一條帶有這條訪問清單的事務,而使用 0x0 儲存槽的第一個指令碼就是 SLOAD,則 Gas 消耗量會是 100 而非 2100,也就是減免了 2000 gas。

但是,在訪問列表中聲明一個儲存項鍵需要額外支付 1900 gas,所以我們只節約了 100 gas。 (如果對該儲存槽的第一個指令是 SSTROE,我們在一個指令中就省下了 2100 gas,也就是總共省下了 200 gas,因為訪問清單本身需要消耗 gas)。

這是不是說,每次使用訪問清單我們都能節省 gas 呢?很遺憾,也不是,因為在訪問清單中填入地址也需要支付 gas。 (也就是我們示例中的 "<address of A>")

訪問過的地址

迄今為止,我們只討論了 SLOAD 和 SSTORE 指令碼,但「柏林」升級還改變了別的指令碼。舉個例子,CALL 指令碼原來的 Gas 消耗量為固定的 700,但 2929 實施後,如果所調用的地址不在訪問清單中,消耗量將提高到 2600;而如果在,則降低為 100。

而且,就像訪問過的儲存鍵一樣,到底哪個指令碼訪問過那個地址並不重要(比如,如果用戶最先調用的是EXTCODESIZE,這一個指令的消耗量是2600,但後續的調用,只要是對同一個地址的,無論是EXTCODESIZE、CALL 還是STATICCALL ,都只消耗100 gas。

那個這個設計對帶有訪問清單的事務有何影響?假設我們向合約 A 發送一條交易,而合約 A 調用了合約 B,而我們在訪問清單中寫入這樣的內容:

accessList: [{ address: "<address of B>", storageKeys: [] }]

我們首先需要為在這條事務的訪問清單中加入這個地址支付 2400 gas,但對 B 使用的第一個指令碼就只需要消耗 100 gas 而不是 2600 gas,這就剩下了 100 gas。

如果B 也需要使用其儲存項,我們又知道它​​將使用哪個鍵,我們也可以把這些鍵包含在訪問列表中,然後為每個鍵的指令省下 100 或 200 gas(取決於第一個指令碼是 SLOAD 還是SSTORE)。

但我們為什麼要加多一個合約來舉例子?我們不是可以這樣寫嗎?

accessList: [
 {address: "<address of A>", storageKeys: []},
 {address: "<address of B>", storageKeys: []},
]

你當然可以這樣做,但不值得,因為EIP-2929 指明了你一開始調用的合約(也即是 tx.to 的目的地)必定會被包含在 accessed_addresses 列表中,所以你就是額外花了 2400 gas ,什麼好處都沒得到。

所以,回頭看我們上面舉的例子:

accessList: [{
 address: "<address of A>",
 storageKeys: [
  "0x0000000000000000000000000000000000000000000000000000000000000000"
 ]
}]

這樣做其實是浪費,除非你在裡面加多幾個儲存項鍵。如果我們假設所有的儲存項鍵的第一個指令都是 SLOAD,那你要至少 24 個鍵,才能賺回來。

而且,如你所見,自己一五一十地分析這些因素、手動生成訪問清單,顯然是極其繁瑣而令人崩潰的事。好在,還有更好的辦法。

延伸閱讀:獨立觀點|以太坊世界停擺 1 天!如何看待「Infura 節點崩潰」及其造成的影響?

eth_createAccessList RPC 方法

Geth 客戶端(從 1.10.2 開始)將包含一個新的 eth_createAccessList RPC 方法,你可以用它來生成訪問清單,就像使用 eth_estimateGas 一樣,只不過返回的不是 Gas 消耗量估計,而是像這樣的數據:

{
 "accessList": [
  {
   "address": "0xb0ee076d7779a6ce152283f009f4c32b5f88756c",
   "storageKeys": [
    "0x0000000000000000000000000000000000000000000000000000000000000000",
    "0x0000000000000000000000000000000000000000000000000000000000000001"
   ]
  }
 ],
 "gasUsed": "0x8496"
}

也就是告訴你一筆事務將會用到的地址和儲存項鍵的清單,以及,假定納入這份訪問清單,將耗用多少 gas。跟 eth_estimateGas 一樣,這也是估計出來的,該筆事務真正上鏈時,會訪問到哪些數據仍有可能改變。

但是,再說一遍,這絕不意味著你只要使用了訪問清單,所用的 Gas 就會比不用清單更少!

我估計隨著時間推移,我們會越來越知道怎麼利用這個功能,但我個人估計,方法的偽代碼形式會像這樣:

let gasEstimation = estimateGas(tx)
let { accessList, gasUsed } = createAccessList(tx)
if (gasUsed > gasEstimation) {
 delete accessList[tx.to]a
}
tx.accessList = accessList;
sendTransaction(tx)

防止合約變磚

值得注意的是,訪問清單功能的主要目的不是節省 Gas。如該 EIP 所述:

緩解由EIP-2929 帶來的合約變磚風險,因為事務可以預先指定、預先支付自身嘗試範文的賬戶和儲存槽。

因此,在實際的執行中,SLOAD 和 EXT* 指令碼都只會消耗 100 gas :這個值低到既足以防止2929 打破某些合約,也可以「解封」被 EIP-1884 封印的合約。

原本,只要一個合約預設了執行的 Gas 開銷,指令碼的 Gas 消耗量變動就有可能導致它變磚。

比如,如果一個合約預設另一個合約的 someFunction 只會用到 34500 gas,因此總是用 someOtherContract.someFunction{gas: 34500}() 調用那個合約,這個合約就有可能變磚。但只要你在事務中添加合適的訪問清單,這個合約就還能運作。

自己驗證

如果你想自己測試一下,複製這個資料庫,這裡面有很多例子,可以使用 Hardhat 和 Geth 客戶端來運行。請仔細閱讀 README 裡的說明。

參考文獻

  • EIP-2929 和 EIP-2930 是兩個跟本文有關的 「柏林」 EIP。
  • EIP-2930 依賴於 「柏林」 升級納入的另一個 EIP:EIP-2718,也叫標準化的事務信封。
  • EIP-2929 大量參考了 EIP-2200,如果你想更深入地理解 Gas 消耗量,你應該從那裡開始。
  • 想了解更複雜的情形中 Gas 消耗量會如何變化,請看這裡。

📍相關報導📍

礦工號召大反攻!「愚人節」遷移算力「51%持續51小時」,武力逼宮以太坊 EIP-1559

V神:以太坊 Rollups「快來了」!無須分片就可擴容百倍,最高達4,000TPS

解讀|除了美圖秀秀,為何上市公司狂買BTC不愛以太坊?機構會是ETH新支柱嗎?


讓動區 Telegram 新聞頻道再次強大!!立即加入獲得第一手區塊鏈、加密貨幣新聞報導。

LINE 與 Messenger 不定期為大家服務

加入好友

加入好友

Tags: ETHethereum以太坊柏林硬分叉硬分叉


關於我們

動區動趨

為您帶來最即時最全面
區塊鏈世界脈動剖析
之動感新聞站

訂閱我們的最新消息

動區精選-為您整理一週間的國際動態

戰略夥伴

Foresight Ventures Foresight News

主題分類

  • 關於 BlockTempo

動區動趨 BlockTempo © All Rights Reserved.

No Result
View All Result
  • 所有文章
  • 搶先看
  • 市場脈動
  • 商業應用
  • 區塊鏈新手教學
  • 區塊鏈技術
  • 數據洞察
  • 政府法規
  • RootData
  • 登入

動區動趨 BlockTempo © All Rights Reserved.