觀點:務實地取消 SELFDESTRUCT

2021-03-19 15:03:30

本文將介紹 SELFDESTRUCT 對以太坊生態弊大於利的一些理由,正是因為這些理由,我們應該以某種方式移除 SELFDESTRUCT 。鑑於有些合約已經使用了 SELFDESTRUCT ,我提出了一些只需要付出最小的代價就能消除 SELFDESTRUCT 危害的方法。

一段歷史: SELFDESTRUCT 已經沒有必要了

SELFDESTRUCT (最初叫作 SUICIDE )早在以太坊的極早期便已引入。實際上,它在 2013 年 12 月發布的以太坊協議 “規範” 預告中就已經出現了。那時候,幾乎沒人仔細考慮過狀態規模管理的長遠問題。但是,有個想法我大概還有些印象,為了防止沒用的垃圾狀態不受限制地膨脹,我們需要讓任何創建出來的對象都可以被銷毀。具體的思路是,當外部账戶(Externally-owned accounts, EOAs)的余額為零時觸發自毀,而合約在沒用後可以調用代碼裏的一行自毀語句觸發自毀。還有一個 gas 退款機制用於激勵大家銷毀沒用的狀態。

2014 年 1 月,Andrew Miller 指出了一個非常嚴重的問題:在 2013 年 12 月的規範設計中,EOA 很容易被重放攻擊。如果我有 100 個幣,我通過一筆交易發給你 10 個幣,你可以簡單地在鏈上重放這筆交易十次,從而轉走我的全部余額。這個問題很快就修復了,為此我們增加了 nonce 字段。然而,nonce 字段的引入讓刪除 EOA 的愿望徹底破滅了:nonce 是不能被重置為零的(譯者注:因為以太坊的狀態樹是根據账戶地址計算的一種前綴樹,(如果仍然允許 EOA 的 nonce 回退為 0)一旦該账戶被再次使用,nonce 又要從零开始,就會被重放攻擊)。

2015 年,有人提出了一些方案試圖繞過這個問題,使余額為零的账戶可以被安全地刪除(譯者注:nonce 重置時與區塊高度關聯,而非從零开始)。然而,當時很明顯,幾乎沒有合約开發者真正使用自毀功能:因為要弄清楚什么時候自毀太難了,而獎勵也太少了。

到 2019-21 年,事情已經變得很明顯了,我們需要的是其他形式的狀態管理,比如租金機制或者是長期未動的狀態 “到期作廢(expiring)” (即 “部分無狀態(partial statelessness)”)。而如果我們採用這兩個方案中的任何一個,只要它是有效的,那么合約是否有能力主動刪除自己就一點兒也不重要了。

SELFDESTRUCT 是唯一一個破壞重要恆常性質(invariant)的操作碼

SELFDESTRUCT 不僅沒什么用,還會產生危害。它破壞了一些重要的恆常性質,這些性質本來是很好的,但是僅僅因為這一個操作碼,我們就失去了這些性質。

SELFDESTRUCT 是唯一一個能在單個區塊中變更無限個狀態對象的操作碼

其他所有的操作碼都只能操作账戶中的單個值或者存儲樹上的單個 key,所以它們能變更多少固定大小的對象是有限制的(通常,調用一個操作碼只能變更一個對象)。但是,SELFDESTRUCT 可以刪除整棵存儲樹。

在目前的狀態樹結構中,這是可以容忍的。但是,考慮一種特殊的情況:當調用 SELFDESTRUCT 刪除許多存儲插槽後,下一個事務又在同一個地址上創建一個合約並訪問同一些存儲槽。為了處理這種情況,需要額外設計復雜的緩存機制。此外,SELFDESTRUCT 還阻礙了我們變更狀態存儲格式。

以 SELFDESTRUCT 會阻礙的兩類狀態存儲格式為例:

  • 任意的 “單層” 方案(使用單棵樹或者單個 hashmap 來存儲所有合約账戶的數據,以此代替目前的每個合約账戶都有一棵存儲樹的設計)

  • 存儲槽可以存儲在一些地址 “附近”,而不是存儲在合約裏的方案(這可能對優化見證大小(witness size)有用,比如在 ERC20 轉账或 Uniswap 交易的場景下)

請注意,這不是在空想,從根本上變更狀態存儲格式(如採用二進制樹、Verkle 樹等)的討論已經开始了,如果狀態存儲的數據結構能夠接近單一的的鍵/值存儲結構,並且單個區塊中可以變更的狀態數量有一個較低的上限,那將大大擴展我們的選擇空間。

SELFDESTRUCT 是唯一一個會導致合約代碼變動的操作碼

如果在一個特定的地址上存儲了一段代碼,那么這段代碼就會永遠保留在鏈上。這樣的恆常性質是有用的,因為在構建應用時不需要擔心這些代碼會出現變動。

账戶抽象化(Account abstraction)非常依賴該恆常性質用以支持庫調用。因為代碼存在變動的可能,還會導致應用的安全性變得復雜很多:2017 年 Parity 的多籤錢包就曾因為其引用的庫代碼合約被偶然刪除而徹底癱瘓。

而唯一破壞代碼不變性的操作碼就是 SELFDESTRUCT (是造成 Parity 多籤猝死的罪魁禍首)。

SELFDESTRUCT 是唯一一個可以未經账戶同意就能修改账戶余額的操作碼

SELFDESTRUCT 有一個內置的 “轉账” 的功能,其並不走正常的轉账流程,因而可以繞過避免合約地址接收 Ether 的守護功能,以及對轉账事件的日志記錄。這為智能合約錢包埋下了隱患,讓一些潛在有用的技巧沒法使用,加重了开發者和審計者的心智負擔(需要考慮更多的例外條件)。

SELFDESTRUCT 當前的用例

