以太坊今日將完成柏林硬分叉升級,這些知識點你需要了解

買賣虛擬貨幣

在北京時間4月15日下午18:00左右(具體是以太坊網路區塊高度達到#12244000時),以太坊的柏林(Berlin)硬分叉升級將會發生,這次升級將納入4個新的EIP改進提案,而其中兩個(EIP-2929和EIP-2930)將會影響交易的gas成本計算。

本文解釋了在這次硬分叉升級前後的gas成本計算,這將如何隨EIP-2929而發生改變,以及如何使用EIP-2930引入的訪問列表功能,原文作者是Nomic Labs軟體開發者Franco Victorio。

注:文章篇幅較長,以下是其中的一些要點:

  1. 柏林硬分叉改變了一些opcode操作碼的gas成本。如果你在dapp或智慧合約中有一個硬編碼的gas值,它們可能會停止工作。如果發生這種情況,並且智慧合約是不可升級的,則使用者將需要使用訪問列表(EIP-2930)來啟用它。

  2. 訪問列表可用於稍稍降低gas成本,但在某些情況下,它們實際上會增加gas消耗總量。

  3. geth包含了一個新的RPC方法(eth\u createAccessList)來簡化訪問列表的建立。

柏林硬分叉前的gas成本

EVM執行的每個opcode操作碼都有一個相關的gas成本。對於大多數操作碼而言,這個成本是固定的:PUSh2總是消耗3個單位的gas,MUL則消耗5個單位的gas,等等。而對於其他操作碼來說,它是可變的:例如,SHA3操作碼的成本取決於其輸入的大小。

我們將重點討論SLOAD和SSTORE操作碼,因為它們是受柏林硬分叉影響最大的操作碼。我們稍後將討論那些以地址為目標的操作碼,就像所有的 EXT*和CALL*操作碼,因為它們的gas成本也會發生變化。

柏林硬分叉之前的SLOAD

如果沒有EIP-2929,SLOAD的成本很簡單:它總是會消耗800 gas。

柏林硬分叉之前的SSTORE

就gas而言,SSTORE可能是最複雜的操作碼,因為它的成本取決於儲存slot的當前值、新值以及它是否以前被修改過。我們將只分析一些場景以獲得基本的理解。如果你想了解更多,請閱讀本文末尾連結的eip。

  1. 如果slot的的值從0更改為1(或任何非零值),則成本為20000;

  2. 如果slot的的值從1更改為2(或任何其他非零值),則成本為5000;

  3. 如果slot的的值從1(或任何非零值)更改為0,則成本也為5000,但在交易結束時你將獲得gas退款。這篇文章中,我們不會詳細討論退款,因為它們不受柏林硬分叉的影響;

  4. 如果以前在同一事務中修改了該值,則所有後續sstore的成本為800;

這裡的細節有些枯燥,重要的一點是,SSTORE是非常昂貴的,其成本取決於幾個因素。

實施EIP-2929之後的gas成本

EIP-2929改變了所有這些值,但在此之前,我們需要先談談這個EIP引入的一個重要概念:已訪問地址和已訪問儲存金鑰。

如果地址或儲存金鑰以前在交易期間被“使用”,則該地址或儲存金鑰就被視為已訪問。例如,當你呼叫另一個合約時,該合約的地址會被標記為已訪問。類似地,當你SLOAD或SSTORE某些slot時,它將被視為在交易的其餘部分已被訪問。不管是哪個操作碼做的:如果一個SLOAD讀取了一個slot,那麼它將被認為對接下來的SLOAD以及SSTORE都是已訪問的。

這裡需要注意的一點是,儲存金鑰位於某個地址的“內部”。正如EIP所解釋的:

“執行事務時,維護一組accessed_addresses: Set[Address] 和 accessed_storage_keys: Set[Tuple[Address, Bytes32]]”

也就是說,當我們說一個儲存slot被訪問時,我們實際上是說一對(address, storageKey)被訪問了。

話雖如此,我們還是來談談新的gas成本吧。

柏林硬分叉之後的SLOAD

在柏林硬分叉之前,SLOAD的固定成本是800 gas,現在,這取決於是否已訪問了儲存slot。如果未訪問,則成本為2100 gas,如果已訪問,則成本為100 gas。因此,如果slot在已訪問的儲存金鑰列表中,則一次SLOAD的成本會降低2000 gas。

柏林硬分叉之後的SSTORE

讓我們在部署EIP-2929的環境下回顧一下之前的SSTORE示例:

  1. 如果slot的值從0更改為1(或任何非零值),則成本為:22100(如果未訪問儲存金鑰),20000(如果已訪問儲存金鑰);

  2. 如果slot的值從1更改為2(或任何其他非零值),則成本為:5000(如果未訪問儲存金鑰),2900(如果已訪問儲存金鑰);

  3. 如果slot的值從1(或任何非零值)更改為0,則成本與上一項相同,然後加上退款;

  4. 如果以前在同一交易中修改了該值,則所有後續SSTORE的成本為100;

如你所見,如果要修改的slot以前被訪問過,那麼第一次SSTORE的成本將降低2100 gas。

下面的表總結了目前為止所有改變的值:

請注意,在最後一行中,談論是否訪問了slot是沒有意義的,因為如果它以前被寫入過,則表明其也被訪問過。

EIP-2930

我們在文章開頭提到的另一個EIP就是EIP-2930,這個改進提案新增了一種新型別的事務,該事務可以在事務負載中包括訪問列表。這意味著你可以在事務開始執行之前預先宣告哪些地址和slot應被視為是已訪問的。例如,一個未訪問slot的SLOAD成本為2100,但是如果該slot包含在事務的訪問列表中,則相同的操作碼成本就為100。

但是,如果當地址或儲存金鑰已被訪問時,gas成本變更低了,這是否意味著我們可以將所有內容新增到事務的訪問列表中並降低gas成本呢?不完全是這樣,因為你還需要為新增的每個地址和每個儲存金鑰支付gas。

讓我們看一個例子,假設我們正在向合約A傳送一筆交易,訪問列表可能如下所示:

如果我們用這個訪問列表傳送了一筆交易,並且第一個使用0x0 slot的操作碼是SLOAD,則它將花費100 gas(而不是2100 gas),這就降低了2000 gas的消耗量。但事務訪問列表中包含的每個儲存金鑰的成本為1900 gas,所以我們只省了100 gas。(如果訪問該slot的第一個操作碼是SSTORE,那麼我們將節省2100 gas,這意味著如果考慮到儲存金鑰的成本,我們總共將節省200 gas。)

這是否意味著我們在使用帶有訪問列表的交易時總是能節省gas消耗?並非如此,因為我們還要為訪問列表中的地址支付gas成本(在我們的示例中是"<address of A>")

已訪問地址

以上,我們只討論了SLOAD和SSTORE操作碼,但這些並不是柏林硬分叉之後唯一改變的操作碼。例如,原先呼叫操作碼的固定成本為700 gas。但是在實施EIP-2929之後,如果地址不在訪問列表中,則開銷就是2600 gas,但如果是在已訪問列表中,則開銷就是100 gas。而且,與已訪問儲存金鑰一樣,之前訪問該地址的操作碼並不重要(例如,如果先呼叫EXTCODESIZE,則該操作碼將花費2600 gas,使用相同地址的任何後續EXTCODESIZE、CALL、STATICCALL將花費100 gas)。

這是如何受到訪問列表交易的影響的?例如,如果我們將一筆交易傳送至合約A,而該合約呼叫另一個合約B,那麼我們可以包含如下訪問列表:

我們必須支付2400 gas的費用才能將這個訪問列表包含在交易中,但是第一個使用B地址的操作碼將花費100 gas(而不是2600gas)。所以我們這樣做就節省了100 gas,如果B以某種方式使用它的儲存,並且我們知道它將使用哪些金鑰,那麼我們還可以將它們包括在訪問列表中,併為每個金鑰節省100/200的gas(取決於第一個操作碼是SLOAD還是SSTORE)。

但我們為什麼要談另一個合約呢?我們呼叫的合約怎麼了?我們為什麼不這樣做?

我們可以這樣做,但這是不值得的,因為EIP-2929指定了被呼叫的合約地址(即tx.to)總是包含在accessed_addresses列表中,因此這隻會白白浪費2400 gas。

讓我們再次分析上一節的示例:

這實際上是浪費,除非我們包含多個儲存金鑰。如果我們假設一個SLOAD總是首先使用一個儲存金鑰,那麼我們至少需要24個儲存金鑰才能實現收支平衡。

顯然,分析並建立這樣的一個訪問列表是沒有意義的。幸運的是,我們有更好的方法。

eth_createAccessList RPC方法

Geth(從1.10.2版本開始)包含了一個新的eth\u createAccessList RPC方法,其可以用來生成訪問列表。它的用法類似於eth_estimateGas,但它不是用於估算gas,而是返回如下內容:

也就是說,它為你提供了該交易將使用的地址和儲存金鑰的列表,以及如果包含訪問列表,則會消耗的gas。(而且,與eth_estimateGas一樣,這是一種估計值,在實際進行交易時,列表可能會更改。)

我想,隨著時間的推移,我們會發現執行此操作的正確方法是什麼,而我的虛擬碼猜測是:

啟用合約

必須要指出的是,訪問列表的主要目的不是使用gas,正如EIP所解釋的:

“EIP-2929所引入的是減輕合約破壞風險,因為交易可預先指定和支付交易計劃訪問的帳戶和儲存slot。因此,在實際執行中,SLOAD和EXT*操作碼只需要100 gas,這已經足夠低了,它不僅可防止因該EIP而導致的破壞,還可以“啟用”由於EIP 1884而卡住的任何合約。”

這意味著,如果一個合約對執行某些操作的成本做出假設,那麼gas成本的增加可能會導致它無法工作。例如,一個合約呼叫另一個合約(例如someOtherContract.someFunction{gas: 34500}())因為它假設某個函式正好使用34500 gas,那麼它就會中斷,但如果在事務中包含適當的訪問列表,那麼合約將再次工作。

如果你想自己測試這些EIP,你可以複製這個repo,它有幾個可使用Hardhat和geth執行的示例。有關說明,請檢視README檔案。

相關資料:

1、EIP-2929‌和EIP-2930‌

2、EIP-2930依賴於柏林硬分叉的另一組成部分:EIP-2718‌;

3、EIP-2929引用了大量EIP-2200‌的內容,所以如果你想更深入地瞭解gas成本,你應該從EIP-2200開始;

4、有關比較gas使用量變化的更復雜示例‌;


本公眾號所載文章中觀點僅代表原作者個人立場,不代表巴位元資訊立場。投資者不應將文中觀點、結論為作出投資決策的惟一參考因素,亦不應認為文中觀點可以取代自己的判斷。在決定投資前,如有需要,投資者務必向專業人士諮詢並謹慎決策。

作者:灑脫喜,來源:巴位元資訊

免責聲明:

  1. 本文版權歸原作者所有,僅代表作者本人觀點,不代表鏈報觀點或立場。
  2. 如發現文章、圖片等侵權行爲,侵權責任將由作者本人承擔。
  3. 鏈報僅提供相關項目信息,不構成任何投資建議

推荐阅读