Blocknative 已經找出了 MakerDAO 在 3 月 12 日和 13 日 時的清算活動乃是有人刻意爲之的證據。這些證據是從 “Mempool”(交易內存池),即礦工打包區塊時候的備選交易池,中來的。交易內存池是一個以太坊生態中經常被人忽略 —— 也不受大家重視 —— 的部分。

作爲交易內存池分析專家, Blocknative 運營着一個由遍佈全球且配置各不相同的 Geth 節點和 Parity(OpenEthereum)節點組成的網絡。這一基礎設施 使我們能部署實時的交易監控服務 ,我們可以捕捉、規範化和歸檔內存交易池的狀態變化(如無專門操作,這些變化都是川流不息、轉瞬即逝的)。Blocknative 在 “黑色星期四” 時捕捉到了 3000 萬行數據,使我們能做一項開放式的研究;迄今爲止,我們已經發現了數個似乎已被利用過的 “漏洞”。

雖然很多人都已寫過 “黑色星期四” 的結果,但在本文中我們會首次披露 Blocknative 內存池存檔中的數據,用新的數據和理論說話。我們也把分析要用的底層內存池交易數據集合開放出來了,供大家審閱(見下文)。

如果你是一位安全研究員,想了解更多我們的發現,或想發現其它潛在的交易池異常, 請聯繫我們

總結

Blocknative 的內存池法醫報告顯示,內存池中有三大因素影響到了 3 月 12 日和 13 日的事態:

  1. 被阻塞的交易 —— 內存池的擁堵極大地提高了交易卡殼率,讓同一地址連續發出的多筆交易都被卡住、不能得到處理;
  2. 交易池 “壓縮” —— 交易池中可上鍊部分(即被礦工認爲 Gas 費用足夠高的交易)比重的大幅減少,可能影響到了 Gas 價格的估計;
  3. “Hammerbot” —— 自動化的交易系統加劇了交易池堵塞,因此與交易池壓縮效應相疊加;

我們出版這份分析以及相關的數據,是爲了引起大家對動態交易風險的關注,這種風險在區塊鏈網絡擁堵期間可能會迅速出現。

背景: “黑色星期四” 概況

在 2020 年 3 月 12 日,密碼學貨幣市場上出現了有史以來最大的拋售潮,幾個小時裏面,ETH 價格暴跌 43%,BTC 的價格跌掉了 39%。隨着價格下跌,一個負反饋循環出現,多種 DeFi 合約內都開始出現流動性降低和強制清算活動。在此期間,這種下行的壓力使得每一個嘗試在網絡上發送交易的個體都遇到了嚴酷的挑戰。

- UTC 時間 2020 年 3 月 12 日到 13 日期間按小時加權的 ETH 平均價格。數據來源:cointelegraph.com -

迅速的價格變化導致了以太坊交易池的持續擁堵,因爲自動化的交易系統(比如交易機器人)總是按照程序對波動性機械地作出反應。

如此堵塞情形的一個負面後果是 MakerDAO 債倉 清算活動中出現了 “0 價格拍賣” 現象。在 “黑色星期四” 期間的 3994 個清算拍賣中,有 1462 個(36.6%)債倉的擔保品是被 0 價格拍走的。在大概 12 小時的時間裏,鎖在債倉中、總計 832 萬美元的擔保品被 0 價格拍走,沒有讓系統回收到一分錢。更多細節請看 MakerDAO 自己發佈的《 2020 年 3 月 12 -13 日的價格暴跌及其對 MakerDAO 的影響 》一文。

1. 由交易池堵塞導致的交易卡殼

3 月 12 日時 ETH 價格的迅速變化導致用戶大量發送交易,產生了交易池內部的擁堵。對價格波動自動作出反應的機器人的活動加劇了這一情形。

- 進入交易池的待打包交易計數 vs. 打包上鍊的交易計數(按小時計) -

結果就是大量的交易通過網絡傳播,迫使使用默認交易池設置的節點通過下列手段來保護自己的系統資源:

  1. 刪除,或者說丟棄掉許多有效的交易;
  2. 拒絕,或者說無視掉許多有效的交易。

