Zihao

Make small but daily progress

0%

0x10--智能合约开发之十分钟学会开发智能合约全流程

1. 学习目标

  1. 了解智能合约
  2. 简单环境搭建
  3. 能够利用solidity编写Hello World合约
  4. 合约部署
  5. 和合约互动

2. 使用solidity语言撰写智能合约

Ethereum上的智能合约需要使用solidity语言来撰写。solidity是一种类似Javsscript的语言,而且围绕着solidity的各种开发工具链,都是使用属于Javascript生态系的npm来提供的。但solidity在语法上比较像Java或C#,因为和Javascript不同,solidity与Java或C#同属于强类型语言,、在定义函数时同样需要指定回传的类型、同样也需要先编译才能执行。这些特性都是Javascript所不具备的。

3. 开发前的准备

我们将使用当前最活跃的智能合约开发框架truffle为基础来开发。ENS(Ethereum Name Service)也是采用truffle框架。其他选择还有embark等。

开发过程中,我们将使用testrpc工具在电脑上模拟智能合约所需的以太坊内存块链测试环境。

testrpc中也包含了Javascript版本的Ethereum虚拟机(Ethereum Virtual Machine),因此可以完整地执行智能合约。

代码编辑器使用Atom,搭配solidity插件来开发。Atom是Github为开发者设计的基于Chromium的编辑器,支持NodeJS写的插件,内置Git,使用MIT协议开源发布。solidity插件除了支持语法高亮之外,也会透过Solium检查并提示基本的语法错误,相当方便。其他编辑器应该也有类似的插件可选择。

Ubuntu环境下Atom编辑器安装及使用

1
2
3
sudo add-apt-repository ppa:webupd8team/atom
sudo apt-get update
sudo apt-get install atom

安装插件linter-solidity、autocomplete-solidity、linter-solium以方便solidity代码编写。

4.安装所需工具

首先安装Node.js

安装nvm

1
2
3
4
cd ~
mkdir .nvm
cd .nvm
git clone https://github.com/creationix/nvm

等待下载完毕后source ~/.nvm/nvm/nvm.sh添加到~/.profile之类的文件中

1
vim ~/.profile

并在文件末尾添加source ~/.nvm/nvm/nvm.sh

1
2
source ~/.profile
nvm --version

检查安装是否正确

安装node

1
2
nvm install node
node --version

安装npm

1
2
3
4
git clone --recursive git://github.com/isaacs/npm.git
cd npm/bin
node npm-cli.js install npm -g
npm --version

安装ethereumjs-testrpc和truffle

1
npm install -g ethereumjs-testrpc truffle
1
2
3
4
5
6
7
zihao@zihaodeMBP:~$ npm install -g ethereumjs-testrpc truffle

/home/zihao/.nvm/nvm/versions/node/v9.3.0/bin/testrpc -> /home/zihao/.nvm/nvm/versions/node/v9.3.0/lib/node_modules/ethereumjs-testrpc/build/cli.node.js
/home/zihao/.nvm/nvm/versions/node/v9.3.0/bin/truffle -> /home/zihao/.nvm/nvm/versions/node/v9.3.0/lib/node_modules/truffle/build/cli.bundled.js
+ [email protected]
+ [email protected]
added 343 packages in 115.81s

5.启动testrpc

安装好后随时可以使用testrpc命令来启动以太坊测试环境。

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
zihao@zihaodeMBP:~$ testrpc
EthereumJS TestRPC v6.0.3 (ganache-core: 2.0.2)

Available Accounts
==================
(0) 0x7e1897cec8e1a098384d17e85a9cd348ccb92b4a
(1) 0x6c96c6cb16442ab83aea0eb1bc3a183e28a922fe
(2) 0x163f0e06082c5cd14ff56658b186a30b0f6e7d9f
(3) 0x0e9558b2cb935ab26484c3d6783fbf2fe70041c8
(4) 0xa2233f08bbb7e3a826cc4c2cede22caf3a6922e6
(5) 0x3b1317510218f98946e0fb72b1a0685ac00bcbc4
(6) 0xca33f56a03e5ae86e25655f4dd7de88be12ec184
(7) 0xfb56aed85dd00908f1e0729ed80d06f456898588
(8) 0x6cfe240b49a28986057016ef2ecd9d6711f6b632
(9) 0x0e866473d6776edc39935dd5d00425b6ab2a423a

