haohao

在以太坊私有链上部署智能合约

Markdown

在自己的私有链上部署智能合约

上节简单介绍了基于以太坊搭建私有链以及挖矿和交易,在部署智能合约之前请确保私有链上的账户有余额,因为部署智能合约需要消耗 Gas ,而 Gas 需要 ether 币来兑换。

智能合约

什么是智能合约(Smart Contract)?智能合约是存储在以太坊网络特定地址的一组代码和数据集。在以太坊网络中智能合约以以太坊虚拟机(EVM)字节码的形式存在,由以太坊虚拟机解释执行。用于编写智能合约常用的语言有 Solidity 、Serpent 以及 LLL ,其中最著名的就是 Solidity 。智能合约的部署和执行都需要费用(Gas),一旦部署便不能修改。

部署智能合约

部署智能合约可以使用以太坊命令行客户端(Geth Console)和 Mist 。

选用 Solidity 官网的例子 Coin 。

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
pragma solidity ^0.4.0;
contract Coin {
// The keyword "public" makes those variables
// easily readable from outside.
address public minter;
mapping (address => uint) public balances;
// Events allow light clients to react to
// changes efficiently.
event Sent(address from, address to, uint amount);
// This is the constructor whose code is
// run only when the contract is created.
constructor() public {
minter = msg.sender;
}
function mint(address receiver, uint amount) public {
require(msg.sender == minter);
require(amount < 1e60);
balances[receiver] += amount;
}
function send(address receiver, uint amount) public {
require(amount <= balances[msg.sender], "Insufficient balance.");
balances[msg.sender] -= amount;
balances[receiver] += amount;
emit Sent(msg.sender, receiver, amount);
}
}

以太坊命令行客户端部署

打开网站https://remix.ethereum.org,将上面的智能合约代码替换为 Coin 的例子。

contract_browser

然后选择 Compile ,点击 Start to compile 生成 EVM 的字节码,复制生成的 ABIByteCode

contract_bytecode

ABI:

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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
[
{
"constant": false,
"inputs": [
{
"name": "receiver",
"type": "address"
},
{
"name": "amount",
"type": "uint256"
}
],
"name": "mint",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "receiver",
"type": "address"
},
{
"name": "amount",
"type": "uint256"
}
],
"name": "send",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"name": "from",
"type": "address"
},
{
"indexed": false,
"name": "to",
"type": "address"
},
{
"indexed": false,
"name": "amount",
"type": "uint256"
}
],
"name": "Sent",
"type": "event"
},
{
"inputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"constant": true,
"inputs": [
{
"name": "",
"type": "address"
}
],
"name": "balances",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "minter",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
}
]

ByteCode, 其中 object 字段对应的就是就是我们需要的 EVM 字节码(下面只贴出来部分代码):

1
2
3
4
5
6
7
{
"linkReferences": {},
"object": "608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506104df806100606000396000f300608060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806307546172146...",
"opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH2 0x10 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP CALLER PUSH1 0x0 DUP1 PUSH2 0x100 EXP DUP2 SLOAD DUP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF MUL NOT AND SWAP1 DUP4 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND MUL OR SWAP1 SSTORE POP PUSH2 0x4DF DUP1 PUSH2 0x60 PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN STOP PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH2 0x62 JUMPI
...",
"sourceMap": "24:902:0:-;;;427:57;8:9:-1;5:2;;;30:1;27;20:12;5:2;427:57:0;467:10;458:6;;:19;;;;;;;;;;;;;;;;;;24:902;;;;;;"
}

另外 ABI 需要转义成字符串,使用网站http://www.bejson.com, 选择【删除空格并转义】。
转义成字符串的 ABI :

