Truffle 框架开发 Dapp 中的注意事项

介绍

Truffle 是开发 Ethereum 的一个框架。

编写合约

概述

智能合约使用的是 solidity 语言,这个语言在一些地方与平常使用的高级语言不同,因而可能会遇到一些坑。

注意事项

变量

  • solidity 中变量分为两类:memory 和 storage,前者就类似于我们平常语言中的变量,存放在内存中,在调用结束后就会释放掉。
  • 而 storage 类型的变量则会存放在区块链中,因而它实际表示的是指针,也正是因为这个原因,这两种变量不能混合使用。
  • 另外,定义在 Contract 函数之外的变量默认是 storage 类型的。

事件

  • solidity 中 event 的作用就是把发生的事件记录在区块链上成为日志。事件可以通过 web3 来监听。
1
2
3
4
5
// 定义事件
event transfer(address indexed _from, address indexed _to, uint indexed value);

// 事件发生,新版本需要加入 emit 关键词
emit transfer(msg.sender, current, value);

映射

1
mapping(address => User) userPool;
  • solidty 中的映射不需要初始化,而且对于不在映射中的内容,通过 m[key] 来调用并不会返回类似于 null 的内容,而是会返回这个类型的默认值,比如 uint 返回的就是 0,因而要检查是否在映射中的时候可能需要辅助的属性:
1
2
3
4
5
6
7
8
9
struct Num {
uint count,
bool inMap
}

mapping(uint => Num) numMap;

if (numMap[1].inMap) { 在映射中 }
else { 不在映射中 }

字符串比较

  • solidity 中不能直接使用 == 来比较字符串,需要一个一个字符来比较或者来比较二者的 hash 值:
1
keccak256(str1) == keccak256(str2)

msg.sender && tx.origin

  • 在用户调用合约的时候,msg.sender 和 tx.origin 都是用户的地址
  • 在合约调用合约的时候,msg.sender 是 合约的地址,而 tx.origin 是用户

这个一定要注意,我就在这里卡了很久。

三个常用方法

require()

里面是一个 bool 表达式,为真的时候才会执行下面的内容。用来检查不严重的错误,可以退回 gas。

assert()

这个方法和 require 类似,但是一般是用来检查严重的错误,会拿走 gas。

revert()

这个方法里面没有参数,会终止并且恢复之前的状态。

数组

  • solidity 数组可以定义不定长的数组,但是要初始化空的不定长数组则要使用 new
1
2
3
4
5
// 这里默认是长度为一的数组
// 但是可以通过两种方法来改变:
// 1. 直接改变长度:list.length++
// 2. 通过 push 来增加元素 list.push(123)
uint[] list = new int[](1);

truffle 常用命令

compile

1
truffle compile

这个命令用来把合约编译成 EVM 可以读懂的语言

migrate

1
truffle migrate

这个命令用来把合约部署到区块链上,在修改合约之后就不用再次编译,直接使用这个命令部署即可

如果想清除掉之前的数据而不像删除掉编译已生成的 build 则可以加一个 --reset 参数

test

1
truffle test

这个命令会运行 test 文件夹中的单元测试,其中单元测试文件要以 Test 作为开头,单元测试方法需要以 test 开头

在 js 中调用合约

创建 web3 对象

1
2
3
4
5
6
7
if (typeof web3 !== 'undefined') {
App.web3Provider = web3.currentProvider;
web3 = new Web3(web3.currentProvider);
} else {
App.web3Provider = new Web3(new Web3.providers.HttpProvider('http://localhost:7545'));
web3 = new Web3(web3.web3Provider);
}

如果已经存在就直接绑定,不存在的话就根据本地的内容来创建再绑定。

读取合约

1
2
3
4
5
6
7
8
9
10
$.getJSON('TeamMarket.json', function(data) {
var TMArtifact = data;
App.contracts.TeamMarket = TruffleContract(TMArtifact);

App.contracts.TeamMarket.setProvider(App.web3Provider);

App.initCompetitions();

return App.markJoined();
});

这里的 json 文件是编译所产生的,其中要注意 getJSON 中的 JSON 是大写的,不然就会出现傻逼的错误(说的就是我……)。

另外在 HTML 部分记得要引入 truffle 相应的 js 文件。

和合约交互

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
web3.eth.getAccounts(function(error, accounts) {
if (error) {
console.log(error);
}

var account = accounts[0];

App.contracts.TeamMarket.deployed().then(function(instance) {
TMInstance = instance;

return TMInstance.createUser(name, email, major);
}).then(function () {
alert("创建账号成功,等待写入区块链");
}).catch(function(err) {
alert("有错误发生,创建账号失败");
console.log(err.message);
});
});

这里是通过 web3 接口来读取用户信息,返回的是一个 list,一般第一个就是当前用户

然后获取合约的实例App.contracts.TeamMarket.deployed(),这里是一个 Promise,在 then 里面就已经获取到了相应的实例,然后再通过这个实例来调用合约里面的方法就可以了。

不过在调用合约之后的 then 中不知道为什么有时候不会执行里面的语句……还得研究研究……

配置文件

truffle.js

1
2
3
4
5
6
7
8
9
module.exports = {
networks: {
development: {
host: "127.0.0.1",
port: 7545,
network_id: "*",
}
}
};

这里是配置区块链网络

package.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"name": "teamseeker",
"version": "1.0.0",
"description": "",
"main": "truffle.js",
"directories": {
"test": "test"
},
"scripts": {
"dev": "lite-server",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"lite-server": "^2.3.0"
},
"dependencies": {
"web3": "^1.0.0-beta.34"
}
}

这里更多是运行的 js 的一系列配置了,比如在这里配置了成序运行所使用的服务器