Private Keys
==================
(0) 3ae87bfaeabf103ce723b78525ab1278b2c7652b93b99a0131321adaa7000537
(1) 2f23eaef51f941b7af171a56acbd7524554ad529236b6abb6920935218f5df58
(2) 9b5b5d42786e74364e80b45ecf1a2b7201db7eafe12684f71006fbe3368e6dfe
(3) 33181b42772371fad50f29b8377f7a3de72000397f9b021677e329bede7bfcc7
(4) 3f5f366d89ab9263b92f35368840d24d562af043db8a5ad773dc8cc8f122662d
(5) 29933e3e79129da23da3cc0f5cecb03c583c3ad6c4c1ad8a39d68386fe92d91b
(6) 7e9bd47a6120bb9a240a96ce80866b98646f07cb98197bdffec389805575144b
(7) 0c7144621371a04e4ee733d06482b0a23ed775af17172e75e26f2b8915814e35
(8) 60ab702bdad5a348645f65fb0678cafec891c534964982d97b96f4a57abd78ef
(9) 8ff45f1ccb23bec8a317a6e218b3b50290646317b5c809c797e1522bcef9d031

HD Wallet
==================
Mnemonic: vapor high liberty evoke pledge bronze critic stomach neck figure recipe mind
Base HD Path: m/44'/60'/0'/0/{account_index}

Listening on localhost:8545

可以看到testrpc启动后自动建立了10个账号(Accounts),与每个账号对应的私钥(Private Key)。每个账号中都有100个测试用的以太币(Ether)。要注意testrpc仅运行在内存中,因此每次重开时都会回到全新的状态。

一切就绪,我们可以开始建立第一个智能合约项目了。

6.建立项目

开启另一个终端,输入以下命令以建立项目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
zihao@zihaodeMBP:~$ mkdir SmartContractDemo
zihao@zihaodeMBP:~$ cd SmartContractDemo/
zihao@zihaodeMBP:~/SmartContractDemo$ mkdir HelloWorld
zihao@zihaodeMBP:~/SmartContractDemo$ cd HelloWorld/
zihao@zihaodeMBP:~/SmartContractDemo/HelloWorld$ truffle init
Downloading...
Unpacking...
Setting up...
Unbox successful. Sweet!

Commands:

Compile: truffle compile
Migrate: truffle migrate
Test contracts: truffle test
zihao@zihaodeMBP:~/SmartContractDemo/HelloWorld$ ls
contracts migrations test truffle-config.js truffle.js
zihao@zihaodeMBP:~/SmartContractDemo/HelloWorld$

目录结构

/contracts:存放智能合约原始代码的地方,可以看到里面有个Migrations.sol文件,我们的HelloWorld.sol文件就存放在这里。

/migrations:这是Truffle用来部署智能合约的功能,待会儿我们会修改2_deploy_contracts.js来部署HelloWorld.sol

/test:测试智能合约的代码放在这里,支持jssol测试。

truffle.js:Truffle的设置文档。

7.新建HelloWorld合约

contracts文件夹下新建HelloWorld.sol文件,当然也可以直接在HelloWorld路径下面直接执行truffle create contract HelloWorld命令来创建HelloWorld.sol

1
2
3
4
5
6
7
zihao@zihaodeMBP:~/SmartContractDemo/HelloWorld$ ls
contracts migrations test truffle-config.js truffle.js
zihao@zihaodeMBP:~/SmartContractDemo/HelloWorld$ truffle create contract HelloWorld
zihao@zihaodeMBP:~/SmartContractDemo/HelloWorld$ cd contracts/
zihao@zihaodeMBP:~/SmartContractDemo/HelloWorld/contracts$ ls
HelloWorld.sol Migrations.sol
zihao@zihaodeMBP:~/SmartContractDemo/HelloWorld/contracts$

HelloWorld.sol的文件内容如下:

1
2
3
4
5
6
7
pragma solidity ^0.4.4;

contract HelloWorld {
function sayHello() returns (string) {
return ("Hello World");
}
}

代码说明

1
pragma solidity ^0.4.4;

第一行表明目前使用的solidity版本,不同版本的solidity可能会编译出不同的bytecode^代表兼容solidity 0.4.4~0.4.9的版本。

1
2
3
contract HelloWorld {
...
}

contract关键字类似于其他语言中较常见的class。因为solidity是专为智能合约(Contract)设计的语言,声明contract后即内置了开发智能合约所需的功能。也可以把这句理解为

