9月27日,據Etherscan顯示,Bitfinex交易所的一個主要錢包以7676.62ETH的Gas費用進行了一筆10萬美元USDT的轉賬,最終接收方為2019年從Bitfinex分拆出來的非託管交易所DeversiFi。這筆巨額礦工費隨後被不知名礦工轉至幣安交易所。
9月28日,事情得到一個圓滿的結局。經過Bitfinex和幣安及礦工交涉,以太坊區塊13307440顯示,礦工歸還了Bitfinex錢包昨晚誤操作而付出的7626ETH Gas費用,DeversiFi給礦工保留了50 ETH作為感謝費。
9月29日,DeversiFi發布了這一事件的完整報告。
事件回顧
發生了什麼
-
UTC +1時間11.10.08 AM,用戶向DeversiFi存入10萬USDT。
-
這筆交易在1 分鐘內得到確認……但這筆交易的交易費高達7,676 ETH(約2300 萬美元)
https://etherscan.io/tx/0x2c9931793876db33b1a9aad123ad4921dfb9cd5e59dbb78ce78f28717595
為什麼發生
-
EthereumJS庫中的潛在問題,再加上在某些情況下和EIP-1559升級相關的gas費用變化,可能導致交易費用極高
-
又遇上Ledger硬件錢包有時可能以非人類可讀的方式顯示Gas費用,錯失用戶肉眼安全檢查
-
只有ETH數量非常大的錢包才會受到影響,其他用戶會看到交易失敗
DeversiFi做了什麼
-
到UTC+1下午12:30:00,DeversiFi團隊意識到這個問題並開始了調查。
-
很快確定了兩個主要關注點,開始積極測試,試圖重現和解釋錯誤交易是如何創建的。
-
與區塊鏈社區分享了一個解釋,注意到這個交易https://twitter.com/deversifi/status/1442487743922286594
-
到UTC + 1時間16:45,禁用Ledger用戶存款
-
到晚上,找到gas費用函數中可能的罪魁禍首,並著手實施改進
-
增加了額外的安全和健全性檢查,以確保與交易相關的gas費用不會超過不切實際的閾值,以防止用戶錯誤、極端網絡費用飆升,並作為防止任何未來編碼錯誤的額外保護層
-
向EthereumJs維護者提交問題,描述了EthereumJs庫中的缺陷
-
最後與Ledger團隊就測試期間發現的異常情況進行了溝通,這些異常情況可能會混淆任何以太坊交易的異常高額費用
-
在28/09/21 15:30 之前推出了安全改進和重開存款
追回資金
-
不知名礦工在13307440區塊打包的巨額Gas費,之後發現此礦工將挖到的ETH存入幣安,DeversiFi立刻聯繫了幣安。
-
幣安同意將DeversiFi的電子郵件地址傳遞給礦工
-
UTC + 1時間20:36 ,收到礦工的電子郵件,達成安全返還資金的流程
-
一個小時內,礦工完成退款交易,共退款7626枚ETH
https://etherscan.io/tx/0x85294effd53126b3bfa9e7f655267e00ac1ae2ef76f4569644670bf5403637d6
-
DeversiFi給礦工保留了50 ETH作為感謝費
到底發生了什麼
背景知識
先回顧一下EIP-1559如何改變以太坊交易費用的處理方式。
-
EIP-1559交易由三部分組成:
-
基本費用- 由網絡決定並銷毀
-
Max Fee Per Gas – 為獲得區塊打包而為每單位Gas支付的最大金額
-
Max Priority Fee – 用戶可選的、直接支付給礦工的小費
EIP-1559 交易包括這些新字段,稱為類型2,而提供原始Gas Price 字段的遺留交易仍受支持,稱為類型0。我們不討論類型1 發生了什麼。
一個常見的誤解是EIP-1559交易完全消除了用戶為交易支付過高Gas費的可能性。但在優先費用和最高費用都設置得太高的情況下,無法防止意外多付。
詳細調查
DeversiFi是以太坊上用於DeFi的第2層協議,其擁有一個前端,提供一個簡單的界面來從各種錢包訪問協議,包括Metamask和Ledger。大約一個月前,DeversiFi更新了前端,以利用倫敦硬分叉激活提供的EIP-1559交易,用最新版本的以太坊庫並按照文檔實現了新功能。
Metamask在生成消息和簽名時執行了很多繁重的工作,但是對於Ledger等其他錢包,DeversiFi使用@ethereumjs/tx npm包(https://github.com/ethereumjs/ethereumjs-monorepo)自己生成交易。
具體來說,創建了一個EIP1559交易主體,在與Ledger錢包庫接口之前生成消息注入參數和費用,以提示用戶在他們的硬件設備上簽名。
處理固定精度和擴展數值範圍的庫在以太坊生態系統中很重要,因為智能合約可以返回高達256位的數字。 JavaScript本身無法處理導致截斷或浮點錯誤的精度。並非所有的大數字庫都支持浮點值,不幸的是,ethereumjs庫使用了BN (https://github.com/indutny/bn.js/),而BN也不支持。看起來這有些道理,因為Solidity不直接支持除整數以外的任何內容,但它確實將責任推給了任何集成其庫的人,也不使用十進制小數數值。
這是這個過程第一個出現問題的地方,特別是計算gas和優先費用然後轉換成一個大數字對象時。由於利用最近幾個區塊用於預測優先費用,因此計算結果可能是十進制小數型數值(MyCrypto的Tay已經警告一段時間了)
當生成的gas值為整數時,底層的ethereumjs庫代碼完美運行,但是當gas值為十進制小數數值時變得奇怪。以太坊庫代碼使用的BN庫拋出一個錯誤,表明傳遞了一個無效值,但是由於該值首先被轉換為緩衝區,因此沒有觸發錯誤處理。
例如,傳遞值33974230439.550003將得到一個整數35624562649959629,可能比預期高六個數量級。
當這種錯誤的數字解釋發生時,它要么由於優先gas金額高於每個gas的最大費用而失敗,要么因為用戶在錢包中擁有的ETH數量幾乎總是不可能足夠高來支付這筆巨額的Gas費超支。
這意味著,除了遇到此問題的少數硬件錢包用戶,幾乎所有人都不會理解他們的交易失敗的原因。
在Ledger上簽署交易時,會向用戶顯示最高費用,以便他們驗證將要授權的交易。讓事情進一步惡化的因素是,當前Ledger將非常大的費用顯示為十六進制值。
在嘗試重現該問題時,DeversiFi遇到瞭如上所示的費用提示。在顯示該問題的示例交易中,B526167CF91FECE4 的十六進制值等於13053145295991336164,這相當於13053145295991.336164 Gwei或13.05 ETH的天文費用 。
如果此交易被接受(並且錢包中存在用於支付它的資金),用戶將簽署最高216,564 ETH 的費用。
DeversiFi懷疑區塊13307440是否可能就是這種情況,其中已支付的最高費用超過了授權的ETH費用2倍。