如何使用Truffle 5.0和ZeppelinOS 2.0编写可升级的智能合约

如何使用Truffle 5.0和ZeppelinOS 2.0编写可升级的智能合约?下面就是使用可升级的智能合约启动并运行的分步教程。

在这篇文章中,我们将学习如何使用最新版本的Truffle和ZeppelinOS编写可升级的智能合约。特别是,版本5.0的Truffle引入了很多更新,其中最突出的是与web3 1.0的集成。让我们解开这些更新,并使用最先进的ZeppelinOS引入可升级的智能合约。

这不是一个以太坊开发的介绍性文章,如果你需要,请查看以下资源:

请注意,区块链世界以一种荒谬的速度发展,几乎没有时间让标准参与其中。这意味着上述文章中的大量代码片段可能无法按预期工作,但不要惊慌失措并返回这里就好了。

先决条件

确保你配备以下产品:

  • node.js和npm
  • ganache-cli或Ganache桌面应用程序
  • 学习更多的好奇心

Truffle 5.0

要全局安装,请转到你的终端并写下:

1
$ npm install truffle@^5.0.0 --global

创建一个属于我们的项目:

1
2
3
4
$ mkdir MyProject
$ cd myProject
$ npm init -y
$ truffle init

这将初始化一堆文件。如果你使用的是较旧版本的Truffle,你可能会注意到truffle-config.js现在更加冗长,并且具有更多可配置选项。让我们来看看重要的变化。

Web3 1.0

这是一个主要的API更改,但它是一个很好的改变,因为新库更优雅和直观使用。当然,如果你已经使用旧版本编写了Truffle测试,则存在转换成本,因此请保留web3 1.0文件的标记以防万一。

HD Wallet Provider

如果你之前使用的是truffle-hdwallet-provider npm模块,则必须升级到truffle-hdwallet-provider @ web3-one才能使其与Truffle 5.0一起使用:

1
$ npm install truffle-hdwallet-provider@web3-one --save-dev

要加载助记符,可以使用节点本机提供的fs模块,也可以安装dotenv。永远不要用JavaScript硬编码!

带上你自己的编译器

在使用Truffle进行编译时,改变Solidity编译器曾经是一个巨大的痛苦,但不要再害怕了!你可以通过执行以下操作来定义所需的任何(远程)版本:

1
2
3
4
5
6
7
8
9
compilers: {
solc: {
optimizer: {
enabled: true,
runs: 200,
},
version: "0.4.25",
},
}

在此特定示例中,我们将编译器版本设置为0.4.25,但如果你愿意,可以选择0.4.240.4.18。列出所有可用版本:

1
$ truffle compile --list

支持异步和等待

如果你是JavaScript ES8的超级酷炫async异步和await等待的粉丝,你现在可以在运行$ truffle develop$ truffle console时充分利用它们。

其他

还有一些其他新功能,例如添加的插件支持,但它们超出了本教程的范围。 如果你正在寻找详尽的列表,请查看更改日志

部署智能合约

这是我们将要使用的模拟Solidity合约:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Note.sol
pragma solidity ^0.4.25;

contract Note {
uint256 private number;

constructor(uint256 _number) public {
number = _number;
}

function getNumber() public view returns (uint256 _number) {
return number;
}
}

contract文件夹中创建一个名为Note.sol的新文件,然后将上面的代码复制并粘贴到其中。现在,通过编译合约确保你已正确完成所有步骤:

1
$ truffle compile

你应该没有错误并看到以下日志:

1
2
3
Compiling ./contracts/Migrations.sol…
Compiling ./contracts/Note.sol…
Writing artifacts to ./build/contracts

现在,让我们编写迁移文件。

1
2
3
4
5
6
7
// 2_migrate_note.js

var Note = artifacts.require("./Note.sol");

module.exports = function (deployer) {
deployer.deploy(Note, 64);
};

migrations文件夹中,创建一个名为2_migrate_note.js的新文件,然后复制并粘贴上面的代码。要验证设置,你必须首先启动以太坊节点,但不要担心,对于本地开发,有一些像Ganache这样的工具。你可以通过打开新的终端窗口并执行$ ganache-cli或启动桌面应用程序来运行它。然后:

1
$ truffle migrate

如果遇到问题,请确保你的truffle-config.js文件看起来像这样(为简洁起见,我删除了注释):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// truffle-config.js

module.exports = {
compilers: {
solc: {
version: "0.4.25",
settings: {
optimizer: {
enabled: false,
runs: 200
}
}
}
},
networks: {
development: {
host: "127.0.0.1",
port: 8545,
network_id: "*",
}
}
};

