因「0x10」地址的Gas消耗分歧產生的柏林硬分叉Bug
以太坊OpenEthereum 單客戶端在區塊 #12244294處發生的Bug 導致當時的以太坊網絡停機,並在問題區塊產生後無法與網絡保持同步。那麼造成這個事故的原因究竟是什麼呢?
使用Tokenview以太坊瀏覽器查看觸發了這個事故的交易:https://eth.tokenview.com/cn/tx/0x7006f38fa2e6654fae1a781aefc5885fe0cb8f778b1add10636eaf7e34279247
這是一筆合約調用交易,是從KuCoin交易所向其他地址分發ETH。
我們仔細分析一下合約調用過程:
1. 在瀏覽器的“數據輸入”欄展示的是合約調用的參數,第一行表示地址列表從“40”(16進制)字節,也就是64 字節開始,圖中第4行,第二行表示轉移數額的列表從“1a0”(16進制)字節,也就是416 字節開始,數據輸入欄第15行。
2. 轉賬是按照地址列表的順序進行的,往每個地址轉入的數額和轉移數據的列表一一對應的。
3. 現在我們開始遍歷地址列表,看第三行的“10” (16進制),表示的是即將為接下來的16 個地址轉入ETH。
按照圖上順序,當數到第10個的時候,發現值變成了“10”。這個值事實上是表示轉移數額的列表長度。但是按照第三行的指示,應該向16個地址轉入,那麼合約會把“0x10”當成地址繼續執行轉賬操作,向地址“0x10”轉入0個ETH。
事實上,“0x10”是EVM “特殊地址” 之一,它完全處在EVM 的預編譯合約列表內。它是一個由EIP-2537 斷言的預編譯合約,是為BLS 配對密碼學程序而設的,但“0x10”地址的Gas消耗分歧
二級標題
“0x10”地址的Gas消耗分歧
柏林硬分叉改變了EVM 中Gas 消耗量的計量方法。在EIP-2929 實施後,如果在一筆交易中對同一個存儲槽多次執行狀態存儲操作,第一次執行會消耗更多Gas,後續執行的消耗會更少。
這就是OpenEthereum 在區塊#12244294 處發生Bug 的根源:OpenEthereum包含了EVM已實現的預編譯列表。所以OpenEthereum 會對該筆交易中訪問了“0x10”的交易給gas 折扣。但網絡的絕大部分活躍客戶端都不是這樣實現EIP-2929 的,它們只會給訪問了已激活預編譯合約的交易提供gas 折扣。
由此,OpenEthereum 客戶端對該交易消耗了多少Gas 的計算與網絡中其他客戶端發生了分歧。
這場由Gas消耗分歧引起的OpenEthereum 單客戶端停機,雖沒有嚴重到導致重大的鏈分叉,但也提醒我們利用多客戶端實現來提升抗性。
不可否認,區塊鏈技術仍然處於不斷嘗試不斷前進的過程中,2021 年爆發的Defi和NFT也以前所未有的速度普及給更多的受眾,Tokenview希望攜手更多的開發者打造更好的區塊鏈世界。