交易池通常會用最低手續費門檻來選擇放棄哪些交易。雖說這些被丟掉的交易並沒在整個網絡中 “完全丟失”,但被丟掉的交易可能會遭遇顯著的時延,直至網絡條件迴歸正常。

- 2020 年 3 月 11 日至 3 月 14 日期間,到達交易池的被丟棄掉的交易和卡殼交易(不能處理的交易)計數(按小時計)-

更重要的是,丟棄交易還會帶來一個副作用:增加交易卡殼的可能性。

刪掉一筆交易之後,該節點不會留下關於該筆交易的任何信息,比如發送地址和交易 nonce。因此,當同一個地址的一筆新的交易(使用新的一個 nonce)到達交易池,該節點會發現該地址已處理交易和這筆新交易之間有 nonce 空缺(nonce gap)(因爲該新交易的上一筆交易還沒得到處理),因此也不能處理這筆新交易。受此效果影響的交易就只能放到該節點的交易池中無法處理的隊列中。 這些交易,無論所支付的交易手續費(即 Gas 費)有多高,都一概會被卡住、無法處理。

想了解更多細節,請看我們此前講解交易排序的文章: 《節點是你通往內存池的網關》

與此同時,網絡的堵塞導致進入內存池的 Gas Price 門檻隨之迅速提高,因此最初的一些交易現在會因爲 Gas Price 太低而被拒絕。而且,因爲之前被刪除的交易不能回到交易池中來,交易的 nonce 空缺問題又變得更嚴重了。事實上,一些節點實現會在一段時間內主動無視掉這些被拒絕的交易,以保護自己不受點對點網絡中的泛濫攻擊(spamming)影響。所以, nonce 空缺實際上會鎖住這些受影響的地址,使得他們無法完成新的交易。

卡殼交易更有可能影響會發出許多新交易的地址,包括自動化交易系統、支付網絡,甚至交易所。這些系統想回到正軌,但通常會加劇擁堵,因爲越來越多交易被推遲處理。

有沒有解決辦法?你得主動發現自己何時開始遭遇卡殼。這可能有點年,因爲你的交易可能僅在某些節點處是卡殼的,但並不是在所有節點處都面臨卡殼。因此,你必須確定第一筆被丟棄、導致 nonce 空缺的交易,然後立即用可以得到打包的 Gas Price 加速 讓這筆交易上鍊。最後,你還得繼續監控一開始發現卡殼的交易,確保它從不能處理的隊列中移回到了交易池的待打包部分中,併成功上鍊。如果你的交易還是卡殼,重複上述加速步驟,直到你可以確認所有導致 nonce 空缺的交易都已成功上鍊。當然,這也是我們開發並運營我們的 Notify API 的理由之一。

2. 壓縮內存池中可上鍊的部分

網絡阻塞的出現 —— 及其導致的交易卡殼 —— 使得交易池中可上鍊交易的比重迅速縮減。 我們管這叫 “交易池壓縮”

礦工的激勵分兩部分:區塊獎勵和交易的 Gas Price;所以收益最大化需要打包 Gas Price 最高的那部分交易,挖礦時要根據交易池 —— 即候選交易(也可以說候選區塊)—— 的情況(也就是各交易願意給多高的 Gas Price)來決定打包哪些交易。

在交易池擁堵時,多種行爲會導致交易池壓縮,對礦工來說值得考慮的交易比重越來越小:

  1. 資源耗盡 :在某些節點實現中,卡殼交易數量的迅速上升會消耗掉可觀的交易內存池資源。這又反過來導致節點處理有效 pending 交易的可用資源進一步減少。
  2. 不斷升高的 Gas Price :ETH 價格的下跌導致許多交易變得 “高度緊急”,因爲這些交易在構造時是希望能夠趕在 ETH 進一步下跌時上鍊確認的。這使得個體用戶也好、自動化機器人也好,都趕緊提高 Gas Price。因爲不是所有參與者都這樣密切關注着堵塞情形,待打包交易總體包含了反常比例的低價 —— 因此不可能被打包的 —— 交易。
  3. 反應慢半拍的定價算法 :交易池中待打包交易的 Gas Price 分佈,以及近期被打包交易的 Gas Price,使得一些預測合適 Gas Price 的服務的測算出現扭曲。但這(些服務報出已低於實際情形的 Gas Price)就導致更多 Gas Price 過低的新交易出現,又進一步加劇了測算值與實際值的不一致。