如果它有效,你应该得到一个像这里的美丽的报告。真棒!现在你知道如何使用Truffle 5.0编译和部署合约,但是你还没有完成更酷的部分:让智能合约升级。

ZeppelinOS

构建

如果你对可升级的智能合约完全不熟悉,请观看Elena Nadolinski关于该主题的演讲。这是完全值得的,它可以做得比博客上的人好得多。

它的要点是我们的目标是将传统的开发实践与智能合同相协调:

  • 当与真实用户一起运行A/B测试并且存在错误时,我们应该修补代码并尽快修复它。
  • 可升级性流程应保持完全透明,并且不用向开发人员授予全部权力。

目前正在研究第二点,许多人积极提出透明的治理模式。但是,我们现在不介意去中心化的自治组织(DAO),而是关注故事的技术部分。

让我们安装ZeppelinOS及其库依赖:

1
2
npm install zos --global
npm install zos-lib --save-dev

然后在我们的Truffle项目中初始化它:

1
zos init MyProject

应该初始化一个名为zos.json的文件:

1
2
3
4
5
6
{
"zosversion": "2",
"name": "MyProject",
"version": "0.1.0",
"contracts": {}
}

重要的是,请确保按如下方式更新Note合约:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Note.sol
pragma solidity ^0.4.25;

import "zos-lib/contracts/Initializable.sol";

contract Note is Initializable {
uint256 private number;

function initialize(uint256 _number) public initializer {
number = _number;
}

function getNumber() public view returns (uint256 _number) {
return number;
}
}

ZeppelinOS合约不使用普通的Solidity构造函数,而是依赖于Initializable基本合约,该合约需要覆盖其initialize函数。让我们继续将编辑后的合约添加到ZeppelinOS:

1
$ zos add Note

应将以下JavaScript对象添加到zos.json中:

1
2
3
"contracts": {
"Note": "Note"
}

在部署之前,ZeppelinOS要求你创建会话:

1
$ zos session --network development --from YOUR_DEPLOYMENT_ACCOUNT --expires 7200

解释每个参数:

  • network:truffle-config.js下定义的网络之一。
  • from:由于已知的透明代理问题,部署帐户不能是truffle或ganache指定的默认地址。
  • expires:会话的生存时间,以秒为单位。

现在,澄清一些事情:

  • 会话有点像behavioural sugar,在部署时,你不必指定networkfrom参数。
  • 你可能想知道要设置的from参数。简单地说,除了第一个帐户之外的任何帐户。在Ganache桌面应用上查看下面的图片以获取示例:

准备好prime time?

1
$ zos push

如果正常工作的话,你可以看到类似以下内容:

1
2
3
4
5
6
7
8
9
10
Compiling contracts
Compiling ./contracts/Migrations.sol…
Compiling ./contracts/Note.sol…
Compiling zos-lib/contracts/Initializable.sol…
Writing artifacts to ./build/contracts

Validating contract Note
Uploading Note contract as Note
Deploying logic contract for Note
Created zos.dev-7923.json

此命令最终将你的智能合约部署到区块链,它还会创建一个名为zos.dev-<network_id>.json的文件,其中<network_id>是你的以太坊网络的ID。你可以在此处找到有关项目的重要信息,例如已部署合约的地址。

可升级

非常重要的是要了解ZeppelinOS,通过创建两个合约来工作:

  • 代理Proxy
  • 逻辑Logic

代理将最终用户重定向到逻辑,但代理保留retains存储。也就是说,即使你更新逻辑,智能合约的状态也保持不变。

以前,你创建并部署了逻辑合约。让我们创建一个代理实例:

1
$ zos create Note --init initialize --args 64

这将部署并记录新代理协议的地址。打开一个新的终端窗口并初始化Truffle控制台:

1
2
3
4
truffle console --network development
let abi = require("./build/contracts/Note.json").abi
let contract = new web3.eth.Contract(abi, "your-proxy-address")
contract.methods.getNumber().call();

它应该打印“64”。

有关如何使用新的Contract API的更多信息,请访问web3 1.0 docs

如果你想更改number怎么办?幸运的是,你可以升级合约逻辑,同时保留存储空间。

首先,通过添加新功能来更新合约:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Note.sol
pragma solidity ^0.4.25;

import "zos-lib/contracts/Initializable.sol";

contract Note is Initializable {
uint256 private number;

function initialize(uint256 _number) public initializer {
number = _number;
}

function getNumber() public view returns (uint256 _number) {
return number;
}

function setNumber(uint256 _number) public {
number = _number;
}
}

然后:

1
2
$ zos push
$ zos update Note

运行日志看起来像这样:

