准备工作
- 安装Nodejs环境,选择对应的平台,并下载LTS版本。安装完成后,在终端输入下面命令查看,如果正常输出版本号,则安装成功
node -v
image.png
- 安装truffle框架,输入如下命令安装
sudo npm install -g truffle
- 安装Ganache,下载对应平台安装包安装即可。Ganache是一个图形化的以太坊私有链程序,打开后便在本地建立起一个以太坊私有链,方便快捷。启动Ganache后,会有10个默认账户,并都有100Eth,当然,这只是测试用的,因为在你的私有链上。安装完成后请开启Ganache,后面部署合约时会用到
image.png - 下载metamask以太坊钱包。介于网络的原因,我们用firfox或者Opera版本的插件。当然,你也可以直接下载浏览器。
创建目录,编程初体验
-
首先,我们创建一个目录。这就不需要多说了,我的目录就叫SmartContart
image.png - 在当前文件夹下输入truffle命令,来unbox一个项目
truffle unbox pet-shop
image.png
-
用你的编辑器打开这个项目,我使用的是Atom,文件夹介绍在图里。有一点需要说明下,migrations是部署合约的文件夹,通过js来部署(图中migrations的解释写错了,请忽略)。
image.png - 我们新建一个智能合约,叫Election.sol,并写上下面的代码:
pragma solidity ^0.4.2; //这句声明了我们的solidity版本
//声明一个合约 contract是关键字,Election是合约名字,合约名字和文件名要一致
contract Election {
/* 声明一个public 的string类型的状态变量(可以理解为成员变量) */
string public candidate;
/* constractor 函数名和合约名一样的函数就是构造函数,与C++相似*/
/* 构造函数是public的,因为需要外部来调用 */
function Election() public{
candidate = "Candidate 1";
}
}
这个代码声明了一个合约,然后在构造函数中给candicate这个状态变量赋值,仅此而已。
- 在终端中打开项目目录,输入下面的命令部署合约
truffle migrate
image.png
- 部署后,输入
truffle console
,打开truffle终端,输入下面这行代码
Election.deployed().then(function(instance) { app = instance})
你会得到undefined错误,原因我们暂时先放放
然后输入app.address
,你会看到一个地址被打印出来,这就是合约地址,再输入app.candicate()
,你会看到打印出来的值是我们在构造函数中赋值给candicate状态变量的值
当我们声明candicate状态变量为public时,会自动生成同名的get函数来获取这个状态变量的值,我们通过创建的app对象访问candicate,便得到了candicate的值。此时,打开你的Ganache,你会发现有个账户的以太币减少了,这就是部署合约的开销。
image.png
- 到此,我们已经部署了一个很单纯的合约,只有读取变量的功能。接下来我们给Election.sol文件稍微增加些代码。我们把Candidate换成了一个结构体,添加了一个mapping及addCandidate方法。我们希望通过Candidate来保存某个Candidate的票数,通过addCandidate来创建候选人
pragma solidity ^0.4.2; //这句声明了我们的solidity版本
//声明一个合约 contract是关键字,Election是合约名字,合约名字和文件名要一致
contract Election {
/* 声明一个candicate结构 */
struct Candidate{
uint id;
string name;
uint voteCount;
}
/* 匹配到一个Candicate结构,通过一个mapping来做
mapping是key-value的,uint类型作为key,可以对应Candicate
结构里的id,而value则是一个Candicate对象
mapping操作会改变合约状态,将candicates放在区块链上存储*/
mapping (uint => Candidate) public candidates;
/* 我们需要手动记录mapping的大小,因为mapping不能被遍历
mapping的key对应的value如果不存在,则返回value类型的默认值
比如unit类型返回的是0,Candidate类型就返回空Candidate对象 */
uint public candidatesCount;
/* constractor 函数名和合约名一样的函数就是构造函数,与C++相似*/
/* 构造函数是public的,因为需要外部来调用 */
function Election() public{
addCandidate("candidate 1");
addCandidate("candidate 2");
}
/* 变量前加下划线表明这个变量是局部变量,不是状态变量 */
function addCandidate(string _name) private {
candidatesCount ++;
candidates[candidatesCount] = Candidate(candidatesCount, _name, 0);
}
}
修改完代码后,在终端中输入truffle migrate --reset
重新部署合约。加入--reset是因为之前的合约已经记录在区块链上了,无法修改,只能重新部署一套合约,重新部署的合约会生成新的合约地址
部署成功后,进入truffle终端,输入下面的代码创建对象
Election.deployed().then(function(instance) { app = instance})
然后输入下面的代码,创建candidate
app.candidates(1).then(function(c) { candidate = c;})
image.png
上图中访问
candidate
的成员id不能像C++等语言一样,通过.操作符来访问结构体中的成员,EVM不认识结构体,结构体只是Solidity里的一个语法糖,方便大家写而已。我们需要通过下标来访问candidate里的成员。打印出来的id显示为一个BigNumber,这是JavaScript的表示方法,c对应的是值,也就是id为1,正如我们创建的那样。
如果需要显示正常些,可以用toNumber()
方法和toString()
方法来得到正常些的结果
调用以太坊的接口
上面的程序已经可以记录候选人的票数,但是投票人怎么投票呢?在解决这个问题之前,我们先看看如何远程调用以太坊提供的方法(functions)
以太坊提供了一个web3.js库来访问以太坊内部的方法或者命令。
我们可以在truffle终端中调用下web3的方法,在truffle终端中输入web3.eth
,你会得到一堆输出
这就是web3中eth提供的函数,我们试试accounts,它会列出Ganache网络内的账户地址。这个命令与在自己搭建的私有链中查看accounts的调用方式不同之处在于前面加了个web3
image.png
合约测试
因为智能合约一旦部署,不可更改,合约的Bug也会随之在区块链上永垂不朽。如果要修复Bug,必须重新部署合约,这会极大影响用户体验,所以奋不顾身地测试合约也是必要的。测试文件可以是Solidity编写,也可以是JavaScript来写。我们先看看JavaScript如何写测试。
sudo npm install --global mocha
sudo npm install chai
var Election = artifacts.require("./Election.sol");
// 声明合约 第一个参数是段描述,描述你的测试,第二个参数是个函数
//函数中的it 是一个测试用例,一个contract可以包含多个it
contract("Election", function(accounts){
it("init with two candidates", function(accounts){
return Election.deployed().then(function (instance){
return instance.candidatesCount();
}).then(function(count){
assert.equal(count, 2);
});
});
});
写好之后在终端中打开项目目录,输入truffle test
,如果看到下面的输出,则测试通过
写在后面
第一篇教程就暂时写到这里,希望对大家有所帮助,后续教程即将来袭
网友评论
function Election() public{
^ (Relevant source part starts here and spans across multiple lines). 在实时编译网站就不会报错