真實存在的以太坊 DeFi「黑暗森林」

買賣虛擬貨幣

撰文:Dan Robinson 與 Paradigm 研究合夥人 Georgios Konstantopoulos,專注於區塊鏈可擴充套件性、資訊保安的獨立研究員
編譯:Perry Wang

這是一個恐怖故事。

挑戰

像普通人一樣,我在 Uniswap Discord 的#support 頻道中花費了大量時間。 (資訊披露:Uniswap 是 Paradigm 參與投資的公司之一。)

週三下午,有人提問:如果把代幣誤傳送給對智慧合約本身的代幣交易對,是否有可能把代幣拿回來?

我最初的想法是這些代幣將永遠被鎖定。但是那天深夜,我突然意識到,如果代幣仍然在那兒,它們可能會被拿走——被任何人拿走。

當任何人在 Uniswap 核心合同上呼叫焚燬 burn 功能時,該合同將衡量自身的流動性代幣餘額並進行 burn ,將提取的代幣返給呼叫方指定的地址。這是 Uniswap v2 預期行為的核心部分(基本機制在 Uniswap v2 白皮書的第 3 章 第 2 節中進行了描述)。

我找到了該合同。流動性代幣仍然存在,價值約 12,000 美元。

這意味著三件事:

  • 存在一個滴答作響的時鐘。即使沒有其他人注意到這筆免費的資金,任何人都可以隨時撤走自己的流動資金,意外地從合同中收到代幣。
  • 我可以扮演白帽駭客,嘗試為將代幣誤傳送到 Uniswap 合約的人找回代幣。非常簡單,只需要在資金池中呼叫 burn 函式,並將代幣傳遞到自己的地址。
  • 除非……我知道它不是那麼簡單。

黑暗森林

眾所周知以太坊區塊鏈是一個危機四伏的環境。如果能透過抓住某一智慧合約的漏洞牟利,總會有人這麼幹的。新駭客攻擊的頻率表明,一些非常聰明的人花費大量時間來尋找有漏洞的合約。

但是與記憶體池 mempool (待處理、未確認的交易集)相比,這種暗藏殺機的環境不值一提。如果這條區塊鏈本身是戰場,那麼 mempool 更糟糕:那就是一片黑暗森林。

《黑暗森林》The Dark Forest 是我個人最喜歡的科幻小說。正是因為這本書而有了「黑暗森林」這一概念——在這環境中高階掠食者不斷製造殺戮。在這種環境中,暴露某人的藏身之處無異於直接毀掉他 / 她。(這一概念也是以太坊測試網中黑暗森林 Dark Forest 遊戲的靈感來源。)

在以太坊 mempool 中,這些超級掠食者是以「套利機器人」形態存在。套利機器人監視著待處理交易,試圖從這些交易造成的每個牟利機會中敲骨吸髓。最深諳這些機器人的白帽駭客是 Phil Daian,這位智慧合約研究者與其同事撰寫了 Flash Boys 2.0 論文,創造了「礦工可提取價值」(MEV)這一概念。

Phil 曾對我描述過一個宇宙級的恐怖殺手,他稱其為「廣義搶跑者」。套利機器人通常會在 mempool 中查詢特定型別的交易(例如去中心化交易所 DEX 的交易或預言機更新),並嘗試根據預定演算法搶先截胡。廣義搶跑者尋找能夠從中賺錢的任何交易,迅速複製其交易並用自己的地址替換原交易中的地址。它們甚至可以執行交易並複製由其執行軌跡生成的有利可圖的內部交易。

這就是為什麼我說要救回上述那筆誤傳送的資金並非如此簡單的原因。任何人都可以呼叫這一 burn  功能。如果我提交了一項 burn  交易,那就像是霓虹燈般的「免費資金」標誌,直接向外界宣告了這個獲利機會。如果這些恐怖殺手確實在 mempool 中,就會看到這一交易,迅速複製,移花接木並接管我的交易,在我的交易之前拿走了錢。

請注意,這種環境比以太坊區塊鏈狀態本身還要殘酷得多。這些免費資金已經在以太坊區塊鏈上呆了大約八個小時而未被發現,靜待某個 burn  者席捲。但是任何試圖拿走這筆錢的嘗試都會迅速在空中被準確狙擊。

拯救

要想不打草驚蛇、不驚動機器人的情況下提取資金,我需要對交易進行瞞天過海的處理,以便機器人無法檢測到對 Uniswap 交易對的呼叫。這涉及編碼和部署定製合約。我是 DeFi 領域專業的思想領袖,我以前從未向以太坊實際部署任何合約。

我需要援手,而當時美國時間已經過了午夜。幸運的是,我認識的一些最好的智慧合約工程師住在歐洲時區。我的 Paradigm 同事 Georgios Konstantopoulos 同意協助部署合約和提交交易。我們投資的另一家公司 Yield 的首席工程師 Alberto CuestaCañada 自告奮勇執行合約。

一些優秀的以太坊安全工程師幫助我們制定了一個瞞天過海的計劃。除了將呼叫隱藏為內部交易之外,我們還將交易分為兩部分:一個啟用我們合約的 set 交易,以及一個拯救啟用(如果合約已啟用)資金的 get 交易。以如下路徑實現:

  1. 部署一個 Getter 合約,由其主人呼叫,只有在啟用後才會做出 burn 呼叫,否則恢復原狀。
  2. 部署一個 Setter 合約,由其主人呼叫,將啟用 Getter 合約。
  3. 在同一區塊提交 set 交易和 get 交易。

