使用Truffle开发以太坊智能合约

  1. 1. 什么是Truffle
  2. 2. 使用Truffle部署剪刀石头布智能合约
  3. 3. 后话

什么是Truffle

如果你的第一反应是什么是以太坊,那么谷歌应该会回答的很详细。

以太坊智能合约使用编程语言Solidity书写,很多开发者一看到要学习一门新语言就望而却步。然而其实仔细看Solidity的语法,就会发现对于大多数程序员来说还是比较容易上手的。

而其实开发以太坊智能合约的瓶颈在于如何部署,调试,测试。

使用Truffle,可以很好解决这一系列问题。

使用Truffle部署剪刀石头布智能合约

首先用npm 安装Truffle。

1
npm install truffle -g

我们用truffle来部署一个简单的见到石头布的游戏。

游戏规则如下:
游戏双方首先各自向合约地址转账至少5wei,然后当准备就绪之后,双方调用play函数输入剪刀石头布的选择,最后智能合约自动将钱转账给赢家。

由于本文目的是熟悉Truffle开发,部署,调试智能合约的步骤,所以智能合约我事先已经准备好了。
请直接使用这里的内容

不过由于我们要熟悉Truffle的使用步骤,所以请不要fork整个repo。

首先创建一个新的目录。初始化Truffle。

1
2
3
mkdir rps
cd rps
truffle init

这个时候会多出来几个东西。

1
2
3
4
5
6
7
8
9
10
.
├── contracts
│   ├── Migrations.sol
│  
├── migrations
│   ├── 1_initial_migration.js
│  
├── test
├── truffle-config.js
└── truffle.js

创建contracts/rps.sol 文件,把上面提供的智能合约的内容粘贴进去。

然后我们需要创建migrations/2_deploy_contracts.js。其中内容如下。

1
2
3
4
5
var  Rps = artifacts.require("rps");

module.exports = function(deployer) {
deployer.deploy(Rps);
};

解释一下。contracts目录下面存放的就是智能合约文件,而migrations下面则是存放的部署文件。
这里你也许会感到奇怪一开始就存在的Migration.sol1_initial_migration.js是什么东西。
这个官方解释是帮助你获取部署的合约地址的合约。所以我们放着不管就行了。

接下来就是部署。
输入truffle development, Truffle会自动调用testrpc开启一个独立的testnet环境。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
➜  rps truffle dev
Truffle Develop started at https://localhost:9545/

Accounts:
(0) 0x627306090abab3a6e1400e9345bc60c78a8bef57
(1) 0xf17f52151ebef6c7334fad080c5704d77216b732
(2) 0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef
(3) 0x821aea9a577a9b44299b9c15c88cf3087f3b5544
(4) 0x0d1d4e623d10f9fba5db95830f7d3839406c6af2
(5) 0x2932b7a2355d6fecc4b5c0b6bd44cc31df247a2e
(6) 0x2191ef87e392377ec08e7c08eb105ef5448eced5
(7) 0x0f4f2ac550a1b4e2280d04c21cea7ebd822934b5
(8) 0x6330a553fc93768f612722bb8c2ec78ac90b3bbc
(9) 0x5aeda56215b167893e80b4fe645ba6d5bab767de

Mnemonic: candy maple cake sugar pudding cream honey rich smooth crumble sweet treat

下面的那串英文是用来恢复密钥,导入密钥的原始口令。不过不用担心,所有测试环境这串英文都一样。

首先编译

1
> compile

接下来部署。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
truffle(develop)> migrate --reset
Using network 'develop'.

Running migration: 1_initial_migration.js
Replacing Migrations...
... 0x9ff90a0b06bc3e9183807fe28a800030fa2b9330405eb3e86d29d16d147d83df
Migrations: 0x345ca3e014aaf5dca488057592ee47305d9b3e10
Saving successful migration to network...
... 0xcc6faa4193f59d7da676c7557e13271f41a8a97ca0ce171911aaf0355b92f11d
Saving artifacts...
Running migration: 2_deploy_contracts.js
Replacing rps...
... 0x44dbe0a34b7065946240a1bf14fe7b2e090aa976d5d5a8e649660952df4866fd
rps: 0x8f0483125fcb9aaaefa9209d8e9d7b9c8b9fb90f
Saving successful migration to network...
... 0x61826f086be719997aa398ef8cbc8ba8d3fb9cdd04f7486fb0f2e0c7565d2b59
Saving artifacts...

可以看到我们的智能合约倍部署到了rps: 0x8f0483125fcb9aaaefa9209d8e9d7b9c8b9fb90f这个地址。
我们引用这个地址并套上接口就可以调用智能合约里面的函数了。但是刚刚说过了,我们有migration.sol 这个合约来帮我们记住部署的地址,所以我们不需要关心这个地址,直接调用封装好的函数获取就行了。

