本地自动化编译、部署和调用智能合约

Posted by sulenn on 2019-04-30
字数:1.2k          阅读量:

本地自动化编译、部署和调用智能合约

因为师门工作需要,我必须完成如题所示的相关工作。但是在网上搜了半天,确发现没有一个博文完全贴合我的需求。要么是内容不全,要么是手动编译、部署和调用智能合约。于是只能自己慢慢摸索,在遇到 n 多坑,排了 n 多雷之后,终于成功了。遂向大家分享一下经验,有需自取。欢迎打扰、讨论。(补充:如有大牛,请自行绕过……)

1. 环境要求

具体环境如何安装,请 google,小编就不赘述了。这里放上各种软件和对应版本

  1. 操作系统:Ubuntu 18.04

  2. nodejs:10.15.3

  3. npm:6.4.1

  4. geth:1.9.0-unstable

  5. solc:0.5.1

  6. web3:0.20.0

  7. vscode:1.33.1

2. 本地模拟搭建双节点私有链

具体如何搭建请 google,有很多教程,很简单。这里注意,因为我们要使用 web3 来和 geth 节点交互,于是我们在启动 geth 时需要顺便将 rpc 服务开启

以下是两个节点启动的指令(其实就是打开两个 bash ,输入如下指令):

  • 节点1:geth --datadir data --networkid 8081 --port 8082 --unlock 0 --rpc --rpcport "8545" --rpcapi admin,eth,miner,personal,txpool,eth,web3,net console

  • 节点2:geth --datadir data --networkid 8081 --port 8083 console --bootnodes "enode://926a5eae181ef9ff3bbf8e16a24f9734e2a9084f7950d23363a05aac386b5967374c8913a6b84b263c8d468eade0fcf50e63ee32d4a31377df46cb525be6bdfa@127.0.0.1:8082" --unlock 0

==注意==:两个节点的 networkid 必须相同。节点2中 –bootnodes 的值需要动态调整。这些内容在其它各种教程中都有。此外 –unlock 0 配置的意思是将 geth 中默认账户解锁,如果不解锁的话就无法利用该账户发起交易。

如果顺利的话可以在节点1中运行 admin.peers,如果出现如下图所示内容,就说明私有链搭建成功

2

在节点2中运行 miner.start(),让节点2一直处于挖矿状态,以便之后的操作

3. 编译、部署和调用智能合约

这里直接贴代码吧。用的编辑器和各种配置在第一部分已经贴出来了,这儿就不赘述了。代码中重要的部分都有注释,方便理解。讲解一下大致流程吧:

  1. 引入 solc 包,主要用于编译智能合约

  2. 声明智能合约,功能就是 set() - 给字符串赋值,get() - 获取字符串值。此外我们将字符串初始化值为 “qiubing”,便于验证

  3. 调整编译所需格式,调用 solc.compile() 方法,编译智能合约,获得 output

  4. 解析 output ,获得 abi 和 bytecode,这两个内容都是部署合约必须的

  5. 引入 web3 包,用于连接 geth,部署和调用智能合约

  6. 判断 geth 连接是否成功

  7. 调用 web3.eth.contract() 方法,初始化 abi

  8. 调用 trustietransactionContract.new()方法,部署智能合约

  9. 部署成功之后,获得合约地址,然后在回调函数中调用 contract.get() 获得智能合约中字符串初始化的值 “qiubing”

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
var solc = require('solc');

// 待编译的合约源码
var contractCode = "pragma solidity >=0.4.0 <0.6.0;" +
"contract trustieTransaction {" +
"string public storedData = 'qiubing';" +
"function set(string memory x) public { storedData = x;}" +
"function get() public view returns (string memory) { return storedData;}}";

var input = {
language: 'Solidity',
sources: {
'trustieTransaction.sol': {
content: contractCode
}
},
settings: {
outputSelection: {
'*': {
'*': [ '*' ]
}
}
}
}

//编译合约源码
var output = JSON.parse(solc.compile(JSON.stringify(input)))

//获取编译后的 bytecode、abi 和 gas
var abi = JSON.stringify(output.contracts['trustieTransaction.sol'].trustieTransaction.abi);
// console.log("abi:", abi);
var bytecode = output.contracts['trustieTransaction.sol'].trustieTransaction.evm.bytecode.object;
// console.log("\n\nbytecode:", bytecode);
var gas = output.contracts['trustieTransaction.sol'].trustieTransaction.evm.gasEstimates.creation.totalCost;
// console.log("\n\ngas:", gas);


// 连接本地启动的 geth rpc 服务,连接成功之后进行如发起交易等操作
var Web3 = require('web3')

// 连接本地启动的 geth rpc 服务
if (typeof web3 !== 'undefined') {
web3 = new Web3(web3.currentProvider);
} else {
web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));//默认http://localhost:8545
}

// 判断是否连接成功
if(!web3.isConnected()) {
console.log("连接失败!!")
} else {
console.log("\n连接成功!!\n")
}

// 本地部署经过编译的合约
var trustietransactionContract = web3.eth.contract(JSON.parse(abi));
var trustietransaction = trustietransactionContract.new(
{
from: web3.eth.accounts[0],
data: "0x" + bytecode,
gas: '4700000' //修改源码,将 storedData 字符串初始化为 “qiubing”。编译源码获得的 gas 值为 `infinite`,导致出错,于是这儿固定一个 `4700000`值
// gas: String(parseInt(gas) * 10) //由于 solc 编译出来的 gas 过低,会导致 gas 不足的问题,于是 * 10
}, function (e, contract){
// console.log(e, contract);
if (typeof contract.address !== 'undefined') {
console.log('\nContract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash + "\n");

// 检测合约是否部署成功
var contract = web3.eth.contract(JSON.parse(abi)).at(contract.address);
var result = contract.get();
console.log("\n" + result)
}
})

如果一切顺利的话可以获得如下输出:

2

恭喜您!又 get 到一项技能!

参考