1
2
3
function sayHello() returns (string) {
return ("Hello World");
}

函数的结构与其他程序类似,但如果有传入的参数或回传值,需要指定参数或回传值的类型(type)。

8.编译

现在执行truffle compile命令,我们可以将HelloWorld.sol原始码编译成Ethereum bytecode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
zihao@zihaodeMBP:~/SmartContractDemo/HelloWorld$ ls
contracts migrations test truffle-config.js truffle.js
zihao@zihaodeMBP:~/SmartContractDemo/HelloWorld$ truffle compile
Compiling ./contracts/HelloWorld.sol...
Compiling ./contracts/Migrations.sol...

Compilation warnings encountered:

/home/zihao/SmartContractDemo/HelloWorld/contracts/HelloWorld.sol:4:3: Warning: No visibility specified. Defaulting to "public".
function sayHello() returns (string) {
^
Spanning multiple lines.
,/home/zihao/SmartContractDemo/HelloWorld/contracts/HelloWorld.sol:4:3: Warning: Function state mutability can be restricted to pure
function sayHello() returns (string) {
^
Spanning multiple lines.

Writing artifacts to ./build/contracts

zihao@zihaodeMBP:~/SmartContractDemo/HelloWorld$ ls
build contracts migrations test truffle-config.js truffle.js

编译成功后,会在HelloWorld文件夹下面的build/contracts文件夹下面看见HelloWorld.json文件。

9.部署

truffle框架中提供了方便部署合约的脚本。打开migrations/2_deploy_contracts.js文件(脚本使用Javascript编写),将内容修改如下:

1
2
3
4
var HelloWorld = artifacts.require("HelloWorld");
module.exports = function(deployer) {
deployer.deploy(HelloWorld);
};

使用artifacts.require语句来取得准备部署的合约。使用deployer.deploy语句将合约部署到区块链上。这边HelloWorldcontract的名称而不是文件夹。因此可以用此语法读入任一.sol文件中的任一合约。

现在执行truffle migrate命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
zihao@zihaodeMBP:~/SmartContractDemo/HelloWorld$ truffle migrate
Compiling ./contracts/HelloWorld.sol...

Compilation warnings encountered:

/home/zihao/SmartContractDemo/HelloWorld/contracts/HelloWorld.sol:4:3: Warning: Function state mutability can be restricted to pure
function sayHello() public constant returns (string) {
^
Spanning multiple lines.

Writing artifacts to ./build/contracts

Error: No network specified. Cannot determine current network.
at Object.detect (/home/zihao/.nvm/nvm/versions/node/v9.3.0/lib/node_modules/truffle/build/cli.bundled.js:41338:23)
at /home/zihao/.nvm/nvm/versions/node/v9.3.0/lib/node_modules/truffle/build/cli.bundled.js:202239:19
at /home/zihao/.nvm/nvm/versions/node/v9.3.0/lib/node_modules/truffle/build/cli.bundled.js:41263:11
at /home/zihao/.nvm/nvm/versions/node/v9.3.0/lib/node_modules/truffle/build/cli.bundled.js:41295:9
at <anonymous>

可以发现,此时在使用truffle migrate进行智能合约test环境发布时出现异常,原因是truffle.js里面未配置链接合约发布时的环境地址,找到对应的truffle.js文件,修改代码为类似如下配置即可解决问题:

1
2
3
4
5
6
7
8
9
module.exports = {
networks: {
development: {
host: "localhost",
port:8545,
network_id:"*" // 匹配任何network id
}
}
};

重新执行truffle migrate:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
zihao@zihaodeMBP:~/SmartContractDemo/HelloWorld$ truffle migrate
Using network 'development'.

Running migration: 1_initial_migration.js
Deploying Migrations...
... 0xd43c42b526addb75876bb26a3086cabf79eaf9b0dbb1cd18ce3d6cc78f8e5335
Migrations: 0x69c7674e74ee9aee187ea05b97faf08d6a7c0b94
Saving successful migration to network...
... 0x4aa706f72045aec809b3ab5d286489c157e6f5e73e1538a441cfb199c512f849
Saving artifacts...
Running migration: 2_deploy_contracts.js
Deploying HelloWorld...
... 0x35afc83dd8e594167f1ddb9f52250b02aa72508a9b77ad15f5bd58b19a1e7275
HelloWorld: 0x93ca4fd7d2820b2156fd88e40783ac9a41e26cfe
Saving successful migration to network...
... 0x12174aa188a4c47f84e071356149dfc9a51942bd81ce53d84bb22225d18d2fae
Saving artifacts...
zihao@zihaodeMBP:~/SmartContractDemo/HelloWorld$

如此以后,合约已经部署到testrpc中。切换到testrpc窗口,可以看到testrpc有反应了。

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
Listening on localhost:8545
net_version
eth_accounts
eth_accounts
net_version
net_version
eth_sendTransaction

Transaction: 0xd43c42b526addb75876bb26a3086cabf79eaf9b0dbb1cd18ce3d6cc78f8e5335
Contract created: 0x69c7674e74ee9aee187ea05b97faf08d6a7c0b94
Gas usage: 269607
Block Number: 1
Block Time: Wed Jan 10 2018 20:19:00 GMT+0800 (CST)

eth_newBlockFilter
eth_getFilterChanges
eth_getTransactionReceipt
eth_getCode
eth_uninstallFilter
eth_sendTransaction

Transaction: 0x4aa706f72045aec809b3ab5d286489c157e6f5e73e1538a441cfb199c512f849
Gas usage: 41981
Block Number: 2
Block Time: Wed Jan 10 2018 20:19:01 GMT+0800 (CST)

eth_getTransactionReceipt
eth_accounts
net_version
net_version
eth_sendTransaction

Transaction: 0x35afc83dd8e594167f1ddb9f52250b02aa72508a9b77ad15f5bd58b19a1e7275
Contract created: 0x93ca4fd7d2820b2156fd88e40783ac9a41e26cfe
Gas usage: 142468
Block Number: 3
Block Time: Wed Jan 10 2018 20:19:01 GMT+0800 (CST)

eth_newBlockFilter
eth_getFilterChanges
eth_getTransactionReceipt
eth_getCode
eth_uninstallFilter
eth_sendTransaction

Transaction: 0x12174aa188a4c47f84e071356149dfc9a51942bd81ce53d84bb22225d18d2fae
Gas usage: 26981
Block Number: 4
Block Time: Wed Jan 10 2018 20:19:01 GMT+0800 (CST)

eth_getTransactionReceipt

10.与合约互动

truffle提供命令行工具,执行truffle console命令后,可用Javascript来和刚刚部署的合约互动。

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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
zihao@zihaodeMBP:~/SmartContractDemo/HelloWorld$ ls
build contracts migrations test truffle-config.js truffle.js
zihao@zihaodeMBP:~/SmartContractDemo/HelloWorld$ truffle console
truffle(development)> let contract
undefined
truffle(development)> HelloWorld.deployed().then(instance => contract = instance)
TruffleContract {
constructor:
{ [Function: TruffleContract]
_static_methods:
{ setProvider: [Function: setProvider],
new: [Function: new],
at: [Function: at],
deployed: [Function: deployed],
defaults: [Function: defaults],
hasNetwork: [Function: hasNetwork],
isDeployed: [Function: isDeployed],
detectNetwork: [Function: detectNetwork],
setNetwork: [Function: setNetwork],
resetAddress: [Function: resetAddress],
link: [Function: link],
clone: [Function: clone],
addProp: [Function: addProp],
toJSON: [Function: toJSON] },
_properties:
{ contract_name: [Object],
contractName: [Object],
abi: [Object],
network: [Function: network],
networks: [Function: networks],
address: [Object],
links: [Function: links],
events: [Function: events],
binary: [Function: binary],
deployedBinary: [Function: deployedBinary],
unlinked_binary: [Object],
bytecode: [Object],
deployedBytecode: [Object],
sourceMap: [Object],
deployedSourceMap: [Object],
source: [Object],
sourcePath: [Object],
ast: [Object],
compiler: [Object],
schema_version: [Function: schema_version],
schemaVersion: [Function: schemaVersion],
updated_at: [Function: updated_at],
updatedAt: [Function: updatedAt] },
_property_values: {},
_json:
{ contractName: 'HelloWorld',
abi: [Array],
bytecode: '0x6060604052341561000f57600080fd5b6101578061001e6000396000f300606060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063ef5fb05b14610046575b600080fd5b341561005157600080fd5b6100596100d4565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561009957808201518184015260208101905061007e565b50505050905090810190601f1680156100c65780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6100dc610117565b6040805190810160405280600b81526020017f48656c6c6f20576f726c64000000000000000000000000000000000000000000815250905090565b6020604051908101604052806000815250905600a165627a7a72305820b98aa7cabcef6181bb855c27290b86a3ed577c3493b6cf1cebfeb4be9a990c2e0029',
deployedBytecode: '0x606060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063ef5fb05b14610046575b600080fd5b341561005157600080fd5b6100596100d4565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561009957808201518184015260208101905061007e565b50505050905090810190601f1680156100c65780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6100dc610117565b6040805190810160405280600b81526020017f48656c6c6f20576f726c64000000000000000000000000000000000000000000815250905090565b6020604051908101604052806000815250905600a165627a7a72305820b98aa7cabcef6181bb855c27290b86a3ed577c3493b6cf1cebfeb4be9a990c2e0029',
sourceMap: '25:112:0:-;;;;;;;;;;;;;;;;;',
deployedSourceMap: '25:112:0:-;;;;;;;;;;;;;;;;;;;;;;;;49:86;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:2;8:100;;;99:1;94:3;90;84:5;80:1;75:3;71;64:6;52:2;49:1;45:3;40:15;;8:100;;;12:14;3:109;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;49:86:0;94:6;;:::i;:::-;108:22;;;;;;;;;;;;;;;;;;;;49:86;:::o;25:112::-;;;;;;;;;;;;;;;:::o',
source: 'pragma solidity ^0.4.4;\n\ncontract HelloWorld {\n function sayHello() public constant returns (string) {\n return ("Hello World");\n }\n}\n',
sourcePath: '/home/zihao/SmartContractDemo/HelloWorld/contracts/HelloWorld.sol',
ast: [Object],
compiler: [Object],
networks: [Object],
schemaVersion: '1.0.1',
updatedAt: '2018-01-10T12:19:01.841Z' },
setProvider: [Function: bound setProvider],
new: [Function: bound new],
at: [Function: bound at],
deployed: [Function: bound deployed],
defaults: [Function: bound defaults],
hasNetwork: [Function: bound hasNetwork],
isDeployed: [Function: bound isDeployed],
detectNetwork: [Function: bound detectNetwork],
setNetwork: [Function: bound setNetwork],
resetAddress: [Function: bound resetAddress],
link: [Function: bound link],
clone: [Function: bound clone],
addProp: [Function: bound addProp],
toJSON: [Function: bound toJSON],
web3:
Web3 {
_requestManager: [RequestManager],
currentProvider: [Provider],
eth: [Eth],
db: [DB],
shh: [Shh],
net: [Net],
personal: [Personal],
bzz: [Swarm],
settings: [Settings],
version: [Object],
providers: [Object],
_extend: [Function] },
class_defaults:
{ from: '0x7e1897cec8e1a098384d17e85a9cd348ccb92b4a',
gas: 6721975,
gasPrice: 100000000000 },
currentProvider:
HttpProvider {
host: 'http://localhost:8545',
timeout: 0,
user: undefined,
password: undefined,
send: [Function],
sendAsync: [Function],
_alreadyWrapped: true },
network_id: '1515578597496' },
abi:
[ { constant: true,
inputs: [],
name: 'sayHello',
outputs: [Array],
payable: false,
stateMutability: 'view',
type: 'function' } ],
contract:
Contract {
_eth:
Eth {
_requestManager: [RequestManager],
getBalance: [Function],
getStorageAt: [Function],
getCode: [Function],
getBlock: [Function],
getUncle: [Function],
getCompilers: [Function],
getBlockTransactionCount: [Function],
getBlockUncleCount: [Function],
getTransaction: [Function],
getTransactionFromBlock: [Function],
getTransactionReceipt: [Function],
getTransactionCount: [Function],
call: [Function],
estimateGas: [Function],
sendRawTransaction: [Function],
signTransaction: [Function],
sendTransaction: [Function],
sign: [Function],
compile: [Object],
submitWork: [Function],
getWork: [Function],
coinbase: [Getter],
getCoinbase: [Function],
mining: [Getter],
getMining: [Function],
hashrate: [Getter],
getHashrate: [Function],
syncing: [Getter],
getSyncing: [Function],
gasPrice: [Getter],
getGasPrice: [Function],
accounts: [Getter],
getAccounts: [Function],
blockNumber: [Getter],
getBlockNumber: [Function],
protocolVersion: [Getter],
getProtocolVersion: [Function],
iban: [Function],
sendIBANTransaction: [Function: bound transfer] },
transactionHash: null,
address: '0x93ca4fd7d2820b2156fd88e40783ac9a41e26cfe',
abi: [ [Object] ],
sayHello:
{ [Function: bound ]
request: [Function: bound ],
call: [Function: bound ],
sendTransaction: [Function: bound ],
estimateGas: [Function: bound ],
getData: [Function: bound ],
'': [Circular] },
allEvents: [Function: bound ] },
sayHello:
{ [Function]
call: [Function],
sendTransaction: [Function],
request: [Function: bound ],
estimateGas: [Function] },
sendTransaction: [Function],
send: [Function],
allEvents: [Function: bound ],
address: '0x93ca4fd7d2820b2156fd88e40783ac9a41e26cfe',
transactionHash: null }
truffle(development)> contract.sayHello.call()
'Hello World'
truffle(development)>

truffle console中预载了truffle-contract函数库,以方便操作部署到区块链上的合约。使用了HelloWorld.deployed().then语句来取得HelloWorld合约的Instance(实例),并存到contract变量中,以方便后续的调用。

上面用的是Javascript ES6+的语法,这句也可以写成:

1
2
3
HelloWorld.deployed().then(instance => {
contract = instance
});

还可以用ES5的写法:

1
2
3
HelloWorld.deployed().then(function(instance) {
hello = instance;
});
1
2
truffle(development)> contract.sayHello.call()
'Hello World'

最后通过contract.sayHello.call()成功打印出"Hello World",这里直接调用contract.sayHello()也会得到一样的结果。truffle-contract提供使用call()来读取只读(read only)的数据,这样就不需提供gas。因此如果遇到的操作需要向区块链写入数据,我们就不能用call语句了。

这样,我们的第一个智能合约就已经写好并部署好了,也验证了合约确实可以运行。

11.加入新方法

我们在HelloWorld.sol中再加入一个echo方法,echo方法接受一个参数,并回传传送的参数。

1
2
3
function echo(string name) constant returns (string) {
return name;
}

新的echo方法中传入了一个name参数。我们也为echo方法加入一个constant声明,表示调用这个方法并不会改变区块链的状态。如此一来,透过truffle-contract来调用此方法时,会自动选用call来呼叫,也不需要额外提供gas

由于更新了合约内容,我们需要先重新编译一次,将编译结果部署到testrpc上,再通过truffle 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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
zihao@zihaodeMBP:~/SmartContractDemo/HelloWorld$ truffle migrate --reset
Compiling ./contracts/HelloWorld.sol...

Compilation warnings encountered:

/home/zihao/SmartContractDemo/HelloWorld/contracts/HelloWorld.sol:4:3: Warning: Function state mutability can be restricted to pure
function sayHello() public constant returns (string) {
^
Spanning multiple lines.
,/home/zihao/SmartContractDemo/HelloWorld/contracts/HelloWorld.sol:8:3: Warning: Function state mutability can be restricted to pure
function echo(string name) public constant returns (string) {
^
Spanning multiple lines.

Writing artifacts to ./build/contracts

Using network 'development'.

Running migration: 1_initial_migration.js
Replacing Migrations...
... 0xfc5af4b443dcae96f96206e26d2bc93b9699e7d14980dd0247baea112bd04c44
Migrations: 0xee671e4bb1e86df24120041587b0a4841924f7bd
Saving successful migration to network...
... 0x1d7e34e903cc04a5e5cd6e5cca1031034841bd001dd693b488500fe75c4df9e2
Saving artifacts...
Running migration: 2_deploy_contracts.js
Replacing HelloWorld...
... 0xdb8c8ef861498972ad769f947e9c03872b3f17f7292deff3fa25d5f5318ce94f
HelloWorld: 0xace9ff211c9131362e05774335c6b3a353739119
Saving successful migration to network...
... 0x3a653f82abd0b92f389ea78ba654f51ff2256961f72b280874676cebac24f895
Saving artifacts...
zihao@zihaodeMBP:~/SmartContractDemo/HelloWorld$ truffle console
truffle(development)> let contract
undefined
truffle(development)> HelloWorld.deployed().then(instance => contract = instance)
TruffleContract {
constructor:
{ [Function: TruffleContract]
_static_methods:
{ setProvider: [Function: setProvider],
new: [Function: new],
at: [Function: at],
deployed: [Function: deployed],
defaults: [Function: defaults],
hasNetwork: [Function: hasNetwork],
isDeployed: [Function: isDeployed],
detectNetwork: [Function: detectNetwork],
setNetwork: [Function: setNetwork],
resetAddress: [Function: resetAddress],
link: [Function: link],
clone: [Function: clone],
addProp: [Function: addProp],
toJSON: [Function: toJSON] },
_properties:
{ contract_name: [Object],
contractName: [Object],
abi: [Object],
network: [Function: network],
networks: [Function: networks],
address: [Object],
links: [Function: links],
events: [Function: events],
binary: [Function: binary],
deployedBinary: [Function: deployedBinary],
unlinked_binary: [Object],
bytecode: [Object],
deployedBytecode: [Object],
sourceMap: [Object],
deployedSourceMap: [Object],
source: [Object],
sourcePath: [Object],
ast: [Object],
compiler: [Object],
schema_version: [Function: schema_version],
schemaVersion: [Function: schemaVersion],
updated_at: [Function: updated_at],
updatedAt: [Function: updatedAt] },
_property_values: {},
_json:
{ contractName: 'HelloWorld',
abi: [Array],
bytecode: '0x6060604052341561000f57600080fd5b6102488061001e6000396000f30060606040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063ef5fb05b14610051578063f15da729146100df575b600080fd5b341561005c57600080fd5b6100646101b5565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100a4578082015181840152602081019050610089565b50505050905090810190601f1680156100d15780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34156100ea57600080fd5b61013a600480803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919050506101f8565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561017a57808201518184015260208101905061015f565b50505050905090810190601f1680156101a75780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101bd610208565b6040805190810160405280600b81526020017f48656c6c6f20576f726c64000000000000000000000000000000000000000000815250905090565b610200610208565b819050919050565b6020604051908101604052806000815250905600a165627a7a72305820ce2a412dd03391bea1a4d86dcaa57502a5d812ad4005a99570a2bb3b3ffd25ec0029',
deployedBytecode: '0x60606040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063ef5fb05b14610051578063f15da729146100df575b600080fd5b341561005c57600080fd5b6100646101b5565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100a4578082015181840152602081019050610089565b50505050905090810190601f1680156100d15780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34156100ea57600080fd5b61013a600480803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919050506101f8565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561017a57808201518184015260208101905061015f565b50505050905090810190601f1680156101a75780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101bd610208565b6040805190810160405280600b81526020017f48656c6c6f20576f726c64000000000000000000000000000000000000000000815250905090565b610200610208565b819050919050565b6020604051908101604052806000815250905600a165627a7a72305820ce2a412dd03391bea1a4d86dcaa57502a5d812ad4005a99570a2bb3b3ffd25ec0029',
sourceMap: '25:198:0:-;;;;;;;;;;;;;;;;;',
deployedSourceMap: '25:198:0:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;49:86;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:2;8:100;;;99:1;94:3;90;84:5;80:1;75:3;71;64:6;52:2;49:1;45:3;40:15;;8:100;;;12:14;3:109;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;139:82:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:2;8:100;;;99:1;94:3;90;84:5;80:1;75:3;71;64:6;52:2;49:1;45:3;40:15;;8:100;;;12:14;3:109;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;49:86:0;94:6;;:::i;:::-;108:22;;;;;;;;;;;;;;;;;;;;49:86;:::o;139:82::-;191:6;;:::i;:::-;212:4;205:11;;139:82;;;:::o;25:198::-;;;;;;;;;;;;;;;:::o',
source: 'pragma solidity ^0.4.4;\n\ncontract HelloWorld {\n function sayHello() public constant returns (string) {\n return ("Hello World");\n }\n\n function echo(string name) public constant returns (string) {\n return name;\n }\n}\n',
sourcePath: '/home/zihao/SmartContractDemo/HelloWorld/contracts/HelloWorld.sol',
ast: [Object],
compiler: [Object],
networks: [Object],
schemaVersion: '1.0.1',
updatedAt: '2018-01-10T13:34:12.869Z' },
setProvider: [Function: bound setProvider],
new: [Function: bound new],
at: [Function: bound at],
deployed: [Function: bound deployed],
defaults: [Function: bound defaults],
hasNetwork: [Function: bound hasNetwork],
isDeployed: [Function: bound isDeployed],
detectNetwork: [Function: bound detectNetwork],
setNetwork: [Function: bound setNetwork],
resetAddress: [Function: bound resetAddress],
link: [Function: bound link],
clone: [Function: bound clone],
addProp: [Function: bound addProp],
toJSON: [Function: bound toJSON],
web3:
Web3 {
_requestManager: [RequestManager],
currentProvider: [Provider],
eth: [Eth],
db: [DB],
shh: [Shh],
net: [Net],
personal: [Personal],
bzz: [Swarm],
settings: [Settings],
version: [Object],
providers: [Object],
_extend: [Function] },
class_defaults:
{ from: '0x7e1897cec8e1a098384d17e85a9cd348ccb92b4a',
gas: 6721975,
gasPrice: 100000000000 },
currentProvider:
HttpProvider {
host: 'http://localhost:8545',
timeout: 0,
user: undefined,
password: undefined,
send: [Function],
sendAsync: [Function],
_alreadyWrapped: true },
network_id: '1515578597496' },
abi:
[ { constant: true,
inputs: [],
name: 'sayHello',
outputs: [Array],
payable: false,
stateMutability: 'view',
type: 'function' },
{ constant: true,
inputs: [Array],
name: 'echo',
outputs: [Array],
payable: false,
stateMutability: 'view',
type: 'function' } ],
contract:
Contract {
_eth:
Eth {
_requestManager: [RequestManager],
getBalance: [Function],
getStorageAt: [Function],
getCode: [Function],
getBlock: [Function],
getUncle: [Function],
getCompilers: [Function],
getBlockTransactionCount: [Function],
getBlockUncleCount: [Function],
getTransaction: [Function],
getTransactionFromBlock: [Function],
getTransactionReceipt: [Function],
getTransactionCount: [Function],
call: [Function],
estimateGas: [Function],
sendRawTransaction: [Function],
signTransaction: [Function],
sendTransaction: [Function],
sign: [Function],
compile: [Object],
submitWork: [Function],
getWork: [Function],
coinbase: [Getter],
getCoinbase: [Function],
mining: [Getter],
getMining: [Function],
hashrate: [Getter],
getHashrate: [Function],
syncing: [Getter],
getSyncing: [Function],
gasPrice: [Getter],
getGasPrice: [Function],
accounts: [Getter],
getAccounts: [Function],
blockNumber: [Getter],
getBlockNumber: [Function],
protocolVersion: [Getter],
getProtocolVersion: [Function],
iban: [Function],
sendIBANTransaction: [Function: bound transfer] },
transactionHash: null,
address: '0xace9ff211c9131362e05774335c6b3a353739119',
abi: [ [Object], [Object] ],
sayHello:
{ [Function: bound ]
request: [Function: bound ],
call: [Function: bound ],
sendTransaction: [Function: bound ],
estimateGas: [Function: bound ],
getData: [Function: bound ],
'': [Circular] },
echo:
{ [Function: bound ]
request: [Function: bound ],
call: [Function: bound ],
sendTransaction: [Function: bound ],
estimateGas: [Function: bound ],
getData: [Function: bound ],
string: [Circular] },
allEvents: [Function: bound ] },
sayHello:
{ [Function]
call: [Function],
sendTransaction: [Function],
request: [Function: bound ],
estimateGas: [Function] },
echo:
{ [Function]
call: [Function],
sendTransaction: [Function],
request: [Function: bound ],
estimateGas: [Function] },
sendTransaction: [Function],
send: [Function],
allEvents: [Function: bound ],
address: '0xace9ff211c9131362e05774335c6b3a353739119',
transactionHash: null }
truffle(development)> contract.echo("2018-1-10 blockchain")
'2018-1-10 blockchain'
truffle(development)>

echo方法确实将我们输入的内容回传了。同时因为声明了constant,我们不需要直接调用call()方法,truffle会自动选用call来调用。

另外需要注意,如果我们还是使用truffle migrate命令,会得到如下信息:

1
2
3
4
$ truffle migrate
Using network 'development'.

Network up to date.

Truffle会告诉你现在网络上的合约都已经是最新的,但事实上刚刚程序中新增的方法并没有更新到内存块链上。要更新内存块链上已部署的程序,需要改写migrations中的脚本,但现在还不到介绍migration的时候。还好我们开发用的内存块链是怎么修改都没关系的testrpc,可以使用truffle migrate --reset命令直接在testrpc上部署一次。

欢迎关注我的其它发布渠道