2.3 Ganache + Truffle的安装与使用
在DAPP系统的开发过程中,除了智能合约的开发和调试,还需要与智能合约交互的界面系统来配合,否则用户就无法便捷地使用DAPP系统,从而形成过高的用户使用门槛并增加DAPP的推广难度。正如上一节所描述的,通过Ethereum Wallet与区块链进行交互,对使用者的编程技能要求很高,因此必须通过开发浅显易懂的UI系统与用户交互。本节讨论的开发工具就具有这样的能力,它们聚合了与区块链交互的模拟环境和与用户交互的JS代码库。
2.3.1 Ganache
官方下载地址:https://github.com/trufflesuite/ganache/releases,选择适合读者的操作系统下载安装,安装完成之后启动该程序,如图2.32所示。
图2.32 Ganache主页面
Ganache是一个以太坊运行环境的模拟器,它模拟了以太坊公链的行为和接口,可以通过与此模拟环境链接和交互,方便地调试智能合约和用户交互系统,下面简单介绍一下它的界面和功能,其他的功能会在后面编写DAPP系统时详细讲解。
区域1为Account标签页,该标签下展示了当前可以用来开发和测试的以太坊账户,当然这些账户都是测试环境的账户,无法到真正的以太坊公链中使用。
区域2为该标签页下的BLOCKS、TRANSACTIONS和LOGS,分别表示以太坊公链上的区块链信息、交易信息和日志信息。因为当前的系统没有产生任何交易,因此这3个标签下的数据都是空,接下来的案例会有交易和日志产生,交易会被打包成区块,届时再来详细介绍该标签页下的内容。
区域3为系统设置,在使用Truffle部署智能合约时,会使用当前模拟环境的网络地址和网络编号,以便Truffle通过该接口将智能合约部署到Ganache的以太坊测试环境中。
区域4表示10个账户中的一个账户的地址,该地址是一个公钥地址,也是区块链钱包地址。
区域5表示该账户下的以太坊余额,在部署和执行智能合约时,需要用以太坊代币的形式支付一定的费用,因此必须保证账户下有充足的余额才能够成功地执行智能合约的逻辑。
区域6表示关于该账户的交易数和该账户的索引,当发生修改区块链状态的交易时,该数据就会发生变化,以反映该账户下产生的状态变更次数。
区域7是该账户下的密钥,也是与公钥即区块链地址一一对应的非对称加密的私钥部分,可以通过私钥驱动该账户下的交易执行。单击该钥匙按钮就会显示该账户的私钥明文,如图2.33所示。
图2.33 私钥明文
区域8为Ganache的系统设置,该设置涉及虚拟环境绑定的IP地址、端口号、初始的账户个数、初始账户的以太坊代币个数等参数。除非有特殊需要,并且精通每个参数设置的具体含义,否则不要修改这些参数,使用参数默认值即可。
2.3.2 Truffle
Truffle支持Windows和Mac OS X系统,在安装Truffle之前需要安装Node.js 8.9.4及以后的版本。下载并安装Node.js的网址为https://nodejs.org/en/download/,安装成功之后,通过npm命令行来安装Truffle:
01 npm install -g truffle
命令执行过程会下载并编译代码,编译成功后即可正式开始创建Truffle的工程。
1.创建工程
(1)创建目录:
01 mkdir chapter-2 02 cd chapter-2
(2)下载代码:
01 truffle unbox metacoin
Truffle的unbox功能类似于GitHub的包管理工具,通过unbox可以下载已经编入Truffle库的既有代码。metacoin是这个代码库的名称,下载metacoin代码后的目录结构如图2.34所示。其中contracts是智能合约文件,migrations是将智能合约部署到区块链上需要用到的代码,test是单元测试的代码,truffle.js是关于环境的配置。
图2.34 下载metacoin代码后的目录结构
用文本编辑器打开truffle.js文件,将上一小节中Ganache的配置3部分按照如下格式写入文件:
01 module.exports = { 02 networks: { 03 development: { 04 host: "127.0.0.1", //模拟器接入IP地址 05 port: 7545, //模拟器监听的端口 06 network_id: "*" //模拟器网络编号 07 } 08 } 09 };
此时我们的Truffle编译工具就会通过该配置文件与Ganache的以太坊模拟公链环境链接起来。对比一下该文件的配置与上一小节中Ganache的配置,读者即可明白如何一一对应。
(3)工程核心代码(注释中涉及的代币即为本合约名称指代的数字货币,即MetaCoin):
01 pragma solidity ^0.4.18; 02 import "./ConvertLib.sol"; 03 contract MetaCoin { 04 mapping(address => uint)balances; //记录账户下代币余额的数组 05 06 event Transfer(address indexed _from, address indexed _to, uint256 _value); 07 //构造函数,初始化合约发起者拥有10000个代币 08 constructor()public { 09 balances[tx.origin] = 10000; 10 } 11 //转移代币函数,做一定的安全检查之后,从交易调用者账户下转移amount个代币到 //receiver账户下,并将该动作通过event Transfer记录在区块链上 12 function sendCoin(address receiver, uint amount) public returns(bool sufficient){ 13 if(balances[msg.sender] < amount)return false; 14 balances[msg.sender] -= amount; 15 balances[receiver] += amount; 16 emit Transfer(msg.sender, receiver, amount); 17 return true; 18 } 19 //查询账户addr下的以太坊余额 20 function getBalanceInEth(address addr)public view returns(uint){ 21 return ConvertLib.convert(getBalance(addr),2); 22 } 23 //查询账户addr下的代币余额 24 function getBalance(address addr)public view returns(uint){ 25 return balances[addr]; 26 } 27 }
该Truffle目录下的所有文件都是为contracts下的MetaCoin.sol文件服务的,其他目录下的文件是为了配合该文件测试而编译和上传到区块链公链平台上的。本文重点讲解Truffle编译工具的使用,不对该智能合约的细节展开讲解,只在注释中进行概要讲解。
2.测试
在命令行下,切换到chapter-2(使用cd命令)目录下执行测试命令:
01 truffle test ./test/TestMetacoin.sol
如果编译成功,则会提示编译时长,否则会提示相应错误。由于该文件的创建时间比较久,而笔者的编译环境较新,因此提示编译环境错误,如图2.35所示。
图2.35 编译失败错误提示
通过修改智能合约的编译器版本号(笔者编译器版本号为0.5.0,所以将合约代码开头第一行改为pragma solidity 0.5.0)之后,测试顺利通过,如图2.36所示。
图2.36 代码测试成功的提示
3.编译
编译命令如下:
01 truffle compile
编译成功之后,工程目录下会增加build目录,如图2.37所示。该目录下生成的文件可以通过部署接口将智能合约部署到以太坊区块链上或以太坊区块链的模拟环境下。
图2.37 编译成功之后,目录下会增加新的目录结构
4.部署
部署命令如下:
01 truffle migrate
保存Ganache开启状态,执行上述部署命令,部署成功之后,会有相应的提示,如图2.38所示。
图2.38 部署成功的提示界面
此时Ganache的账户和交易数据也有了相应的变化,其变化的细节及与智能合约部署和执行的逻辑关系会在后面进行详细讲解,如图2.39和图2.40所示。
图2.39 部署成功之后账户下的交易数和余额信息
图2.40 部署成功之后,Ganache模拟环境下的交易信息
5.交互
通过命令行与以太坊模拟环境交互,依次输入以下命令:
wsli@chapter-2$ truffle console //进入truffle的命令行 //获取合约的地址 truffle(development)> let instance = await MetaCoin.deployed() undefined //获取Ganache的所有账户 truffle(development)> let accounts = await web3.eth.getAccounts() undefined truffle(development)> web3.eth.getAccounts() //查看当前Ganache的所有账户 [ '0xc1831D70D220A5A4567EfDC6BE12aEc815A4680B', '0x4a9F0563242b3eA383d0A6F7be98FB9b2DA1F4C9', '0xEC4F0C59727DCE74Dfcb780fFf6b3c3fDbc47553', '0xb65136afc5d3ecd16E847eBa78df260F3B567163', '0x691c560495bBc499419eC01971F51fc4398Bdc50', '0x30D7aeA2A216738cC60FA240C083f59CE8c48cd9', '0x5f1C9DC8742769134dB07a9507229A2847C5BE87', '0xd24948e99AF5055e1Cba964703D0398B786FcC17', '0x7830Ae9B0690fE777cD28AFB1fF805236f754196', '0x9d0246CDC84e54cdf8E02DB8834c89d8Ff6333C4' ] truffle(development)>(await web3.eth.getAccounts())[0] '0xc1831D70D220A5A4567EfDC6BE12aEc815A4680B' //查看账户0下MetaCoin这种代币的余额 truffle(development)>let balance = await instance.getBalance(accounts[0]) undefined truffle(development)>balance.toNumber() 10000 //给Ganache账户1转移500个MetaCoin代币,此时会产生交易数据,本例中略 truffle(development)> instance.sendCoin(accounts[1], 500) //省略的交易数据 //查询账户1下的余额 truffle(development)> let received = await instance.getBalance(accounts[1] undefined truffle(development)> received.toNumber() 500 //查询账户0下的余额 truffle(development)> let newBalance = await instance.getBalance(accounts[0]) undefined truffle(development)> newBalance.toNumber() 9500
2.3.3 安装总结
虽然本节使用的工具是在命令行下执行的,与之前的两个工具相比,使用难度更高,但是该命令行下使用的命令都可以在对应的JS代码库中使用,只要引用正确的JS代码库,就可以在页面编写用户界面,并调用Web3.js代码库,与以太坊发生交互,进行区块链数据的输入输出。其具体实现会在后面的章节中讲解。