如今 SELFDESTRUCT 有兩類重要的應用:

  1. GasToken:當 gas 價格低時通過創建合約用掉 gas,當 gas 價格高時通過調用 SELFDESTRUCT 獲得 gas 退款(對於幾乎不佔用空間的合約來說,可以退回大約 60% 的創建費用)。

  2. 利用 SELFDESTRUCT 實現代碼的動態變更:這可用於 dApp 或 DAO 及其他類似用例的 “升級”。

(1)可以被安全地銷毀。GasToken 的开發者已經發出了警告 “雖然對以太坊網絡的變更會導致 GasToken 無法使用、不可贖回、不能互換以及/或毫無價值,但是 GasToken 的开發者極可能會擁護該變更”。移除 selfdestruct 退款只會導致有些操作的費用變得更貴(2 倍以上)。

從長遠來看,(2)是沒必要的,還有其他一些被廣泛使用的範式可用於支持動態代碼變更。最容易實現的是 DELEGATECALL 轉發器,合約從一個存儲插槽中獲取一個代碼地址,然後調用對應地址的代碼;修改這個存儲插槽就能更新代碼。不過,從短期來看,有少數應用已經使用了(2)。

提案 1:完全移除 SELFDESTRUCT

從某個區塊(用 FLAG_BLOCK 表示,比如取 PoW 鏈與信標鏈合並發生的那個區塊)开始,完全停用 SELFDESTRUCT 。在這個及之後的區塊裏,如果 EVM 在執行時遇到 0xff 操作碼,只要拋出異常直接退出即可,就像 EVM 執行時遇到不存在的操作碼一樣。

在完全停用前,為了警示用戶避免使用 SELFDESTRUCT ,我們可以漸進式地增加其 gas 費用:如果 block.number + 10**6 >= FLAG_BLOCK ,則 SELFDESTRUCT 的 gas 費用增加到 10**10 // (FLAG_BLOCK - block.number) 。

提案 2:閹割 SELFDESTRUCT

我們也可以保留這個操作碼,但是改變其行為,一方面消除其對狀態樹的破壞,另一方面增加一個新特性,讓合約可以標識為不可自毀(un-self-destructible),從而確保代碼不可變。

暫時提議新增的行為包括:

  • 當一個合約調用 SELFDESTRUCT 時,並不會刪除合約账戶,而是清空代碼,並且將 nonce 值增加 2**40 。沒有退款。

  • 通過調用(轉账)將合約中的 ETH 轉移到目標地址(要么由父調用提供所需的全部 gas,要么父調用並不提供任何 gas)。

  • 可以在代碼為空的地址(比如被清空過的)上創建合約。

  • 在合約裏調用 SSTORE 和 SLOAD 操作地址 A 時,實際操作的是 A_offset = (A + A.nonce // 2**40) % 2**160 的存儲樹。

注意,從 EIP-2929 的角度來看, A_offset 需要 “可達(accessed)”。如果該账戶不在可達账戶集合中,則需要額外支付 2600 gas 以加入可達集合。

另一種選擇是調整將 storage key 轉換為 tree key 的哈希函數,用 sha3(storage_key + contract_nonce // 2**40) 代替 sha3(storage_key) 。需要注意的是,無論如何都需要做一些類似的調整,以方便合約級別的無狀態 key 空間擴展(expanding-key-space statelessness)。

合約可以在代碼中指定 0xA8 作為第一個字節,EVM 會將其識別為無操作,但使用它來开啓一個標志,在執行過程中完全禁用 SELFDESTRUCT 的功能(注意:這與 SET_INDESTRUCTIBLE 提案是一樣的)。

這兩種解決方案也可以結合起來:當前立即閹割,將來完全移除。或者,這個操作碼也可以永遠不被完全移除,但是最終只保留一個功能,即向目標地址發送合約當前的全部 ETH 余額,我們可以將這個操作碼重命名為 CLEAR 。

原文鏈接:

https://hackmd.io/@HWeNw8hNRimMm2m2GH56Cw/selfdestruct

作者:  Vitalik

翻譯&校對: 戡亂 & 阿劍

鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播信息之目的,不構成任何投資建議,如有侵權行為,請第一時間聯絡我們修改或刪除,多謝。

推薦文章

btc日內再次下跌 短线應當如何處理?

盡管以太坊現貨ETF獲批是個好消息,但市場反應卻不如預期。在消息公布後,以太坊價格出現了小幅下跌,...

加密蓮
107 3個月前

7月23日、BTC(合約)ETH(合約)行情分析及操作策略

昨日收益還是不錯的,日內給出的現價空單分別止盈我們目標點位,恭喜跟上的朋友喫肉。時間一晃到月底了,...

倪老師
107 3個月前

幣圈院士:血與淚的教訓!交易者為何總是撞死在同一棵樹上?

幣圈院士談。交易市場中的幾種“死法” 在幣圈市場鱗次櫛比的海洋,風起雲湧,時常讓人感到驚手不及。在...

幣圈院士
111 3個月前

7月23:Mt. Gox 比特幣錢包在市場緊縮的情況下轉移了價值 28.2 億美元的 BTC

7月23:Mt. Gox 比特幣錢包在市場緊縮的情況下轉移了價值 28.2 億美元的 BTC一個引...

168超神
106 3個月前

悅盈:比特幣68000的空完美落地反彈繼續看跌 以太坊破前高看回撤

一個人的自律中,藏着無限的可能性,你自律的程度,決定着你人生的高度。 人生沒有近路可走,但你走的每...

我是周悅盈
88 3個月前

btc完美盈利 晚間波動較大注意

昨日btc空單完美給到,最大化走出一千七百點空間~ btc: 日內开盤下跌繼續測試66000一线,...

加密蓮
95 3個月前