升級智慧型合約
最後編輯: @nhsz(opens in a new tab), 2023年8月15日
以太坊上的智慧型合約是在以太坊虛擬機器 (EVM) 中運行的自動執行程式。 這些程式在設計上是不可變的,一旦部署合約,就可以防止對商業邏輯進行任何更新。
儘管不可變性在智慧型合約的去信任化、去中心化和安全中是必要的,卻可能在一些案例中變成缺點。 舉例來說,不可變的程式碼讓開發者無法修復有漏洞的合約。
但是,在越來越多改進智慧型合約的研究中,引入了幾種升級合約的模式。 這些升級模式讓開發者可以透過把商業邏輯置於不同的合約,對智慧型合約進行升級(在保持不可變的前提之下)。
先決條件
你需要對智慧型合約、智慧型合約結構和以太坊虛擬機 (EVM) 有一定程度的了解。 本指南還假設讀者了解智慧型合約的程式設計。
什麼是智慧型合約升級?
智慧型合約升級需要在變更智慧型合約商業邏輯的同時保留合約的狀態。 分清楚可升級性與可變性是兩回事,這點很重要,尤其在智慧型合約背景下。
你依然無法改變已部署在以太坊網路上某個地址的程式, 但是可以改變使用者與智慧型合約互動時所執行的程式碼。
可以透過以下幾個方法做到:
建立多個版本的智慧型合約以及從舊合約遷移狀態(例如:資料)到新的合約執行個體。
建立單獨的合約來儲存商業邏輯和狀態。
使用代理模式從不可變代理合約委派函式呼叫到可以修改的邏輯合約。
建立一個不可變的主合約作為介面並依靠有彈性的衛星合約來執行特定函式。
使用鑽石模式從代理合約委派函式呼叫到邏輯合約。
升級機制 #1:合約遷移
合約遷移是建立在版本控制上 — 想法來自在相同的軟體上建立和管理唯一的狀態。 合約轉移需要部署現有合約的新執行個體,並將存儲和餘額轉移到新合約。
新部署的合約存儲將為空,允許你從舊合約恢復資料並寫入新的實作。 此後,你需要更新所有與舊合約有互動的合約以反映新的地址。
合約遷移的最後一步是說服使用者轉去使用新合約。 新的合約版本將保留使用者餘額和地址,從而保持不可變性。 如果是基於代幣的合約,你還需要聯繫交易所放棄舊合約並使用新合約。
合約遷徙是一個相對簡單且安全的智慧型合約升級方式,不會影響到使用者的互動。 但是,手動遷徙使用者的存儲和餘額到新合約非常花時間而且需要高燃料費用。
關於合約遷徙的更多資訊。(opens in a new tab)
升級機制 #2:資料分離
另一種升級智慧型合約的方式為,將商業邏輯與資料儲存分開到不同的合約。 這代表使用者與邏輯合約互動,同時資料儲存在存儲合約中。
邏輯合約包含當使用者與應用程式互動時執行的程式碼, 還會保存存儲合約的地址,並與之互動以取得並設定資料。
同時,存儲合約會保存與智慧型合約相關的狀態,例如使用者餘額和地址。 請注意,存儲合約歸邏輯合約所有,並在部署時配置邏輯合約的地址。 這可以防止未經授權的合約呼叫存儲合約或更新其資料。
預設情況下,存儲合約是不可變的 — 但可以將它指向的邏輯合約替換為新的實作。 這樣可以變更在以太坊虛擬機上執行的程式碼,同時原封不動地保有存儲和餘額。
使用這種升級方法需要更新存儲合約中的邏輯合約地址, 還必須在新的邏輯合約上設定存儲合約的地址,理由稍後說明。
資料分離模式可以說是相較於合約遷移更簡單的實作方法。 然而,你將必須管理多個合約並實作複雜的授權方案,以保護智慧型合約不受惡意升級影響。
升級機制 #3:代理模式
代理模式也會使用資料分離來將商業邏輯和資料保存在單獨的合約中。 然而,在代理模式下,存儲合約(或稱代理)會在執行程式碼時呼叫邏輯合約。 這是資料分離模式的反向過程,這裡的邏輯合約會呼叫存儲合約。
代理模式的流程如下:
使用者與儲存資料的代理合約互動,但這個合約上沒有保有商業邏輯。
代理合約儲存邏輯合約的地址,並使用
delegatecall
函式將所有函式呼叫委派到邏輯合約(該合約擁有商業邏輯)。在呼叫轉送到邏輯合約後,從邏輯合約回傳的資料會被擷取並回傳給使用者。
使用代理模式需要了解 delegatecall 函式。 簡單來講,delegatecall
是允許合約呼叫另一個合約的操作碼,而實際的程式碼執行發生在呼叫合約的背景下。 在代理模式中使用 delegatecall
的含義是,代理合約讀取儲存在邏輯合約中的邏輯,寫入其存儲並執行,就像呼叫一個内部函式一樣。
根據 Solidity 文件(opens in a new tab):
存在一個訊息呼叫的特殊變體,稱爲 delegatecall,除了目標地址中的程式碼是在呼叫合約的背景下執行,並且
msg.sende
和msg.value
不會改變它們的值之外,該變體與訊息呼叫相同。__這意味著合約可以在運行時從另一個地址動態載入程式碼。 存儲、目前的地址和餘額仍然是指呼叫合約,只有程式碼來自被呼叫的地址。
代理合約知道在使用者一呼叫函式時就叫用 delegatecall
,因為它有一個內建的 fallback
函式。 在 Solidity 編程中,當函式呼叫與合約中指定的函式不相符時,便會執行遞補函式(opens in a new tab)。
要使代理模式正常運作,需要編寫一個自訂遞補函式,指定代理合約應該如何處理其不支援的函式呼叫。 在這種情況下,代理的遞補函式編寫為啟動 delegatecall 並將使用者的要求重新路由到目前的邏輯合約實作。
預設情況下,代理合約是不可變的,但可以建立具有更新的商業邏輯的新邏輯合約。 之後,執行升級不過是在變更代理合約中援引的邏輯合約地址。
透過將代理合約指向新的邏輯合約,使用者呼叫代理合約函式時執行的程式碼發生了改變。 這樣我們在升級合約邏輯時,就不用要求使用者與新的合約互動。
代理模式是一種升級智慧型合約的熱門方法,因爲其消除了與合約遷移相關的難題。 然而,代理合約使用起來更加複雜,使用不當時可能帶來重大缺陷,如函式選擇器崩潰(opens in a new tab)。
關於代理模式的更多資訊(opens in a new tab)
升級機制 #4:策略模式
該技術受到策略模式(opens in a new tab)的影響,這種模式鼓勵建立與其他程式介接的軟體程式以實作特定功能。 在以太坊開發中套用策略模式是指,建立一個智慧型合約來呼叫其他合約的函式。
在這種情況下,主要合約包含核心商業邏輯,但會與其他智慧型合約(「衛星合約」)介接以執行特定函式。 此主要合約也儲存了各衛星合約的地址,可以在不同的衛星合約實作之間切換。
你可以建立一個新的衛星合約並在主要合約上設定新的地址, 讓你可以更換智慧型合約的策略(即,實作新的邏輯)。
儘管與先前討論的代理模式類似,但策略模式有所不同,因為與使用者互動的主要合約保有商業邏輯。 使用這個模式可為你提供一個對智慧型合約引入有限變更而不影響核心基礎設施的機會。
主要缺點是,這種模式只適合發佈小幅度的升級。 另外,當主要合約受到波及(例如,透過駭客入侵)時,便無法使用這種升級方法。
升級機制 #5:鑽石模式
鑽石模式可視為改良版的代理模式。 鑽石模式與代理模式不同的是,鑽石代理合約可以委派函式呼叫到不只一個邏輯合約。
在鑽石模式中,邏輯合約被稱為切面。 為了使鑽石模式發揮作用,需要在代理合約中建立對應,將函式選擇器(opens in a new tab)對應到不同切面的地址。
當使用者進行函式呼叫時,代理合約會檢查該對應,找到負責執行該函式的切面。 之後,它會叫用 delegatecall
(使用遞補函式),並將呼叫重新導向至適合的邏輯合約。
鑽石升級模式有幾個優於傳統代理升級模式的好處:
允許升級合約的一小部分,而不用更改所有程式碼。 使用代理模式升級時,即便是很小幅度的升級,仍需要建立全新的邏輯合約。
所有智慧型合約(包括代理模式中使用的邏輯合約)都有 24KB 的大小限制,尤其對於需要更多函式的複雜合約而言,可能成為一個限制。 鑽石模式藉由拆分函式到多個邏輯合約,輕易解決了這個問題。
代理模式採用了包羅萬象的存取控制方法。 有升級函式存取權限的實體可以更改整個合約。 但鑽石模式啟用了模組化權限方法,可以限制實體僅升級智慧型合約中的特定函式。
關於鑽石模式的更多資訊(opens in a new tab)
升級智慧型合約的利弊
優勢 | 劣勢 |
---|---|
智慧型合約升級可以更輕易地修復在部署後階段發現的漏洞。 | 升級智慧型合約否定了程式碼不可變性的概念,會連帶影響到去中心化與安全性。 |
開發者可以使用邏輯升級為去中心化應用程式新增功能。 | 使用者必須信任開發者不會隨意修改智慧型合約。 |
智慧型合約升級可以增進終端使用者的安全性,因為漏洞可以被快速修復。 | 在智慧型合約中編寫升級功能增加了另一層複雜性和出現重大缺陷的可能性。 |
合約升級給了開發者更多的空間去試驗不同的功能並持續地改進去中心化應用程式。 | 升級智慧型合約的機會可能會鼓勵開發者在開發階段未經充分檢查研究就快速發佈專案。 |
智慧型合約的不安全存取控制和中心化可能導致惡意行爲者更容易執行未經授權的升級。 |
升級智慧型合約的注意事項
使用安全的存取控制/授權機制來避免未經授權的智慧型合約升級,尤其是在使用代理模式、策略模式或資料分離時。 例如,限制對升級函式的存取,使得只有合約的所有者可以呼叫升級函式。
升級智慧型合約是一個複雜的活動,需要高度的謹慎,避免引入漏洞。
透過升級實作流程的去中心化,減少信任假設。 可能的策略包括使用多重簽名錢包合約來控制升級,或要求去中心化自治組織 (DAO) 的成員投票批准升級。
注意升級合約的相關花費。 例如,在合約遷移期間從原有合約複製狀態(如使用者餘額)到新合約可能會需要不止一筆交易,這意味著需要更多燃料費。
考慮實作時間鎖來保護使用者。 時間鎖是指延遲執行對系統的變更。 時間鎖可以與多重簽名管理體系相結合來控制升級:如果提議的行動達到了所需的批准門檻,它還需要等到預定義的延遲期過後才會執行。
如果使用者不同意提議的變更(例如,邏輯升級或新的費用方案),時間鎖讓使用者有時間可以退出系統。 沒有時間鎖,使用者必須信任開發者不會未提前通知,便在智慧型合約上實作隨意變更。 缺點是,時間鎖限制了快速修補漏洞的能力。
資源
OpenZeppelin 升級外掛程式 - 一系列用於部署和保護可升級智慧型合約的工具。
教學
- 升級你的智慧型合約 | YouTube 使用教學(opens in a new tab) - 作者:Patrick Collins
- 以太坊智慧型合約遷移使用教學(opens in a new tab) - 作者:Austin Griffith
- 使用通用可升級代理標準 (UUPS) 代理模式升級智慧型合約(opens in a new tab) - 作者:Pranesh A.S
- Web3 使用教學:使用 OpenZeppelin 編寫可升級的智慧型合約(代理)(opens in a new tab)- 作者:fangjun.eth
了解更多
- 智慧型合約升級的狀態(opens in a new tab) - 作者:Santiago Palladino
- 多種升級 Solidity 智慧型合約的方法(opens in a new tab) - Crypto Market Pool 部落格
- 學習:升級智慧型合約(opens in a new tab) - OpenZeppelin 文件
- 適合 Solidity 合約升級能力的代理模式:透明與通用可升級代理標準 (UUPS) 代理(opens in a new tab) - 作者:Naveen Sahu
- 鑽石升級運作原理(opens in a new tab) - 作者:Nick Mudge