Truffle的console中,默认使用第一个账户作为合约的调用者。

1
2
truffle(develop)> rps.deployed().then(function(instance){return instance.getMyBalance();});
BigNumber { s: 1, e: 19, c: [ 991693, 37699999999995 ] }

有一点比较在意的是,我们看到的blanance不是普通的整型,而是所谓的BigNumber。在Truffle里面可以直接调用bignumber.js的提供的扩展来将其转换成我们能理解的形式。

1
2
3
truffle(develop)> rps.deployed().then(function(instance){return instance.getMyBalance({from: "0xf17f52151ebef6c7334fad080c5704d77216b732"})}).then(function(value){return value.to
Number();});
99319236300000000000

我们再来看一下用户是否已经注册。

1
2
truffle(develop)> rps.deployed().then(function(instance){return instance.AmIPlayer1();});
false

可以看到用户还没有注册,现在我们开始注册用户。

1
2
3
4
5
6
7
8
9
10
11
12
truffle(develop)> rps.deployed().then(function(instance){return instance.register({value: 5});});
{ tx: '0x5d8ad0ba323971aef600d89cd9c7188edcf2b326265f9ff402be4626c7c2aba0',
receipt:
{ transactionHash: '0x5d8ad0ba323971aef600d89cd9c7188edcf2b326265f9ff402be4626c7c2aba0',
transactionIndex: 0,
blockHash: '0xaa5a88bc27b87e59556576fb4fcd613d4409c78308b957424bcafc42af9ad752',
blockNumber: 21,
gasUsed: 42526,
cumulativeGasUsed: 42526,
contractAddress: null,
logs: [] },
logs: [] }

value 表示转账的数目。这里转账数目是5wei。

接下来注册另一个用户。这个时候由于不是默认用户,所以我们在调用的时候需要带上。from 这个属性。

1
2
3
4
5
6
7
8
9
10
11
12
truffle(develop)> rps.deployed().then(function(instance){return instance.register({from: "0xf17f52151ebef6c7334fad080c5704d77216b732", value: 5});});
{ tx: '0x756985811c0543e8db681c47b500021dd1f3b202ddd090e837a5a8a11136a07e',
receipt:
{ transactionHash: '0x756985811c0543e8db681c47b500021dd1f3b202ddd090e837a5a8a11136a07e',
transactionIndex: 0,
blockHash: '0x96f175ead2a87824b556380923996cd96ce51ab02487c0ea1210a814493d6e26',
blockNumber: 22,
gasUsed: 42784,
cumulativeGasUsed: 42784,
contractAddress: null,
logs: [] },
logs: [] }

开始玩游戏。假设默认用户出石头,第二个用户出布。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
truffle(develop)> rps.deployed().then(function(instance){return instance.play("paper", {from: "0xf17f52151ebef6c7334fad080c5704d77216b732"});});
{ tx: '0x78a9447e2357db30a48eb5123d343d888571a6a4a43856ee5bd6be48c78963ec',
receipt:
{ transactionHash: '0x78a9447e2357db30a48eb5123d343d888571a6a4a43856ee5bd6be48c78963ec',
transactionIndex: 0,
blockHash: '0x3e9bfed42be281c84b6a8d9041a9e2d816cbec14ddd722afb25c10f94cadcd6e',
blockNumber: 23,
gasUsed: 44004,
cumulativeGasUsed: 44004,
contractAddress: null,
logs: [] },
logs: [] }

truffle(develop)> rps.deployed().then(function(instance){return instance.play("rock");});
{ tx: '0xac42883642732fa4fc6e62e9b7edc6d6a79a33c2331d3c1f7022ad4b706b96e6',
receipt:
{ transactionHash: '0xac42883642732fa4fc6e62e9b7edc6d6a79a33c2331d3c1f7022ad4b706b96e6',
transactionIndex: 0,
blockHash: '0x28b225671df0d97d734649def71d2a962c37cded0456d18fe665b4185685e020',
blockNumber: 24,
gasUsed: 60932,
cumulativeGasUsed: 60932,
contractAddress: null,
logs: [] },
logs: [] }

然后我们看一下谁是赢家。

1
2
truffle(develop)> rps.deployed().then(function(instance){return instance.getLastWinner();});
'0xf17f52151ebef6c7334fad080c5704d77216b732'

后话

Truffle的debug console 非常强大。如果熟悉JS,那么其实也十分容易上手。
本文没有涉及test。不过有兴趣的话可以看一下Truffle官网的教程,也是手把手,十分容易上手。
那么就先这样吧。

如果你觉得本文对你有帮助,请给我点赞助。