我們智慧合約的程式碼

如果攻擊者只試圖執行這一 get 交易,就會在不呼叫 burn 功能的情況下讓合約恢復原狀。我們原本希望的是在攻擊者先後執行 set 和 get 交易,發現內部呼叫 pool.burn 的指令,然後試圖對我們超車時,我們已經完成了交易。

我們拯救這筆錢的程式碼指令碼:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#!/usr/bin/env node

const ethers = require()

const WHITEHAT = [
    "function get()"
]
const SETTER = [
    "function set(address whiteHat, bool on)"
];

(async () => {
    const provider = new ethers.providers.JsonRpcProvider("http://localhost:8545");

    const dest = process.env.DEST
    console.log("Dest balance before:", await provider.getBalance(dest))

    const whitehatAddress = process.env.WHITEHAT

    const gasPrice1 = 160 * 1e9
    // 20% higher
    const gasPrice2 = gasPrice1 * 1.2

    // call Whitehat.set(on) indirectly via the setter contract by the Setter account
    const setterWallet = new ethers.Wallet(process.env.SETTER_KEY)
    const setterClient = setterWallet.connect(provider);
    const setter = new ethers.Contract(process.env.SETTER, SETTER, setterClient)
    const tx1 = await setter.set(whitehatAddress, true, { gasPrice: gasPrice2 })
    console.log("Submitted",  tx1);

    // call whitehat.get by the Getter account
    const getterWallet = new ethers.Wallet(process.env.GETTER_KEY)
    const whitehatClient = getterWallet.connect(provider);
    const whitehat = new ethers.Contract(whitehatAddress, WHITEHAT, whitehatClient)
    const tx2 = await whitehat.get({ gasLimit: 2e6, gasPrice: gasPrice1 })

    console.log(await tx1.wait())
    console.log(await tx2.wait())

    console.log("Dest balance after:", await provider.getBalance(dest))
})()

但出乎我們意料的是,這個 get 交易會被 Infura 拒絕,即使我們手動覆蓋 Gas 估算器也是如此。經過多次失敗的嘗試後和重新設定後,我們越來越感到時間壓力,抱著一絲僥倖心理,我們讓第二個交易滑進之後的區塊。

這成了一個致命的錯誤。

我們的 get 交易確實被收錄進了較早的區塊,但一個 UniswapV2: INSUFFICIENT_LIQUIDITY_BURNED 錯誤,意味著其在 Uniswap 的流動性消失了。結果我們的 get 交易剛剛進入 mempool 幾秒鐘,就有人執行了呼叫,捲走了這筆錢。黑暗森林裡的怪獸還是吞噬了我們。

教訓

真實存在的怪獸

之前我們切實知道廣義搶跑者機器人的存在。但在看到它們真正出手之前,大家很可能低估了它們。

我們的希望是,透過一個授權合約、利用內部呼叫來完成拯救這筆資金的任務,從儲存傳遞一個變數作為目標地址, 也許會保護我們,但實際上並沒有起到作用。

如果你切實陷入了這種困境,我建議你去找 Amberdata 工程副總裁 Scott Bigelow,他是一直研究這一課題的安全研究人員,有一套能更好實現瞞天過海目的的原型實施策略。

不要心存僥倖

即使面臨時間壓力,我們當時也應該堅持原計劃。如果我們在程式碼指令碼上花更多時間,調整合約(也許將 Getter 合約更改為不執行任何操作,而不是在啟用之前被呼叫就恢復原狀),或者甚至同步自己的節點以避免使用 Infura,我們可能能夠讓該交易進入同一區塊。

不要依賴常規基礎架構

你的招數越奇怪,越難透過 Infura 這樣的現有基礎架構阻塞它。在我們的案例中,我們試圖基於當前的區塊鏈狀態提交看起來像將失敗的交易,Infura 有合理的保護措施阻止它。使用我們自己的節點可能會避免此問題。
更妙的是,如果你碰巧認識一個礦工(我們不認識),則可以讓他們直接將交易直接打包在一個區塊中,從而完全跳過 mempool,自然也就避開了吃人怪獸。

未來只會更恐怖

這只是一個搶先交易的一個例子。每天發生無數次類似的事情。今天的搶先交易者只是機器人。明天可能會是礦工。

礦工們目前沒有對這些機會下手,把錢留在了賭桌上。將來他們可能會為了自己的利益而在 mempool 中重新排序並提交交易。更糟糕的是,他們可能會重組其他礦工挖出的區塊,以試圖竊取不屬於自己的 MEV,從而導致區塊鏈的不穩定。

我們認為這種恐怖的未來是可以被預防的。Optimism (Paradigm 投資的另一家公司)對於 MEV 如何被重新引導以維護生態系統的利益有著雄心勃勃的構想,這是他們 Layer 2 擴容解決方案 Optimistic Rollup 的一部分。

免責聲明:

  1. 本文版權歸原作者所有,僅代表作者本人觀點,不代表鏈報觀點或立場。
  2. 如發現文章、圖片等侵權行爲,侵權責任將由作者本人承擔。
  3. 鏈報僅提供相關項目信息,不構成任何投資建議

推荐阅读

;