其实本章另外还有web3的调用方法,但因为从没使用过nodejs做应用,我一直也没用过web3的方式调用。所以就先删掉了。
truffle的安装与使用
项目地址:http://truffleframework.com/
项目文档:http://truffleframework.com/docs/
truffle是一个去中心化应用的开发框架,也可以被用来测试、部署智能合约。项目说明里说自己为一个开发环境[目瞪狗呆脸]。但是我必须得说现在连UltraEdit都比它更像个IDE好吧?不过目前来看,还是最好用的一个DAPP(decentralized application去中心化应用)开发环境。
类似的项目有dapple(项目文档:http://dapple.readthedocs.io/en/master/)以及以太坊官方的meteor(项目文档:https://github.com/ethereum/wiki/wiki/Dapp-using-Meteor)。功能上都大同小异,如果有机会可以再试用一下,写相关的说明文档。
1 truffle安装与配置
1.1前置需求
nodejs5.0+
安装nodejs,需要去官网(nodejs.org)下载安装。在几年9月份,nodejs最新版本是6.8,推荐版本是4.5;到笔者发稿时,最新版本已经是7.0+,推荐版本是6.9了。所以考虑到比较高频率的版本更新,安装方法请直接参考官网。
1.2 truffle安装
使用npm安装
npm install -g truffle
2 truffle的使用
truffle是一个构建DAPP(decentralized application去中心化应用)的快捷工具。它主要由几个工具组成
2.1创建新项目
init命令的作用是在当前目录下建立一个truffle项目。在执行该命令之前,强烈建议先建立一个新目录。
$ mkdir myproject
$ cd myproject
$ truffle init
truffle项目的目录结构包括:
-app/:dapp目录,默认生成的dapp的建立目录;
-contracts/:合约目录,用于存放合约;
-migrations/:部署目录,用于存放部署合约用的脚本;
-test/:测试目录,用于存放测试应用程序和合约用的脚本;
-truffle.js-Truffle配置文件。
tips:在执行truffle init命令的时候,目录中会有一个范例项目MetaCoin。有兴趣的读者可以看一看这个项目的构成,本文在稍后的章节也会略微介绍一下这个项目。
2.2编译合约
在之前init建立的路径下执行编译命令。
$ truffle compile
编译所有位于contracts目录下的.sol文件,并生成相应的js文件,放入./build/contracts目录下。
需要注意的是,在编译合约时,合约文件中,必须存在与文件名完全一致的合约(包括大小写)。这句话的意思是,如果我的文件名叫MyContract.sol,那么这个文件中必须包含一个叫MyContract的合约,就像这样:
contract MyContract{
...
}
或者
library MyContract{
...
}
如果合约之间存在import依赖,那么也没关系,compile命令会自动按顺序来处理。
tips:第二次执行truffle compile命令时,只会编译那些与第一次编译相比有改动的合约。如果要强制全部编译,加上--compile-all选项。
tips:我经常用不到compile命令,而使用build命令代替。如果不构建基于javascript的去中心化app,只是测试智能合约的时候,使用二者是等同的。
2.3构建去中心化应用
在之前init建立的路径下执行构建命令。
$ truffle build
build命令的作用是构建去中心化应用。其作用是,首先执行编译合约,将生成合约的js文件放入./build/contracts/路径下(这一步与truffle compile功能一样)。之后将./app/目录下的文件拷贝到./build/目录下。
./app/目录结构如下:
app/
- javascripts/
- app.js
- stylesheets/
- app.css
- images/
- index.html
在配置文件truffle.js中,有一段代码是这样的:
build:{
"index.html": "index.html",
"app.js": [
"javascripts/app.js"
],
"app.css": [
"stylesheets/app.css"
],
"images/": "images/"
},
这段代码代表执行build命令时,会做出这样的操作:
(1)把./app/index.html拷贝到./build/index.html
(2)将app.js后面数组中的所有文件连接到一起(当前只有./javascripts/app.js一个文件),生成一个app.js文件。
tips:默认项目的./app/javascripts/app.js只有55行,而生成的app.js有44024行。前面的将近44000行代码,全部都是建立web3对象等等等等前置需求。最后把那55行代码复制到文件末尾……
(3)将app.css后面数组中的所有文件连接到一起(当前只有./stylesheets/app.js一个文件),生成一个app.css文件。
(4)将./app/images目录拷贝到./build/images
我们可以看到这里有个index.html。实际上,我们构建成功的应用,就是一个网站。如果要使用这个网站,需要之后执行serve命令。
2.4部署合约
2.4.1部署合约
在之前init建立的路径下执行部署命令。
$ truffle migrate
作用是执行migrations文件夹中的js脚本,通常功能是部署合约。其中migrations目录下的文件名称前缀必须是一个数字,部署时按顺序执行。在建立项目(init)时,会自动默认生成migration合约,其作用是将migrate的历史记录保存到区块链中。用来记录Migrate命令是否执行成功。
具体执行的脚本可以参考默认生成的项目脚本。比如,要部署一个叫做MyContract的合约,其Migration中就可以新建一个prefixNum_example_migration.js文件,内容如下:
module.exports = function(deployer) {
// deploymentsteps
deployer.deploy(MyContract);
};
Truffle要求我们在使用Migration机制的时候,必须使用Migration合约,这个Migration合约中会保存若干特殊的接口。正常情况下,这个Migration合约是第一个被部署的,而且以后再也不会被执行。
tips:如果第一次执行成功,第二次执行truffle migrate命令时,只会执行新的合约部署。如果要强制重新部署,要加上--reset选项。
./contracts/Migrations.sol合约内容如下:
contract Migrations {
addresspublic owner;
// Afunction with the signature `last_completed_migration()`, returning a uint, is required.
uintpublic last_completed_migration;
modifierrestricted() {
if(msg.sender == owner) _
}
functionMigrations() {
owner= msg.sender;
}
// Afunction with the signature `setCompleted(uint)` is required.
functionsetCompleted(uint completed) restricted {
last_completed_migration= completed;
}
functionupgrade(address new_address) restricted {
Migrationsupgraded = Migrations(new_address);
upgraded.setCompleted(last_completed_migration);
}
}
为了部署它,./migrations/1_initial_migration.js文件内容如下:
module.exports = function(deployer) {
deployer.deploy(Migrations);
};
2.4.2 deployer对象的使用
如果有两个合约A和B,在Migrations目录下的脚本中写入这样的代码
deployer.deploy(A);
deployer.deploy(B);
它们会按顺序先部署合约A,后部署合约B。
如果合约B的部署需要合约A的地址怎么办呢?Truffle也可以实现先部署合约A,再部署合约B,同时将A的地址传入。
deployer.deploy(A).then(function() {
returndeployer.deploy(B, A.address);
});
我们还可以在部署合约的时候,要求目标网络畅通:
module.exports = function(deployer, network){
// Adddemo data if we're not deploying to the live network.
if(network != "live") {
deployer.exec("add_demo_data.js");
}
}
到这里,我们可以看到deployer对象有很多的api接口,在这里列举一下。
(1)deployer.deploy(contract, args...)
在部署合约的时候,有些合约可能会需要一些参数,有些不需要,这些参数会作为合约构造函数的传入参数保存下来。那么我们部署合约的时候,就需要满足构造函数的要求。
示例代码:
//如果合约A的构造函数没有参数
deployer.deploy(A);
//如果合约A的构造函数有多个参数。
deployer.deploy(A, arg1, arg2, ...);
//如果同时部署多个合约。这样可以发起一个批量请求,而不是三个单独的请求。
deployer.deploy([
[A,arg1, arg2, ...],
B,
[C,arg1]
]);
(2)deployer.link(library, destinations)
将一个已经部署的合约库Library,链接到一个或多个合约。
示例代码:
// Deploy library LibA, then link LibA to contractB
deployer.deploy(LibA);
deployer.link(LibA, B);
// Link LibA to many contracts
deployer.link(LibA, [B, C, D]);
(3)deployer.autolink(contract)
自动将某个合约,链接到它依赖的已经部署的合约库Library
示例代码:
// Assume A depends on a LibB and LibC
deployer.deploy([LibB, LibC]);
deployer.autolink(A);
// Link *all* libraries to all available contracts
deployer.autolink();
(4)deployer.then(function() {...})
这个就像是一个条件,感受一下……
示例代码:
deployer.then(function() {
// Createa new version of A
returnA.new();
}).then(function(instance) {
// Setthe new instance of A's address on B.
varb = B.deployed();
returnb.setA(instance.address);
});
(5)deployer.exec(pathToFile)
示例代码:
deployer.exec("../path/to/file/demo_data.js");
部署的时候,执行程序。
2.5去中心化应用测试
在之前init建立的路径下执行测试命令。
$ truffle test
作用是执行test目录下的全部脚本文件(js/es/es6/jsx),通常功能是测试合约。
$ truffle test ./path/to/test/file.js
作用是执行指定路径下的测试脚本文件。
这些脚本应以contract()函数开头。(然而亲测不使用contract函数开头也不会报错……)contract()函数提供两个特性,①在运行脚本之前会重新部署合约,②可以输入账户地址。
这里给一个默认例子,作用是先部署合约,之后执行it块内的代码。
contract('MetaCoin', function(accounts) {
it("should put 10000 MetaCoin in thefirst account", function() {
// Get a reference to the deployed MetaCoincontract, as a JS object.
var meta = MetaCoin.deployed();
// Get the MetaCoin balance of the firstaccount and assert that it's 10000.
return meta.getBalance.call(accounts[0]).then(function(balance){
assert.equal(balance.valueOf(),10000, "10000 wasn't in the first account");
});
});
});
2.6与合约交互
(暂略)
2.7提供服务
$ truffle serve
作用是提供对外服务,启动truffle项目服务,部署到http 8080端口上。在build应用之后,项目的build目录下已经有了一个index.html、js脚本以及相应的网站设计css文件。如何把这个项目部署到Tomcat服务器上呢?只需要运行本命令就可以了!
2.8去中心应用MetaCoin
MetaCoin合约是Truffle默认建立的一个项目,简单的说,它是一个代币合约,类似于比特币,是基于智能合约建立的一种用于支付的数字货币。
2.8.1如何运行MetaCoin示例项目
简单的说,按照如下的步骤即可生成:
(1)建立目录
$ mkdir truffletest
$ cd truffletest
(2)建立项目
$ truffle init
(3)构建应用
$ truffle build
(4)部署合约
$ truffle migrate
(5)提供服务
$ truffle serve
打开浏览器,访问http://localhost:8080就可以运行这个去中心化应用了。
(默认项目MetaCoin的网站是这样子的,这里因为没有部署合约,显示的有点问题,正常情况下会显示有多少meta代币)
之后可以运行一些转账给另一个账户之类的功能。
2.9测试智能合约的简单说明
(1)建立项目
$ truffle init
(2)编写合约,将合约文件(*.sol)拷贝到./contracts目录下
(3)编译合约
$ truffle build
(4)修改配置
修改truffle.js,要部署的目标ip和端口,比如使用本地测试区块链testrpc,就修改为
rpc: {
host:"localhost",
port:8545
}
修改./migrations/目录下面的2_deploy_contracts.js文件,更改要部署的合约。
(5)部署合约,部署之前要确保测试区块链已经启动。如果使用testrpc,需要打开新终端,输入testrpc;如果使用pyethapp,需要运行pyethapp。诸如此类,等等等等。
$ truffle migrate
(6)编写测试脚本,拷贝到./test目录下
(7)测试合约
$ truffle test
class=MsoNorma�]���