Ropsten 共識問題
概述
2021年7 月21 日,OpenEthereum 團隊注意到他們在Ropsten 上的節點卡在了區塊 10679538 處。人們原以為這是OpenEthereum 的問題。其實,問題真正出在go-ethereum 實現檢查1559 交易發送方餘額的方式上。一個無效交易(發送方餘額只夠支付交易實際使用的gas,而非交易指定的 maxFeePerGas 總額)被打包進了區塊。由於Ropsten 礦工運行的都是go-ethereum,這個區塊隨後又被其它go-ethereum 礦工接受,但是被網絡中的其它一些客戶端拒絕了。具體來說,OpenEthereum 和Besu 拒絕了這個交易/區塊,Nethermind、go-ethereum 和Erigon(這些客戶端實現的部分代碼來自go-ethereum 代碼)接受了它。問題的根源已經找到,相關客戶端已經在新的版本中修復了該問題:
go-ethereum: v1.10.6, fix PR;Erigon: v2021.07.04-alpha, fix PR;Nethermind: v1.10.79, fix PR。
問題區塊的信息
網絡:Ropsten區塊編號:10679538哈希值:0x1252a34c4f2b061adc609e909d958c02e1ac39043e2e60c0ec47e565e3f625f1OpenEthereum debug 日誌eth_getBlock 輸出(go-ethereum)
eth.getBlock('0x1252a34c4f2b061adc609e909d958c02e1ac39043e2e60c0ec47e565e3f625f1'){ baseFeePerGas: 11, difficulty: 1124214874, extraData: '0xd883010a05846765746888676f312e31352e36856c696e7578', gasLimit: 8000000, gasUsed: 1762587, hash: '0x1252a34c4f2b061adc609e909d958c02e1ac39043e2e60c0ec47e565e3f625f1', logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', miner: '0xfbb61b8b98a59fbc4bd79c23212addbefaeb289f', mixHash : '0x178c542ebd5b730aa141b3e07fce663b81d7f5485011cca55b5cd55dc39b2550', nonce: '0x98728302c513a677', number: 10679538, parentHash: '0xe936ee0e5a915b9c163a7a1ff67269dd5f1ccb981f91b269a2130711e6a62598', receiptsRoot: '0x09a6eb2bf38000dd934b2cdc66f7f7923397ddd6d9cd1ac69379aaed73d00f1e', sha3Uncles: '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347', size: 5175,stateRoot: '0xa942f7462d923a5e292627b64e1b20cc314bac31f7e72ecb02b65d954d12f758', timestamp: 1626863988, totalDifficulty: 34224959923696599 , transactions: ['0x07ca8f5634d634e2eb67a9af6f9d510d73d3c2f0393f0e0490e7c0b4c18fdf0e', '0x9dbef6da5331b085d1dcc70eaa028376fd0452c49992e5ddccc132f4d42467cc', '0xdfa858a98cab341e540fc2da8abfd0b298df22a9ee9eff0c6e1edf4828ab9b84', '0x01bdfaba318f4c0b2878db0d413a20d3a2669ebbaa5c4e8f6901bdc9a01a99ec', '0xd9d2aa19f747b04863eb13a2698cd8a3c96b2463d2cf7eb60d7ca3ea8e2d45e3', '0xf5ee17e9bf8bc4fe3325860d91535d1eb98bc1d83f39fe998e0b6c4706c581c5'],transactionsRoot: '0x6e6f39318ad2e60969e2422977deffd42dc34ac7bdbb6fb1934541c044f18774',uncles: []}
測試網事故的時間線
(注:所有時間已轉換成北京時間)。
2021 年7月21日
18 : 39:Ropsten 測試網上挖出區塊10679537。21 : 53:OpenEthereum 開發者在Ethereum R&D discord 的#1559-dev 頻道發帖稱他們的節點卡在了區塊10679538 處。 21 : 58:@smixx 稱他們的在Ropsten 節點位於區塊10680453。22 : 36:Besu 確認他們的節點也拒絕了區塊10679538。22 : 51:確認挖出區塊10679538 的礦工是go-ethereum節點。 22 : 55:確認go-ethereum 礦工仍繼續在區塊10679538 上面挖礦。 22: 56:確認Nethermind 也接受了區塊10679538。23 : 08:go-ethereum 已確認問題的根本原因。 23 : 43:go-ethereum 開啟 pull request,提供候選修復方案。 23 : 46:Erigon 開啟pull request,提供候選修復方案。
2021年7月22 日
00 : 01:更新後的go-ethereum 和Besu 礦工在Ropsten 上重啟(此時,錯誤的鏈已經挖到了區塊10680803)。 00 : 43:EthereumJS 確認與go-ethereum、Erigon 和Nethermind 存在同樣的問題。 01 : 57:Nethermind 開啟pull request,提供候選修復方案。 10 : 22:修復後的版本挖出了區塊 10680804。22 : 54:go-ethereum 發布了修復後版本 v1.10.6。 ~23 : 00:Nethermind 發布了修復後版本 v1.10.79。
2021 年7 月23日
~00 : 00:Erigon 發布了修復後版本 v2021.07.04-alpha。
糾正措施建議
提高規範中斷言(assertion)的清晰度
該提交新增了關於EIP 1559 類型交易有效性的斷言。具體來說,在第217 行代碼新增了以下斷言:
assert sender.balance >= gasLimit * transaction.max_fee_per_gas
另外還要注意的是,在前幾行代碼(第207 行)中,sender.balance 被修改成了減去交易量之後的部分(sender.balance -= transaction.amount)。這個參數引發了混亂,因為一些客戶端團隊在檢查第217 行定義的斷言時使用的是全部sender.balance(即,沒有減去transactiion.amount 的發送者地址餘額),而非更新後的值。
Go-Ethereum 恢復
@holiman 關於go-ethereum 恢復的說明:節點同步時跟隨錯誤的鏈假設你正在運行geth,並處於同步中。區塊X 上發生了分叉。你的節點跟隨了總難度較高的錯誤的鏈。在區塊Z,你停止了節點並將其更新至修復後版本。
問題描述:節點依然在“錯誤” 的鏈上。
解決方案:執行debug.setHead{X-1) 回到分叉發生之前。這會將節點倒回區塊X 之前的某個狀態,不一定是區塊X-1 的狀態,因為geth 不一定有區塊X-1 的完整狀態,但是會有其它某個區塊的完整狀態。通常情況下,geth 大約每隔1 萬個區塊(1 小時)和/或宕機時會將狀態刷到磁盤。如果geth 在gcmode=archive 下運行,就會將每個區塊都刷到磁盤。
當錯誤的鏈總難度較高時進行同步假設你正在同步一個geth 節點,區塊X 上發生了分叉。由於分叉已經發生了,再加上錯誤的鏈總難度更高,你很可能會同步錯誤的鏈,pivot 區塊是X+M。在這種情況下,由於你沒有區塊X+M 之前的狀態,無法執行debug.setHead 來解決這個問題。
這種情況需要重新同步。但是,你需要防止geth 同步錯誤的那條分叉鏈。這可以通過whitelist 命令行參數實現。
$ geth -h | grep white --whitelist value Comma separated block number-to-hash mappings to enforce (
所謂的白名單,就是一個geth 節點在與另一個對等節點連接時會向對方請求區塊123123 的數據。如果該geth 節點收到的區塊頭中的哈希與白名單中的不符,就會與之斷開連接。這就意味著,節點將排斥錯誤的鏈上的對等節點,只與較短(但是正確的)鏈上的對等節點連接。
(完)
原文鏈接:
https://github.com/ethereum/eth1.0-specs/blob/master/network-upgrades/retrospectives/london.md
作者: Tim Beiko
翻譯&校對: 閔敏 & 阿劍