交易池堵塞出現時,讓交易打包上鍊所需付出的 Gas Price 會升高是意料中的事情。但是,因爲並不是所有人都跟蹤到了 Gas Price 這種迅速的提高 —— 包括,尤其是那些有 Gas Price 報告功能的服務 —— 上鍊所需的平均 Gas 價格開始偏離進入交易池的交易平均 Gas 價格 。而一般的出價上漲跟不上當時的價格上漲速率。

在 3 月 12 日,我們的數據平臺發現了交易手續費定價的明顯偏離。在 3 月 13 日,交易池中部分交易的 Gas Price 與上鍊交易的 Gas Price 差額在可預料範圍內, 但在 3 月 12 日,已挖出交易和未挖出交易的 Gas 出價簡直是天壤之別

不能上鍊的交易要成爲可以上鍊的交易,一般來說標準的操作就是提高 Gas Price。但是,在 3 月 12 日,這樣做根本就沒用。 因爲,大量交易池資源被以幾乎同樣的 Gas Price 重發的交易消耗掉了

- 2020 年 3 月 12 日至 13 日每小時的 Gas Price 箱型圖。每個小時的箱型都劃分了 1 分位值和 3 分位值,兩者中間的線表示中值,而箱頂和箱底的線表示最大值和最小值。已忽略掉了異常值(1.5 IQR) -

這張圖反映了真實情形嗎?還是說這些出價過低的交易本身就是爲了堵塞掉交易池呢?如果真是有意爲之,爲什麼呢?

3. Hammerbot 交易導致內存池失真

我們的內存池存檔數據暗示,機器人成功地提高了堵塞情形,並扭曲了交易池中交易的 Gas 價格分佈,而且還沒有導致交易手續費的相應提高。

這樣做的淨效果就是交易卡殼率的提高和 Gas Price 報告服務扭曲,結果是交易池一場,使天平偏向了某些特定的交易 —— 即,提高了清算 CDP 倉位的交易以 0 價格成功競拍的幾率。

機器人用本來就無意提交上鍊的交易捶打(hammer)交易池。 這些 Hammerbot 通過發送置換率極高的交易(不相應提高 Gas 價格但又不斷重發)消耗掉了交易池的資源但是,交易池還有一種設計,是要求重發交易至少要提高 10% 的 Gas 價格,本身就是用來防止此類行爲的。那這些交易是如何實現置換的呢?

答案很簡單:異常高的交易丟棄率導致節點 “失憶”(見上文)。

Hammerbot 等待着 —— 或者僅僅是預估 —— 自己發出的交易從交易內存池中丟棄,然後立即用相同(甚至更低)的 Gas 價格重發交易。因爲節點 “忘記掉了” 之前被丟棄的交易,自然就盡職地把這些置換後的交易當成有效交易接了過來。當然,結果就是進一步的擁堵。

Hammerbot 用顯然是 “自動化” 的方式讓自己的交易變得畸形,每一次置換都包括了稍微更改過的合約輸出。因此,每一筆 Hammerbot 交易都有一個獨特的哈希值,可以繞過所有節點的點對點網絡協議中的泛濫攻擊過濾保護。

如下圖中重點標出的粉色線所示,從 UTC 時間 3 月 12 日 9 點開始,我們的交易池數據平臺發現根本不可能被打包的待打包交易數量急劇上升。35 分鐘後,此類不可能上鍊的交易產生的速度翻了一倍,在 10 點之後才降爲線性增長的模式。

- 從 UTC 時間 8 點到 10 點期間達到交易池的交易計數,以分鐘計。藍線表示最終上鍊了的交易計數;而橙線表示根本沒有上鍊的交易計數。-