1
[{\"constant\":false,\"inputs\":[{\"name\":\"receiver\",\"type\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"receiver\",\"type\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"send\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Sent\",\"type\":\"event\"},{\"inputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"address\"}],\"name\":\"balances\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"minter\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]

通过 Geth Console 创建合约对象。

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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// 声明合约 coin
> var abi = JSON.parse('[{\"constant\":false,\"inputs\":[{\"name\":\"receiver\",\"type\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"receiver\",\"type\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"send\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Sent\",\"type\":\"event\"},{\"inputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"address\"}],\"name\":\"balances\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"minter\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]')
> coin = web3.eth.contract(abi)
// 需要为字节码添加,'0x' 前缀。
> byteCode = "0x608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506104df80610060600039
6000f300608060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063075461721461006757806327e235e3146100be57806340c10f1914610115578063d0679d34146101
..."
"0x608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506104df806100606000396000f30060806
0405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063075461721461006757806327e235e3146100be57806340c10f19146101..."
// 就是部署智能合约需要的燃料(Gas)值。
> eth.estimateGas({data: byteCode})
406661
// 我们输入的燃料值需要大于 406661
>
// 解锁 coinbase 账户
> personal.unlockAccount(eth.coinbase)
Unlock account 0x33213084015dab454fee16eb369fa2a8e6e65eee
Passphrase:
true
// 创建 coin 合约对象,部署合约
> coinContractIns = coin.new({data: byteCode gas: 410000, from: eth.coinbase}, function(e, contract){
if(!e){
if(!contract.address){
console.log("Contract transaction send: Transaction Hash: "+contract.transactionHash+" waiting to be mined...");
}else{
console.log("Contract mined! Address: " + contract.address);
console.log(contract);
}
}else{
console.log(e)
}
})
INFO [10-07|12:19:00.077] Submitted contract creation fullhash=0x114e241d2a62064b0d41ec143c595768f648306595a931563a3add4cee3c2d77 contract=0x7227255a42b167061B4260A0b1827e3E6F737905
Contract transaction send: Transaction Hash: 0x114e241d2a62064b0d41ec143c595768f648306595a931563a3add4cee3c2d77 waiting to be mined...
{
abi: [{
constant: false,
inputs: [{...}, {...}],
name: "mint",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function"
}, {
constant: false,
inputs: [{...}, {...}],
name: "send",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function"
}, {
anonymous: false,
inputs: [{...}, {...}, {...}],
name: "Sent",
type: "event"
}, {
inputs: [],
payable: false,
stateMutability: "nonpayable",
type: "constructor"
}, {
constant: true,
inputs: [{...}],
name: "balances",
// coin 合约并没有部署成功,需要启动挖矿
> miner.start();
> // 等待约 12 以上区块生成,停止挖矿
> miner.stop();
// 查看合约是否部署成功
> eth.getCode(coinContractIns.address)
"0x608060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063075461721461006757806327e235e31461
b600080fd5b34801561007357600080fd5b5061007c6101af565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff
00080fd5b506100ff600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506101d4565b6040518082815260200191505060405180
..."
// 合约部署成功
//解锁账户
> eth.defaultAccount=eth.coinbase
"0x33213084015dab454fee16eb369fa2a8e6e65eee" // coinbase 账户
> personal.unlockAccount(eth.coinbase)
Unlock account 0x33213084015dab454fee16eb369fa2a8e6e65eee
Passphrase:
true
// 查看账户在合约中的余额
> coinContractIns.balances("0x33213084015DAB454fEe16eb369FA2a8E6E65eEE") //替换成自己的 coinbase 账户
0
// 由于合约没有被执行,合约中的余额为 0
// 通过合约中的 mint 接口设置 coinbase 账户在合约中的 coin 数目为 30000 。
> coinContractIns.mint("0x33213084015DAB454fEe16eb369FA2a8E6E65eEE", 30000);
INFO [10-07|12:37:31.191] Submitted transaction fullhash=0x788857480e7c33d59f5893e7287c697d33e5ad29a52817e19930ec26178e62b3 recipient=0x7227255a42b1
"0x788857480e7c33d59f5893e7287c697d33e5ad29a52817e19930ec26178e62b3"
// 上述操作并未执行,需要启动挖矿
> miner.start();
> // 等待约 12 以上区块生成,停止挖矿
> miner.stop();
// 挖矿结束,查看 coinbase 账户 coin 数量
> coinContractIns.balances("0x33213084015DAB454fEe16eb369FA2a8E6E65eEE")
30000
//解锁 coinbase 账户,进行交易
> personal.unlockAccount(eth.coinbase)
Unlock account 0x33213084015dab454fee16eb369fa2a8e6e65eee
Passphrase:
true
// 发送给账户 0x55213084015DAB454fEe16eb369FA2a8E6E65ebb(这个账户可以随意填写,保证 20 Byte)10000 个 coin
> coinContractIns.send("0x55213084015DAB454fEe16eb369FA2a8E6E65ebb", 10000);
INFO [10-07|12:47:17.161] Submitted transaction fullhash=0xf0ec8dc4daf6d85cf55be8289a37fbd271437658b2a02fc330272403192d863e recipient=0x7227255a42
"0xf0ec8dc4daf6d85cf55be8289a37fbd271437658b2a02fc330272403192d863e"
// 交易还未执行,这个账户余额还是 0
> coinContractIns.balances("0x55213084015DAB454fEe16eb369FA2a8E6E65ebb")
0
// 上述操作并未执行,需要启动挖矿
> miner.start();
> // 等待约 12 以上区块生成,停止挖矿
> miner.stop();
// 挖矿结束,查看账户的 coin 余额,合约执行成功。
> coinContractIns.balances("0x55213084015DAB454fEe16eb369FA2a8E6E65ebb")
10000
> coinContractIns.balances("0x33213084015DAB454fEe16eb369FA2a8E6E65eEE")
20000
>

Mist 部署

通过 Mist 部署智能合约非常简单方便。打开 Mist ,选择【CONTRACT】->【DEPLOY NEW CONTRACT】,在 【SOLIDITY CONTRACT SOURCE CODE】 中,将 coin 例子代码粘贴进来,将合约改为 LTCCoin 。

mist_depo_code
https://github.com/EthFans/wiki/wiki/%E6%99%BA%E8%83%BD%E5%90%88%E7%BA%A6
点击【DEPLOY】输入账户密码创建合约(这里创建账户选择 Account1)。

mist_depoing

点击【WALLETS】在下面的【Latest Transaction】可以看到合约正在创建中,这里需要在 Geth Console 启动挖矿,合约才能创建成功,当然进行任何交易操作都需要挖矿来完成。

mist_depoing2

挖矿后,选择【CONTRACT】查看合约部署成功。

mist_dep_success

然后在右侧【Select function】选择相应操作,注意执行 mint 函数执行账户需要选择为创建账户 Account1 ,交易成功需要挖矿。

mist_mint

参考

https://github.com/EthFans/wiki/
https://solidity.readthedocs.io



联系我

Wechat ID

公众号

生活不止于眼前的苟且, 还有诗和远方的田野