1
2
3
4
5
Using session with network development, sender address 0xd833B5fa468A5a430a890390D8Ec652142836019
Upgrading proxy to logic contract 0xe6d6d1cd339f129275e5b5e7ab39d85bc5642010
Instance at 0x746bb4a872bdfd861c4af5dd9391b3fb5b22fb7a upgraded
0x746bb4a872bdfd861c4af5dd9391b3fb5b22fb7a
Updated zos.dev-7923.json

现在,启动一个新的Truffle控制台并调用新的setNumber函数:

1
2
3
4
5
6
$ truffle console --network development
let abi = require("./build/contracts/Note.json").abi;
let contract = new web3.eth.Contract(abi, "your-proxy-address");
contract.methods.getNumber().call();
contract.methods.setNumber(65).send({ from: YOUR_OTHER_ACCOUNT });
contract.methods.getNumber().call();

它应该打印“65”。

瞧!你刚刚编写,部署和升级了以太坊智能合约。

注意事项

  • “YOUR_DEPLOYMENT_ACCOUNT”必须仅在设置会话时使用,对于与合约的任何其他交互,你必须使用其他帐户。
  • 如果你有”一个A网络名称必须提供执行请求”的操作错误信息即A network name must be provided to execute the requested action,只需启动一个新会话,旧会话已过期。
  • 你可能已经注意到我们没有使用TruffleContract与已部署的合约进行交互,而是依赖于web3 1.0实现。这是因为我发现前者令人困惑,并且在许多情况下12,甚至没有函数。
  • 可升级的智能合约比我们在此讨论的更多。前往Zeppelin的精彩文档,了解整体情况。特别是,我建议查看升级存储变量的限制
  • 来自Zeppelin的Santiago Palladino在Twitter上宣布他们现在支持0.5.0版本的Solidity。如果你想测试它,只需要执行npm install zos-lib@2.1.0-rc.0 --global

包起来

我希望你喜欢这个教程,尽管偶尔会出现版本控制和协调问题,但你和我一样对区块链开发感到兴奋。

我在这个GitHub repo中编译了本文中使用的代码。有三个分支(master,zos,zos-upgraded)和我们经历过的每个阶段的Note合约的代码。

======================================================================

分享一些以太坊、EOS、比特币等区块链相关的交互式在线编程实战教程:

  • java以太坊开发教程,主要是针对java和android程序员进行区块链以太坊开发的web3j详解。
  • php以太坊,主要是介绍使用php进行智能合约开发交互,进行账号创建、交易、转账、代币开发以及过滤器和交易等内容。
  • python以太坊,主要是针对python工程师使用web3.py进行区块链以太坊开发的详解。
  • 以太坊入门教程,主要介绍智能合约与dapp应用开发,适合入门。
  • 以太坊开发进阶教程,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。
  • ERC721以太坊通证实战,课程以一个数字艺术品创作与分享DApp的实战开发为主线,深入讲解以太坊非同质化通证的概念、标准与开发方案。内容包含ERC-721标准的自主实现,讲解OpenZeppelin合约代码库二次开发,实战项目采用Truffle,IPFS,实现了通证以及去中心化的通证交易所。
  • C#以太坊,主要讲解如何使用C#开发基于.Net的以太坊应用,包括账户管理、状态与交易、智能合约开发与交互、过滤器和交易等。
  • EOS教程,本课程帮助你快速入门EOS区块链去中心化应用的开发,内容涵盖EOS工具链、账户与钱包、发行代币、智能合约开发与部署、使用代码与智能合约交互等核心知识点,最后综合运用各知识点完成一个便签DApp的开发。
  • 深入浅出玩转EOS钱包开发,本课程以手机EOS钱包的完整开发过程为主线,深入学习EOS区块链应用开发,课程内容即涵盖账户、计算资源、智能合约、动作与交易等EOS区块链的核心概念,同时也讲解如何使用eosjs和eosjs-ecc开发包访问EOS区块链,以及如何在React前端应用中集成对EOS区块链的支持。课程内容深入浅出,非常适合前端工程师深入学习EOS区块链应用开发。
  • java比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Java代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Java工程师不可多得的比特币开发学习课程。
  • php比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Php代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Php工程师不可多得的比特币开发学习课程。
  • c#比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在C#代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是C#工程师不可多得的比特币开发学习课程。
  • tendermint区块链开发详解,本课程适合希望使用tendermint进行区块链开发的工程师,课程内容即包括tendermint应用开发模型中的核心概念,例如ABCI接口、默克尔树、多版本状态库等,也包括代币发行等丰富的实操代码,是go语言工程师快速入门区块链开发的最佳选择。

汇智网原创翻译,转载请标明出处。这里是如何使用Truffle 5.0和ZeppelinOS 2.0编写可升级的智能合约