即使這些交易似乎沒有上鍊的意圖,這些從未被打包的 Hammerbot 交易的 Gas Price 還是顯得太低了:在市場要求給出 30 Gwei 乃至更高價格時,這些交易幾乎總是隻給 5 Gwei。Hammerbot 交易既沒有像套利機器人經常做的那樣加速交易(從而炒高 Gas Price),又高效地消耗掉了節點的交易池資源。

從整體上來看,雖然進入交易池的交易數量急劇增加,交易池中還是有很大一個比例的交易 Gas Price 被人爲壓低了。

- 從 UTC 時間 8 點到 10 點間的交易 Gas Price 箱型圖(按分鐘劃分)。藍線表示最終上鍊的交易的情形。橙線表示從未上鍊過的交易的情形。-

拿出一筆 Hammerbot 交易作爲例子(我們用的是一筆 nonce 值爲 3070 的交易),可能有說明價值。這裏是最終的交易哈希值: 0x5b00c13020b82c9e8a098393564feca976dbbd2e8da6c54263f6e492be56fbfb

僅僅用你慣用的區塊瀏覽器檢視這筆交易並不能給出除了其區塊確認信息以外的洞見。表面上來看,這筆交易平平無奇。但 Blocknative 的交易池數據平臺檢測到了這筆交易上鍊之前使用這個 nonce 值的 418 筆獨特交易。這些交易都是在一個小時內出現的,也就是這些置換交易平均每 6.86 秒重發一次,而某些置換交易之間的時間差不超過 0.1 秒。

- 地址 0x5cf2fa4e0c84e71fd2e4fa86d2fa64b7a50a6fc0 從 UTC 時間 9 點開始在相連的 4 個 nonce 上發起的置換交易次數(按分鐘計)-

大多數置換交易都使用同樣高的 Gas Price,都是因爲節點的交易丟棄機制才作爲 “新” 交易成功進入到節點的交易池內。只有最後一筆想要上鍊的置換交易才需要給出更高的 Gas Price,然後被打包到區塊內。

- 地址 0x5cf2fa4e0c84e71fd2e4fa86d2fa64b7a50a6fc0 從 UTC 時間 9 點開始在相連的 4 個 nonce 上的平均 Gas Price(按分鐘作平均化)-

這種模式使我們很難說這些 Hammerbot 交易到底是有心還是無意的。實際上,我們的系統在一分鐘之內記錄下了超過 20 個從 UTC 時間 9:05 開始活躍的可能的 Hammerbot 地址:

地址 Nonce 一分鐘之內的置換交易筆數
0xdd3b6ae71ff420375fefaa2448046a37beeed800 9211 22
0x704bf43e578b2b912584495467c3a2210deaec11 7307 20
0x56f0bdbdf48556ed41248021fbc8027f69c41f27 5004 19
0x4a1351523071ed88d20afd1d10cda75d1b34f4e7 6644 19
0x2a7139e98f47c2fc65aec0de9a3adb8ffd46206a 10712 19
0x602bf7ba827a61d708614eb35284e79654c7e58f 8458 18
0xe6919901cef07c15373feac6871046848efd4212 2745 17
0x58500f55ac86a3022703806a7415cac321cce2a1 2587 16
0x6861d397f7ff510a1ab4bb60434d8a9c4dd01240 5837 16
0x5cf2fa4e0c84e71fd2e4fa86d2fa64b7a50a6fc0 3067 15
0x3a711e39640b802d201391d14d5f2d3159f07957 2279 14
0x4769ad67bce2779d1374675069a3b78b8dafbea6 2671 14
0x97e7ba1f2db224dac7e0a812a071474e7c60819a 9715 14
0x124f58b41fd43a498fe7041bc2d9d5813c4f80d7 2379 13
0x124f58b41fd43a498fe7041bc2d9d5813c4f80d7 2379 13
0x82b1e33c6465a9bedefd4af8a2c1cfc1c874bfbe 2516 13
0x3a711e39640b802d201391d14d5f2d3159f07957 2354 12
0x95227df275141c9bbf679f695668a838a31459fc 2435 12
0x5e4f7ce2607c39f4ec08355e2ea48e50f6f77bff 9208 12
0x3046a9743a1b8d967f2ddb014e341b0eca41c191 6193 10
0xb123a59c4e3ff44e57b3113234fa6ebe804e996d 2223 8
0xc1bfbc44536200b02f92aec4dee08ea390fa0535 2187 6
- 如果你是上述任一地址的運營者,我們很希望跟您聊聊。請聯繫我們 -

