觀點:務實地取消 SELFDESTRUCT
本文將介紹 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
有兩類重要的應用:
GasToken:當 gas 價格低時通過創建合約用掉 gas,當 gas 價格高時通過調用
SELFDESTRUCT
獲得 gas 退款(對於幾乎不佔用空間的合約來說,可以退回大約 60% 的創建費用)。利用 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
翻譯&校對: 戡亂 & 阿劍
鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播信息之目的,不構成任何投資建議,如有侵權行為,請第一時間聯絡我們修改或刪除,多謝。
7月23:Mt. Gox 比特幣錢包在市場緊縮的情況下轉移了價值 28.2 億美元的 BTC
7月23:Mt. Gox 比特幣錢包在市場緊縮的情況下轉移了價值 28.2 億美元的 BTC一個引...
悅盈:比特幣68000的空完美落地反彈繼續看跌 以太坊破前高看回撤
一個人的自律中,藏着無限的可能性,你自律的程度,決定着你人生的高度。 人生沒有近路可走,但你走的每...