OpenZeppelin 合約昇級插件 1/9

AndyHuang
Oct 12, 2020

--

OpenZeppelin 合約昇級插件 1/9

在寫了幾個合約之後,對於同一個合約的昇級問題,一直覺得不是很好管理。

Ethereum 的智能合約,創建了之後無法修改原合約,但是可以刪除。所以合約的更新,一般順序就是部署新的合約,然後把所有關的應用地址更改為新合約的地址,最後再把舊合約刪除。當然這中間會包括新舊合約內資產的轉移之類的相關操作。

這裡產生的問題,就是在更改相關應用的合約地址時,不可遺漏。甚至如果有合約調用之類的操作,那整個更新的動作將會變得十分繁複,並且容易出錯。

因此,我選用了 OpenZeppelin 的合約昇級插件來學習,這個系列文章按照官網的文檔順序來寫,會加上我自己實作的結果及心得,所以這不是原文的翻譯,不可以做為原文的替代品。

20201109 補充

我在本系列文章,把 migration 、migrate 翻譯為”部署”的原因有兩個,第一個是比較符合行為的意義,一般我們說部署是指,把編譯好的程式放到生產或測試環境之中,也就是把各種檔案放到它應該去的地方。可是在智能合約的開發,這是分為兩個步驟,編譯好之後再使用 0交易發布到鏈上。另一個原因是已經有其他文章將 migrate 翻譯為部署。綜合考量這兩個因素,再加上 migrate 動作,與其”遷移”不如”部署”。在最後一章,因為 migrate 跟 deploy 出現次數很多,因此才分開各以原文書寫。

合約昇級插件

這個插件可以把昇級合約這件事加到你現有的工作流裡面。這個插件讓 Buidler 和 Truffle 兩個整合開發環境可以在以太坊上部署及管理可昇級的合約。

本插件的目標是以下四項

部署可昇級的合約

昇級已部署的合約

管理代理管理員(proxy admin) 權限

容易測試

總覽

安裝方式

Buidler 的安裝方式

$ npm install — save-dev @openzeppelin/buidler-upgrades @nomiclabs/buidler-ethers ethers

以上命令安裝了本插件及必要的依賴程式庫

Truffle 安裝

$ npm install — save-dev @openzeppelin/truffle-upgrades

用法

詳細的用法請各自參考 Truffle 及 Buidler ,以下是簡單的用法程式片段。

Buidler的用法

Buidler的用戶可以寫腳本來部署或昇級合約,也可以管理代理管理員權限。

const { ethers, upgrades } = require(“@nomiclabs/buidler”);

async function main() {

// Deploying

const Box = await ethers.getContractFactory(“Box”);

const instance = await upgrades.deployProxy(Box, [42]);

await instance.deployed();

// Upgrading

const BoxV2 = await ethers.getContractFactory(“BoxV2”);

const upgraded = await upgrades.upgradeProxy(instance.address, BoxV2);

}

main();

Truffle 用法

Truffle 的用戶,可以寫稱為 migrations 的設定檔,來使用插件。可以部署,昇級,管理代理管理員權限。

const { deployProxy, upgradeProxy } = require(‘@openzeppelin/truffle-upgrades’);

const Box = artifacts.require(‘Box’);

const BoxV2 = artifacts.require(‘BoxV2’);

module.exports = async function (deployer) {

const instance = await deployProxy(Box, [42], { deployer });

const upgraded = await upgradeProxy(instance.address, BoxV2, { deployer });

}

測試用法

無論用的是 Buidler 或 Truffle ,都可以用這個插件來測試,以確定在預期中運作。

it(‘works before and after upgrading’, async function () {

const instance = await upgrades.deployProxy(Box, [42]);

assert.strictEqual(await instance.retrieve(), 42);

await upgrades.upgradeProxy(instance.address, BoxV2);

assert.strictEqual(await instance.retrieve(), 42);

});

插件是如何運作的

插件提供兩個主要的函數,deployProxy 和 upgradeProxy 。負責管理合約的可昇級部署。

在 deployProxy時,完成以下四件事

驗証合約的實作是可以安全昇級的(下一篇 QA中說明什麼是可以安全昇級的合約)

為你的專案部署一個代理管理員(proxy admin) 合約

部署功能實作合約

建立及初始化代理合約

在調用 upgradeProxy 時,完成以下三件事

驗証新的功能實作合約是不是可安全昇級,是不是跟前一版本相容

檢查功能實作合約的 bytecode 是不是跟前一版本相同,不相同才部署

昇級代理合約,使用新的功能實作合約

插件會持續的追蹤所有在專案根目錄底下的 .openzeppelin 目錄下的所有功能實作合約及代理管理員。在目錄裡面一個每個網路名稱會配一個檔案,建議針對所有的網路都做版本管理。

管理所有權

所有的代理都會定義管理員地址,只有管理員有權限更新。預設是代理管理員合約,可以調用 admin.changeAdminForProxy 函數來更改代理管理員。記得代理管理員只能更新代理合約,不能和功能實作合約交互。

代理管理員合約也定義了擁有者地址,這個地址有操作權限。預設這個地址是部署時的外部帳戶。可以調用 admin.transferProxyAdminOwnership 函數來修改代理管理員的擁有者地址。注意這個會改變更新任何代理合約的權力,要小心使用。

修改了更新的權限的地址,還是可以用本地的設定來驗証和部署功能實作合約。插件裡的 prepareUpgrade 函數可以驗証新的功能實作合約是不是可以安全昇級,和之前的合約是不是相容。然後用本地的以太坊帳戶部署。最後用 admin 地址來執行更新。

在下一篇中我會先說明文中的特定名詞,如代理管理員,可以安全昇級的合約,功能實作合約

參考

https://docs.openzeppelin.com/upgrades-plugins/1.x/faq

https://buidler.dev/

https://www.trufflesuite.com/truffle

--

--

AndyHuang
AndyHuang

Written by AndyHuang

blockchain , defi , programe

No responses yet