注意:這些特徵明顯的、使用同樣的 Gas Price 重發被丟棄交易的 Hammerbot 行爲,也可能跟最近大家討論的 “backrunning” 行爲有關。見 此處此處

交易池漏洞對 MakerDAO 的影響

MakerDAO 的 擔保債倉 (CDP)是用戶生成穩定幣 DAI 時託管被鎖定的擔保品 ETH 的智能合約。因爲 ETH 的價格有波動,而 DAI 希望能保持 1 美元的價格,所以維持一個開放的 CDP 所需的擔保品數量是不斷變動的。

當 3 月 12 日 ETH 價格暴跌時,大量的 CDP 立即變成了擔保不足的狀態,需要被 強制清算 。系統爲保證擔保品清算時可以獲得競爭性的市場價格,安排了折價機制:參與清算者可以以折價買到擔保品 ETH;這就使得許多人都願意運營多種多樣的 Keeper 機器人 。清算活動的表現形式爲 拍賣

任何 Keeper(看護者)都可以用 鏈上 出價參與拍賣,而一個看護者出價之後,另一個看護者想要與之競爭必須在 10 分鐘之內發送競拍價更高的交易。每當有新的競拍交易到達,都會刷新 10 分鐘的競拍窗口期。如果 10 分鐘之內系統沒有收到更高的出價,則拍賣結束,最高出價者勝出。 [注意:在 “黑色星期四” 下午,MakerDAO 把競拍窗口期從 10 分鐘提高到了 6 小時。]

但是,當礦工節點因爲網絡喲難度和交易池壓縮而以異常高的比例丟棄交易時,許多看護者的交易都被卡住了,因此根本沒能及時讓清算 CDP 的競拍交易成功上鍊。結果就是看護者們無法在由 0 價格競拍交易開啓的窗口中可靠地開展競爭,即使看護機器人正確地發現了 0 價格拍賣併發送出了更高出價的交易也沒用。

最終,因爲擁堵和實際手續費的混淆,看護者無法恰當地解讀出 Gas Price 的實際上漲幅度,看護者的交易也因此被節點丟棄。接下來是產生 nonce 空缺、交易在使用默認交易池設置的節點處卡殼。 遭遇此種情形的看護者機器人爲其他拍賣發出的所有後續競拍交易會全部被卡住,讓該看護者根本無法在 10 分鐘的競拍窗口內參與交易,最終讓 0 價格競拍交易得逞

下面我們用 1866 號拍賣作爲例子來說明上述過程:

  1. 一個 “0 價格競拍機器人” 在 UTC 時間 15:59:50 時以 200 Gwei 的價格發送一筆 0 價格競拍交易。該交易在 26 秒後成功上鍊,10 分鐘倒計時就此開始。
  2. 一個 “誠信看護者機器人” 在 16:08:01 時以 450 Gwei 的 Gas Price 發送一筆競價交易。這筆交易是在 10 分鐘內發出的 —— 之隔了 8 分 11 秒 —— 而且 Gas 價格還是最初那筆 0 價格競拍交易的 2.5 倍。如果能在接下來的 1 分 49 秒內被打包到區塊中,1866 號拍賣就會像大家預期的那樣進行。但是,這筆競拍交易因爲這個好機器人之前發送的一筆交易被丟棄(也就是產生了 nonce 空缺)而被卡住了。
  3. 這個好機器人在 16:15:31 又發出一筆 4500 Gwei 的競價交易。但是哪怕 Gas Price 提高了 10 倍也無濟於事,因爲這個地址被 nonce 空缺鎖死了。再然後,等到競價交易不再被卡的時候,已經過了 10 分鐘的窗口期,交易自然就失敗了。

