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

  • 哈希值:0x1252a34c4f2b061adc609e909d958c02e1ac39043e2e60c0ec47e565e3f625f1

  • OpenEthereum 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 --whitelist 123123=0x2342fafa9af9af9af9af9af9`。  
`

所謂的白名單,就是一個 geth 節點在與另一個對等節點連接時會向對方請求區塊 123123 的數據。如果該 geth 節點收到的區塊頭中的哈希與白名單中的不符,就會與之斷開連接。這就意味着,節點將排斥錯誤的鏈上的對等節點,只與較短(但是正確的)鏈上的對等節點連接。

(完)

(文內有許多超鏈接,可點擊左下 ”閱讀原文“ 從 EthFans 網站上獲取)


原文鏈接 :

https://github.com/ethereum/eth1.0-specs/blob/master/network-upgrades/retrospectives/london.md

作者 :Tim Beiko**

翻譯 & 校對 :閔敏 & 阿劍