从零开始搭建以太坊私链并部署智能合约
作者:张立勇 区块链底层开发| 网络架构 | 网络运维自动化
微信ID:yy109628620 博客:https://bolenzhang.github.io
搭建以太坊私链并部署智能合约
0、前言
本文环境搭建以Ubuntu 16.0.4为例。
在以太坊的共有链上部署智能合约、发起交易都需要花费以太币。而通过修改配置,可以在本机搭建一套以太坊私有链,因为与公有链没关系,既不用同步公有链庞大的数据,也不用花钱购买以太币,很好地满足了智能合约开发和测试的要求,开发好的智能合约也可以很容易地切换接口部署到以太坊公有链上。
1、下载
$ git clone https://github.com/ethereum/go-ethereum.git
2、编译
$ cd go-ethereum
$ make geth
windows下是:
go install -v ./...//其实就是去下载安装一些依赖的静态库
将 geth
添加到环境变量中 vi ~/.bashrc
export GETH="$GOPATH/src/github.com/ethereum/go-ethereum/build"
export PATH="$PATH:$GETH/bin"
然后执行 source ~/.bashrc
,使配置生效。
检查是否安装成功
geth --help
如果输出一些帮助提示命令,则说明安装成功。
其实此处也可以直接去下载以太坊geth。就不用去编译生成geth了:
3、搭建私有链
3.1 配置初始状态
要运行以太坊私有链,需要定义自己的创世区块,创世区块信息写在一个 JSON 格式的配置文件中。首先将下面的内容保存到一个 JSON 文件中,例如 genesis.json
$ mkdir ~/privatechain
$ cd privatechain
$ vi genesis.json
genesis.json 的代码
{
"config": {
"chainId": 110,
"homesteadBlock": 0,
"eip155Block": 0,
"eip158Block": 0
},
"difficulty" : "2000000",
"gasLimit" : "2100000",
"alloc": {
"0x986c0D89b3804D923Ef527788c92d18106f3ba86": {"balance": "300000"},
"0xd303F820Cff99CA74B802549ccB570e35bb0F1d4": {"balance": "250000"}
}
}
其中,chainID 指定了独立的区块链网络 ID。网络 ID 在连接到其他节点的时候会用到,以太坊公网的网络 ID 是 1,为了不与公有链网络冲突,运行私有链节点的时候要指定自己的网络 ID。不同 ID 网络的节点无法相互连接。配置文件还对当前挖矿难度 difficulty、区块 Gas 消耗限制 gasLimit 等参数进行了设置。
alloc,是把实现创建的钱包地址中预分配了一些余额。
3.2 初始化:写入创世区块
mkdir data0
geth --datadir data0 init genesis.json
上面的命令的主体是 geth init
,表示初始化区块链,命令可以带有选项和参数,其中 --datadir
选项后面跟一个目录名,这里为 当前目录
,表示指定数据存放目录为 当前目录
,genesis.json
是 init
命令的参数。
运行上面的命令,会读取 genesis.json
文件,根据其中的内容,将创世区块写入到区块链中。如果看到以下的输出内容,说明初始化成功了。
$ geth --datadir data0 init genesis.json
INFO [06-01|22:51:47] Maximum peer count ETH=25 LES=0 total=25
INFO [06-01|22:51:47] Allocated cache and file handles database=C:\\projects\\privatechain\\geth\\ch aindata cache=16 handles=16
INFO [06-01|22:51:47] Persisted trie from memory database nodes=3 size=503.00B time=0s gcnodes=0 gcsize =0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [06-01|22:51:47] Successfully wrote genesis state database=chaindata hash=947374…5b939a
INFO [06-01|22:51:47] Allocated cache and file handles database=C:\\projects\\privatechain\\geth\\li ghtchaindata cache=16 handles=16
INFO [06-01|22:51:47] Persisted trie from memory database nodes=3 size=503.00B time=0s gcnodes=0 gcsize =0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [06-01|22:51:47] Successfully wrote genesis state database=lightchaindata hash=947374…5b939a
初始化成功后,会在privatechain
的data0文件夹中生成 geth
和 keystore
两个文件夹,此时目录结构如下:
privatechain
├── data0
│ ├── geth
│ │ ├── chaindata
│ │ │ ├── 000001.log
│ │ │ ├── CURRENT
│ │ │ ├── LOCK
│ │ │ ├── LOG
│ │ │ └── MANIFEST-000000
│ │ └── lightchaindata
│ │ ├── 000001.log
│ │ ├── CURRENT
│ │ ├── LOCK
│ │ ├── LOG
│ │ └── MANIFEST-000000
│ └── keystore
└── genesis.json
其中 geth/chaindata
中存放的是区块数据,keystore
中存放的是账户数据。
3.3 启动私有链节点
geth --dev --datadir data0 --networkid 110 --rpc console --port 3000
上面命令的主体是 geth console
,表示启动节点并进入交互式控制台,--networkid
选项后面跟一个数字,这里是110,表示指定这个私有链的网络id为110,并且开启了rpc服务。网络id在连接到其他节点的时候会用到,以太坊公网的网络id是1,为了不与公有链网络冲突,运行私有链节点的时候要指定自己的网络id。
如果要指定端口可以执行如下命令:
geth --datadir data0 --networkid 110 --rpc console --port 30304 --rpcport 8546
注意,上面的命令如果报错,Fatal: Error starting protocol stack: Access is denied.
,就检查下是否有其他的geth进程开启,我卡在这里半天,后来发现是我打开的钱包客户端也开启了geth
,所以关掉钱包后就正常了。
...
Welcome to the Geth JavaScript console!
instance: Geth/v1.8.10-unstable/windows-amd64/go1.9.4
modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0
>
这是一个交互式的 JavaScript 执行环境,在这里面可以执行 JavaScript 代码,其中 > 是命令提示符。在这个环境里也内置了一些用来操作以太坊的 JavaScript 对象,可以直接使用这些对象。这些对象主要包括:
- eth:包含一些跟操作区块链相关的方法;
- net:包含一些查看p2p网络状态的方法;
- admin:包含一些与管理节点相关的方法;
- miner:包含启动&停止挖矿的一些方法;
- personal:主要包含一些管理账户的方法;
-
txpool
:包含一些查看交易内存池的方法; - web3:包含了以上对象,还包含一些单位换算的方法。
运行上面的命令后,就启动了区块链节点并进入了该节点的控制台,可以通过命令与私有链节点进行访问,我们可以通过admin.nodeInfo
命令查看节点摘要信息。注意,我们现在只是启动了一个节点,如果需要启动第二个节点,步骤跟上述一样,另外创建一个新文件夹,将genisis.json
复制到目录中,指定不同的端口然后进行初始化并启动节点即可。
若创建多个节点,则节点之间可以通过admin.addPeer
连接,在本机启动多个节点或者在不同的计算机上运行多个节点都可以,这样可以模拟出一个私有链网络。
3.4 通过geth命令参数的方式初始化并启动私链
说明:此方式相当于3.1-3.3所述的另一种实现方式,如果上述步骤以做完就不用执行此步骤。
geth --networkid 123 --dev --datadir data1 --rpc --rpcaddr 192.168.183.110 --rpcport 8989 --port 3000
各参数含义如下:
-
--identity:
指定节点 ID; -
--rpc:
表示开启 HTTP-RPC 服务; -
--rpcaddr:
HTTP-RPC 服务ip地址; -
--rpcport:
指定 HTTP-RPC 服务监听端口号(默认为 8545); -
--datadir:
指定区块链数据的存储位置; -
--port:
指定和其他节点连接所用的端口号(默认为 30303); -
--nodiscover:
关闭节点发现机制,防止加入有同样初始配置的陌生节点。
3.5 进入JavaScript执行环境
保持私链的运行,另外开一个终端,进入data0文件夹下执行 geth attach ./geth.ipc。(这个其实是geth作为客户端加入到以太网私链网络中,然后再开启geth console。)
root@zly-virtual-machine:~/privatechain/data0# geth attach ./geth.ipc
WARN [07-09|23:30:58.110] Sanitizing cache to Go's GC limits provided=1024 updated=656
Welcome to the Geth JavaScript console!
instance: Geth/v1.8.13-unstable-3b074515/linux-amd64/go1.10.1
coinbase: 0x92001ff7da9aa1a85dd5180cb1bd29981bac8d44
at block: 0 (Thu, 01 Jan 1970 08:00:00 CST)
datadir: /root/privatechain/data0
modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0
这是一个交互式的 JavaScript 执行环境,在这里面可以执行 JavaScript 代码,其中 > 是命令提示符。在这个环境里也内置了一些用来操作以太坊的 JavaScript 对象,可以直接使用这些对象。这些对象主要包括:
- `eth:`包含一些跟操作区块链相关的方法;
- `net:`包含一些查看p2p网络状态的方法;
- `admin:`包含一些与管理节点相关的方法;
- `miner:`包含启动&停止挖矿的一些方法;
- `personal:`主要包含一些管理账户的方法;
- `txpool:`包含一些查看交易内存池的方法;
- `web3:`包含了以上对象,还包含一些单位换算的方法。
4、控制台操作
执行geth console
进入以太坊 Javascript Console 后,就可以使用里面的内置对象做一些操作,这些内置对象提供的功能很丰富,比如查看区块和交易、创建账户、挖矿、发送交易、部署智能合约等。
常用命令有:
- personal.newAccount():创建账户;
- personal.listAccounts:查看账户,同eth.accounts
- personal.unlockAccount():解锁账户;
- eth.accounts:枚举系统中的账户;
- eth.getBalance():查看账户余额,返回值的单位是 Wei(Wei 是以太坊中最小货币面额单位,类似比特币中的聪,1 ether = 10^18 Wei);
- eth.blockNumber:列出区块总数;
- eth.getTransaction():获取交易;
- eth.getBlock():获取区块;
- miner.start():开始挖矿;
- miner.stop():停止挖矿;
- eth.coinbase:挖矿奖励的账户
- web3.fromWei():Wei 换算成以太币;
- web3.toWei():以太币换算成 Wei;
- txpool.status:交易池中的状态;
-
admin.addPeer()
:连接到其他节点; -
admin.nodeInfo
:查看节点摘要信息
4.1 创建账户
输入 eth.accounts
查询系统中的账户:
> eth.accounts
[]
显示为 []
,表示没有账户,接下来使用 personal.newAccount()
来创建一个账户:
> personal.newAccount()
Passphrase:
Repeat passphrase:
"0xb327a595dfbc769d73144c060b4f34c392021dad"
Passphrase
表示输入密码,Repeat passphrase
表示输入确认密码
再次创建一个账户
> personal.listAccounts
["0x92001ff7da9aa1a85dd5180cb1bd29981bac8d44"]
> personal.newAccount('zhangliyong')
"0x7ae3450f0b23d819aa9b45d5a60bd1f507aadc05"
> personal.listAccounts
["0x92001ff7da9aa1a85dd5180cb1bd29981bac8d44", "0x7ae3450f0b23d819aa9b45d5a60bd1f507aadc05"]
> eth.accounts
["0x92001ff7da9aa1a85dd5180cb1bd29981bac8d44", "0x7ae3450f0b23d819aa9b45d5a60bd1f507aadc05"]
查看刚刚创建的用户:(eth.accounts和personal.listAccounts两种方式)
> eth.accounts
["0xfb9cc019fc650a1699d05b7fb564b83c3a72b64d", "0xc6b5702b15a3794374e28f41f36e1e8dbdd564df"]
账户默认会保存在数据目录的 data0/keystore
文件夹中。可以查看其中的文件
{
"address": "fb9cc019fc650a1699d05b7fb564b83c3a72b64d",
"crypto": {
"cipher": "aes-128-ctr",
"ciphertext": "0efae4f94134172b785f1f635be8e70342bf31e1e0ecad21d672594f09ccf572",
"cipherparams": {
"iv": "37f4f3fa2cf070b70e3eb668fad6f46f"
},
"kdf": "scrypt",
"kdfparams": {
"dklen": 32,
"n": 262144,
"p": 1,
"r": 8,
"salt": "1cdd1cf97fb882c3d3e734223668b47c888170e795346e11d0f521a71fe3aa2a"
},
"mac": "902a29db25a3a35a2568b04b30a8194201f07ab37c96ed1fe9af932fc1d38be0"
},
"id": "ecd5beb2-8261-4207-8eff-2fa34678d064",
"version": 3
}
4.2 查看账户余额
通过 eth.getBalance()
可以查看账户余额
> eth.getBalance(eth.accounts[0])
0
> eth.getBalance(eth.accounts[1])
0
目前两个账户的以太币余额都是0,要使账户有余额,可以从其他账户转账过来,或者通过挖矿来获得以太币奖励。
4.3 启动&停止挖矿
通过 miner.start()
启动挖矿
> miner.start(3)
其中 start 的参数表示挖矿使用的线程数。第一次启动挖矿会先生成挖矿所需的 DAG 文件,这个过程有点慢,等进度达到 100% 后,就会开始挖矿,此时屏幕会被挖矿信息刷屏。
停止挖矿,在 console 中输入:
miner.stop()
挖到一个区块会奖励5个以太币,挖矿所得的奖励会进入矿工的账户,这个账户叫做coinbase,默认情况下coinbase是本地账户中的第一个账户:
> eth.coinbase
"0xfb9cc019fc650a1699d05b7fb564b83c3a72b64d"
也可以用web3命令[https://ethereumbuilders.gitbooks.io/guide/content/en/ethereum_javascript_api.html]
> web3.eth.coinbase
"0x92001ff7da9aa1a85dd5180cb1bd29981bac8d44"
> eth.coinbase
"0x92001ff7da9aa1a85dd5180cb1bd29981bac8d44"
可以通过 miner.setEtherbase()
将其他账户设置成 coinbase
即可
> miner.setEtherbase(eth.accounts[1])
true
> eth.coinbase
"0xc6b5702b15a3794374e28f41f36e1e8dbdd564df"
重新启动挖矿,查看 eth.accounts[1]
是否可以获得以太币
> miner.start(3)
//等待几秒后
> miner.stop()
查询账户余额:
> eth.getBalance(eth.accounts[0])
280000000000000000000
> eth.getBalance(eth.accounts[1])
210000000000000000000
发现账户0 和 账号1 都有以太币,说明 miner.setEtherbase()
设置成功。
getBalance()
返回值的单位是wei,wei是以太币的最小单位,1个以太币=10的18次方个wei。要查看有多少个以太币,可以用web3.fromWei()将返回值换算成以太币:
> web3.fromWei(eth.getBalance(eth.accounts[0]),'ether')
280
> web3.fromWei(eth.getBalance(eth.accounts[1]),'ether')
210
4.4 发送交易
我们从账户0转移10个以太币到账户1,首先要解锁账户 0,才能发起交易:
> personal.unlockAccount(eth.accounts[0])
Unlock account 0xfb9cc019fc650a1699d05b7fb564b83c3a72b64d
Passphrase:
true
发起交易
> amount = web3.toWei(10,'ether')
"10000000000000000000"
> eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[1],value:amount})
INFO [05-29|22:37:32] Submitted transaction fullhash=0x493e8aa2bcb6b2a362bdbd86b2c454279e14beea43b444aeb45c7f667bf572e2 recipient=0xc6b5702B15a3794374e28f41F36e1E8dBDd564DF
"0x493e8aa2bcb6b2a362bdbd86b2c454279e14beea43b444aeb45c7f667bf572e2"
查询 账户1 的余额:
> web3.fromWei(eth.getBalance(eth.accounts[1]),'ether')
210
发现账户余额没有发生改变,此时交易已经提交到区块链,但还未被处理,这可以通过用 txpool.status
命令可以看到本地交易池中有一个待确认的交易:
> txpool.status
{
pending: 1,
queued: 0
}
其中有一条pending的交易,pending表示已提交但还未被处理的交易。
要使交易被处理,必须要挖矿。这里我们启动挖矿,然后等待挖到一个区块之后就停止挖矿:
>miner.start(1);admin.sleepBlocks(1);miner.stop()
> web3.fromWei(eth.getBalance(eth.accounts[1]),'ether')
225.000378
发现账户收到了账户的钱,还多了5个以太币。其实多出的5个以太币是挖矿奖励。
4.5 查看交易和区块
查看当前区块总数:
> eth.blockNumber
99
通过区块号查看区块:
> eth.getBlock(6)
{
difficulty: 131328,
extraData: "0xd98301080a846765746888676f312e31302e328664617277696e",
gasLimit: 3160033,
gasUsed: 0,
hash: "0x9679d0dc01045c3d15cbf29241ae1cd6de2e5661d3387c0980f6397e3fd9ed2c",
logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
miner: "0xfb9cc019fc650a1699d05b7fb564b83c3a72b64d",
mixHash: "0x05c369134eac2cb6227860b4ed11527b11825af3541712c5704914576c37c0a0",
nonce: "0x00168a9831624417",
number: 6,
parentHash: "0xad68f0a581cf2144b8a05190b6310c7a9d945d9c338fd16b4708651b8813ad8b",
receiptsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
size: 537,
stateRoot: "0xfaa0a4ffe160a2937b967f9780ae0de51a465bcce6a3f6f3aa24b903df3d44a0",
timestamp: 1527602736,
totalDifficulty: 918144,
transactions: [],
transactionsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
uncles: []
}
通过交易hash(hash 值包含在上面交易返回值中)查看交易:
eth.getTransaction("0x493e8aa2bcb6b2a362bdbd86b2c454279e14beea43b444aeb45c7f667bf572e2")
{
blockHash: "0xbe60f7ccddcb7cab39a7b932c0d89d37ac13ae14521cfdcb8ac359a5b6773655",
blockNumber: 99,
from: "0xfb9cc019fc650a1699d05b7fb564b83c3a72b64d",
gas: 90000,
gasPrice: 18000000000,
hash: "0x493e8aa2bcb6b2a362bdbd86b2c454279e14beea43b444aeb45c7f667bf572e2",
input: "0x",
nonce: 0,
r: "0x8da41e865d399fc4d8f813a39116e486db7658a04ea1f89ca0b7f44c02dd3c57",
s: "0x15edd22404460cfc5e86c9735774a02aad024bc8c369ec531e4485f1012cbcf6",
to: "0xc6b5702b15a3794374e28f41f36e1e8dbdd564df",
transactionIndex: 0,
v: "0x37",
value: 10000000000000000000
}
4.6 小结
如果希望某个节点上的数据要写入到区块,并且同步到网络中的其他节点,必须要启动挖矿,挖矿就是一个区块打包和传播同步的过程,同时也只有启动挖矿,才能让私有链中的主帐号得以太币,进而可以用来继续测试转账交易、合约部署、合约调用等功能。
这种配置私有链的方式还是有些麻烦。对于想快速方便进行测试使用以及只能合约开发的读者,还有一种配置私有链的方式,那就是使用TestRPC与Truffle组合。TestRPC是在本地使用内存模拟的一个以太坊环境,可以用于搭建测试环境,基于Node.js开发,因此使用TestRPC首先要安装Node.js环境并且版本要大于6.9.1。Truffle是针对以太坊智能合约应用的一套开发框架。我们来看下具体如何搭建。
5、使用TestRPC与Truffle组合搭建智能合约
5.1 智能合约环境搭建
1)使用npm
安装TestRPC
,命令如下:
注意:testrpc
已改名为ganache-cli,本文以testrpc为例,实际上两者同样东西的不同叫法。
npm install -g ethereumjs-testrpc
2)查看版本:
testrpc -version
$ testrpc -version
EthereumJS TestRPC v6.0.3 (ganache-core: 2.0.2)
Available Accounts
==================
(0) 0x6f41fffc0338e715e8aac4851afc4079b712af70 🔒
(1) 0xad8926fdb14c2ca283ab1e8a05c0b6707bc03f97 🔒
(2) 0x1cb0ff92ec067169fd6b1b12c6d39a4f6c2cf6f9 🔒
(3) 0x594b70524993798cb093ca8a2bd7f02f904b66d3 🔒
(4) 0x2f1ee0930f00b0f3cdab66d916cbd1fa4fe9535a 🔒
(5) 0x5513a551c5aafaa8719a0df5bf398d4b3af4e211 🔒
(6) 0xa1bf121993c23cc467eec8b7e453011dae250404 🔒
(7) 0xe0b161979ebca95235c4cfeddfd11fb30d782a4d 🔒
(8) 0x093b30604ac41e054e71b670d8e3ab68360017c9 🔒
(9) 0x1cac60d851a44305d7dd6ecf8ff32f3403427d3d 🔒
Private Keys
==================
(0) db3ecbc77961edb030858411850bd1ed12915dcf3c13762ec3692989810c2870
(1) e0cc6b69516123fdf5d739dc9109ab486cf385affba555f0f03a7c74d7c1a88f
(2) cea171049aa817c8ecade84ed8d20ce9e2312bed6e0f39548c9673a65bd47a47
(3) e7f5cbba7403d0c5ea421f6ce64165b79d5c13594ad4a0ffa27896b498d6f5ad
(4) 6d1cd1fa965caf8a0b073faff072732de0eb0a8c153cc355f89eedb49c12ed12
(5) c535a490deefd452daab45b01daaf34366d6748afa5f0778aac5dae6c590c135
(6) 1292992fba6774016899237b8eb217108c5f9021e0e334aa5fad3db62e5c406b
(7) 03d02b40ab919c9d51e26fdc2e70c221d65ec1970f1283acad8b3445c5771f56
(8) 018069b07dc4d177a612ef0e20f63b9962781ab2d57d2ad50d9361a9be804bc4
(9) cd348e9ee0bc2cfbda9b9822d42b3dd3b83d180425bc153ffbc7e7e5bf13fa28
HD Wallet
==================
Mnemonic: say slight close trip refuse seven marble fun edge galaxy cash pioneer
Base HD Path: m/44'/60'/0'/0/{account_index}
Listening on localhost:8545
3)输出信息(如上)
可以看到,默认就自动配置了10个账户地址。注意,以上信息是动态的,每次启动是随机生成,不是固定的。通过最后一行数据的提示,表明TestTPC启动后使用8545端口监听。
4)使用npm安装Truffle,命令如下:
npm install -g truffle
5)安装成功后,输入truffle--version,输出如下:
$ truffle --version
Truffle v4.1.11 - a development framework for Ethereum
Usage: truffle <command> [options]
Commands:
init Initialize new and empty Ethereum project
compile Compile contract source files
migrate Run migrations to deploy contracts
deploy (alias for migrate)
build Execute build pipeline (if configuration present)
test Run JavaScript and Solidity tests
debug Interactively debug any transaction on the blockchain (experimental)
opcode Print the compiled opcodes for a given contract
console Run a console with contract abstractions and commands available
develop Open a console with a local development blockchain
create Helper to create new contracts, migrations and tests
install Install a package from the Ethereum Package Registry
publish Publish a package to the Ethereum Package Registry
networks Show addresses for deployed contracts on each network
watch Watch filesystem for changes and rebuild the project automatically
serve Serve the build directory on localhost and watch for changes
exec Execute a JS module within this Truffle environment
unbox Download a Truffle Box, a pre-built Truffle project
version Show version number and exit
See more at http://truffleframework.com/docs
6)安装solc:
npm install -g solc
注意,安装后的命令是solcjs,这是用来编译智能合约代码的。
7)运行测试
首先运行TestRPC,在命令行中直接通过testrpc命令可以启动,接着开始初始化Truffle目录,命令如下:
mkdir mytruffle && cd mytruffle
truffle init
这个命令其实就是下载一个项目框架,也可以直接通过网址https://github.com/trufflesuite/truffle-init-webpack下载后解压缩,复制到相应目录效果是一样的。
或者按照上述网址的readme.md操作也可以。
初始化完成后,Trufflebox项目目录如下:
[图片上传失败...(image-180fb4-1531151889766)]
其中,contracts中存放的是合约,truffle compile的时候就会在这里面寻找合约文件,migrations目录里面存放的JS文件,它帮助部署合约到以太坊网络中,他们表示了进行部署任务的步骤,truffle.js文件内容如下:
// Allows us to use ES6 in our migrations and tests.
require('babel-register')
module.exports = {
networks: {
development: {
host: '127.0.0.1', //节点地址,如果私有链一般是本机
port: 7545, //节点rpc端口
network_id: '*' // 自定义网络号
}
}
}
默认的配置与testrpc的参数是一致的,也可以根据需要修改。
8)启动了testrpc,也初始化了truffle,现在开始试着编写智能合约。可以看到在contracts中已经有几个示例合约了,创建一个Greeter.sol,源码如下:可以到这个网站,有很多的 contracts demo
pragma solidity ^0.4.17; // 指定 solidity 版本
contract Greeter // The contract definition. A constructor of the same name will be automatically called on contract creation.
{
address creator; // At first, an empty "address"-type variable of the name "creator". Will be set in the constructor.
string greeting; // At first, an empty "string"-type variable of the name "greeting". Will be set in constructor and can be changed.
// 构造函数
//constructor(string _greeting){
// creator = msg.sender;
//greeting = _greeting;
//}
function Greetor(string _greeting) public {
creator = msg.sender;
greeting = _greeting;
}
// 只读函数
function greet() constant public returns (string){
return greeting;
}
function getBlockNumber() constant public returns (uint) { // this doesn't have anything to do with the act of greeting // just demonstrating return of some global variable
return block.number;
}
// 非只读函数
function setGreeting(string _newgreeting) public {
greeting = _newgreeting;
}
// 自毁函数
function kill() public {
if (msg.sender == creator) // only allow this action if the account sending the signal is the creator
selfdestruct(creator); // kills this contract and sends remaining funds back to creator
}
}
9)在mytruffle目录中:
sudo truffle compile
10)编译如果没有问题,则会有如下提示:
$ truffle compile
Compiling .\contracts\Greeter.sol...
Writing artifacts to .\build\contracts
11)看到生了一个build目录,编译没有问题就可以部署了,进入到migrations目录,编辑2_deploy_contracts.js
,在最后一行插入“deployer.deploy”(合约名),编辑内容如下
var ConvertLib = artifacts.require("./ConvertLib.sol");
var MetaCoin = artifacts.require("./MetaCoin.sol");
var Greeter = artifacts.require("./Greeter.sol");//新增
module.exports = function(deployer) {
deployer.deploy(ConvertLib);
deployer.link(ConvertLib, MetaCoin);
deployer.deploy(MetaCoin);
deployer.deploy(Greeter); //新增
};
12)编辑保存后执行部署命令:
truffle migrate --reset //如果报错就检查下truffle.js中配置的rpc port和实际运行中的port是否一致
Greeter合约部署完成,我的Greeter的地址为: 0xc2787552e7a9f3796b147dfa22cad41a7e58fb0b,可以通过这个地址与合约进行交互。
13)注意,在操作过程中一个要保证TestRPC
是开启的,命令执行成功后,在TestRPC
中可以看到响应,接下来调用一下合约中的方法,要调用合约的功能,得与TestRPC
模拟节点交互,首先进入到控制台,命令如下:
truffle console
14)进入到控制台后,执行如下命令:(与合约进行交互)
$ truffle console
truffle(development)> var greeter = Greeter.at(Greeter.address)
undefined
truffle(development)> greeter.address
'0x9d8bf4bab5bee3fcb857ffde0f819205e9813d6b'
truffle(development)> greeter.greet()
''
truffle(development)> greeter.setGreeting("Hello World")
{ tx: '0xea98c0886e78f237d2aaadaf58db7ffce62b8023abfbf9172f8436119d7e75f4',
receipt:
{ transactionHash: '0xea98c0886e78f237d2aaadaf58db7ffce62b8023abfbf9172f8436119d7e75f4',
transactionIndex: 0,
blockHash: '0xaf3ee548e04898d6490d8072a7d7db8692f27750eb705052fa731460939d1d06',
blockNumber: 24,
gasUsed: 43439,
cumulativeGasUsed: 43439,
...
logs: [] }
truffle(development)> greeter.greet()
'Hello World'
合约以及创建成功,现在部署到私有链。
5.2 发布合约到私有链
退出ganache-cli,运行geth --datadir data0 --networkid 110 --rpc console --port 30304 --rpcport 8546
1、部署
先解锁账户(之前做过),然后必须先开启挖矿:
> miner.start()
切换到之前创建的私链环境,在另一个窗口中运行:
truffle migrate --reset
看到以下类似信息声明以及成功部署。
$ truffle migrate --reset
Using network 'development'.
...
Replacing Greeter...
... 0xfed35089e364676ed1ceca88f248b2d8cba147b281e1b4566d93947bc66f3c1d
Greeter: 0xd672f94aac6eea9e6a12bc41267b878562c29dc5 //合约地址
Saving successful migration to network...
... 0xa2cc625d5ff6016d4011f6c6d1638e61e36e4317e9669515fe2f901107ada30f
Saving artifacts...
2、查看
在私有链中查看合约:
拷贝 truffle compile
编译后的build/contracts/Greeter.json
文件中的abi串,然后在 geth console 窗口中,执行以下命令
var abi = JSON.parse('[{"constant": false, "inputs": [{"name":"_greeting,"type":"string"}], "name": "Greeter", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function"}]')
var greeter = eth.contract(abi).at('0xd672f94aac6eea9e6a12bc41267b878562c29dc5') # 修改成你的 Greeter 合约地址
> greeter.greet()
> greeter.setGreeting.sendTransaction('Hello World',{from:eth.accounts[0],gas:200000})
5.3 通过以太坊钱包来查看部署合约
通过以太坊钱也可以管理合约,更简单直观。
PRIVATE-NET
表明连接的是私有链。点击右上角的合约 CONTRACES
, 点击WATCH CONTRACT,添加我们刚创建的合约。
[图片上传失败...(image-9d4e3e-1531151889767)]
[图片上传失败...(image-c998c6-1531151889767)]
第一行填入合约地址,第二行给合约起个名字,第三个填入合约的ABI串,即合约编译truffle compile
后的 build/contracts/Greeter.json 文件中 abi 的值。
添加之后,就可以在合约界面看到刚添加的合约:
[图片上传失败...(image-f609f1-1531151889767)]
点击 Greeter,进入合约详情页,在这里可以执行合约。
[图片上传失败...(image-de157d-1531151889767)]
选择要执行的函数,输入字符串,点击 EXECUTE
。确保私有链中挖矿在进行,提交成功之后,界面的Greet字符串会自动刷新为新赋予的值。
[图片上传失败...(image-a0753b-1531151889767)]
Greet 值已改变。
[图片上传失败...(image-5ca922-1531151889767)]
在主界面可查看所有的交易,点击左上角 WALLETS
,最下面有最近的交易列表。
至此,以太坊私有链环境已搭建完成,合约也能正常工作。关于怎么编写合约,移步 solidity文档。