上述模式可能重複了一整天,最終導致總計 832 萬美元的擔保品被 0 價格拍走。值得指出的是,大多數 Hammerbot 的活動似乎都在當天較早的時候發生,這樣就造成了後來影響到 Keeper 機器人交易的擁堵情況。

Gas Price 的迅速上升、導致 Gas 價格估計失靈的交易池壓縮,同樣對其它價格信息傳輸機制造成了負面影響。這導致了定價方面的混亂,也有可能使得面臨清算風險的 CDP 持有者推遲了添加擔保品的決定。

總結

總結一下,我們對 “黑色星期四” 交易池狀況的事後檢驗暗示了下述情況:

  1. 多種 Hammerbot 的活動導致挖礦節點的交易池飽和。這推高了以太坊網絡的擁堵程度並使之不斷上升。
  2. 發送許多以同樣的 Gas Price 發出的置換交易在點對點 gossip 層和節點本身產生了巨大的開銷。
  3. 正常交易的傳播受到阻礙,大量的交易被挖礦節點丟棄或者拒絕。
  4. 這又反過來提高了出現 nonce 空缺和卡殼交易的幾率,還扭曲了對 Gas Price 的估計。
  5. 雖然自動化的交易系統通常被設計成會自動提高交易的 Gas Price,許多交易系統並不能很好地處理 nonce 空缺問題 —— 甚至是完全束手無策。

以上就是我們在研究過程中的發現。我們的數據集可能還能揭示出更多的證據 以及/或者 新的發現來支持或證否我們的結論。

爲加速這個發現過程,我們現在把那兩天的交易池存檔數據開放給社區,我們希望這能促進對交易池在 “黑色星期四” 及以太坊網絡其它類似事件中所扮演的角色的研究。

建議:保護好你自己,以及你的用戶

交易池是區塊鏈生態系統非常關鍵的一環,雖然它瞬息萬變,常常被忽略。因此,交易池中藏着很多開發者和用戶 “完全無知的未知之物”。

現在,我們並不知道有多少人在開發這樣的利用交易池弱點的技術 —— 只是顯然有人在利用交易池的特點。我們也不知道有多少這樣的弱點存在 —— 只是那些複雜的利用方法似乎已經在現實中證明了其有效性。

因此,我們建議所有交易、所有協議、所有錢包供應商和交易員:

  1. 持續監控交易池的情況 ,注意(1)發現交易池開始變得擁堵的時機;(2)交易丟棄率和拒絕率的變化。
  2. 理解交易 nonce 排序的細微差別 。即使合理構造的交易也可能被卡在網絡的某個角落。
  3. 主動觀察被卡殼的交易 ,並且要知道加速先前的哪一筆交易能使發送交易的地址脫困。
  4. 基於交易池中對礦工有吸引力的部分來計算 Gas Price 。還需要知道你所依賴的 Gas Price 報告服務所用的算法。
  5. 在高度擁堵期間,不要假設交易待打包的情形是可以預測的 。先防範,你要監控每一筆交易,瞭解每一個細節,包括 Gas 是否充分,交易是不是被丟棄、被卡殼,會不會被人搶跑(front-running),等等。

這些問題,如果你自己動手解決,是非常有挑戰性的、很耗時而且很昂貴的。Blocknative 的全球數據平臺一直在往捕捉和規範化去中心化交易池的方向優化 —— 也就是一直在實現上述的五個建議。 我們的基礎設施和 API 提供了實時的交易池監控服務,保證你的交易能可靠、靈活、可預測地流動

……

非常感謝評議本文初稿的各位,包括: Sarah Baker-MillsDmitriy BerenzonSpencer BogartNic CarterHsin-Ju ChuangTomasz Drwięga , Andy GrayHudson JamesonJon KolCalvin LiuJustin MartGavin McDermottTaylor MonahanAndra NicolauCharlie NoyesSimona PopAlex PrudenAustin RobertsCuy SheffieldLarry SukernikChris Whinfrey ,等等。我們非常感謝你們的反饋、洞見和指導。

(完)

原文鏈接: https://blog.blocknative.com/blog/mempool-forensics

作者:Blocknative

翻譯:阿劍

相關文章