Contents
  1. 搭建以太坊私链并部署智能合约
    1. 1、下载
    2. 2、编译
    3. 3、搭建私有链
      1. 3.1 配置初始状态
      2. 3.2 初始化:写入创世区块
      3. 3.3 启动私有链节点
    4. 4、控制台操作
      1. 4.1 创建账户
      2. 4.2 查看账户余额
      3. 4.3 启动&停止挖矿
      4. 4.4 发送交易
      5. 4.5 查看交易和区块
      6. 4.6 小结
    5. 5、使用TestRPC与Truffle组合搭建智能合约
      1. 5.1 智能合约环境搭建
      2. 5.2 发布合约到私有链
      3. 5.3 通过以太坊钱包来查看部署合约

搭建以太坊私链并部署智能合约

1、下载

1
$ git clone https://github.com/ethereum/go-ethereum.git

2、编译

1
2
3
4
$ cd go-ethereum
$ make geth
windows下是:
go install -v ./...//其实就是去下载安装一些依赖的静态库

geth 添加到环境变量中 vi ~/.bashrc

1
2
export GETH="$GOPATH/src/github.com/ethereum/go-ethereum/build"
export PATH="$PATH:$GETH/bin"

然后执行 source ~/.bashrc,使配置生效。

检查是否安装成功

1
geth --help

如果输出一些帮助提示命令,则说明安装成功。

其实此处也可以直接去下载以太坊geth。就不用去编译生成geth了:

钱包工具:https://ethfans.org/wikis/Ethereum-Wallet-Mirror

geth :https://ethfans.org/wikis/Ethereum-Geth-Mirror

3、搭建私有链

3.1 配置初始状态

要运行以太坊私有链,需要定义自己的创世区块,创世区块信息写在一个 JSON 格式的配置文件中。首先将下面的内容保存到一个 JSON 文件中,例如 genesis.json

1
2
3
$ mkdir ~/privatechain
$ cd privatechain
$ vi genesis.json

genesis.json 的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"config": {
"chainId": 10,
"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 初始化:写入创世区块

1
geth --datadir data0 init genesis.json

上面的命令的主体是 geth init,表示初始化区块链,命令可以带有选项和参数,其中 --datadir 选项后面跟一个目录名,这里为 当前目录,表示指定数据存放目录为 当前目录genesis.jsoninit 命令的参数。

运行上面的命令,会读取 genesis.json 文件,根据其中的内容,将创世区块写入到区块链中。如果看到以下的输出内容,说明初始化成功了。

1
2
3
4
5
6
7
8
$ 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文件夹中生成 gethkeystore 两个文件夹,此时目录结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 启动私有链节点

1
geth --datadir data0 --networkid 110 --rpc console --port 0

上面命令的主体是 geth console,表示启动节点并进入交互式控制台,--networkid 选项后面跟一个数字,这里是110,表示指定这个私有链的网络id为110,并且开启了rpc服务。网络id在连接到其他节点的时候会用到,以太坊公网的网络id是1,为了不与公有链网络冲突,运行私有链节点的时候要指定自己的网络id。

如果要指定端口可以执行如下命令:

1
geth --datadir data0 --networkid 110 --rpc console --port 30304 --rpcport 8546

注意,上面的命令如果报错,Fatal: Error starting protocol stack: Access is denied.,就检查下是否有其他的geth进程开启,我卡在这里半天,后来发现是我打开的钱包客户端也开启了geth,所以关掉钱包后就正常了。

1
2
3
4
5
6
...
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连接,在本机启动多个节点或者在不同的计算机上运行多个节点都可以,这样可以模拟出一个私有链网络。

4、控制台操作

进入以太坊 Javascript Console 后,就可以使用里面的内置对象做一些操作,这些内置对象提供的功能很丰富,比如查看区块和交易、创建账户、挖矿、发送交易、部署智能合约等。

常用命令有:

  • personal.newAccount():创建账户;
  • 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 查询系统中的账户:

1
2
> eth.accounts
[]

显示为 [],表示没有账户,接下来使用 personal.newAccount() 来创建一个账户:

1
2
3
4
> personal.newAccount()
Passphrase:
Repeat passphrase:
"0xb327a595dfbc769d73144c060b4f34c392021dad"

Passphrase 表示输入密码,Repeat passphrase 表示输入确认密码

再次创建一个账户

1
2
3
4
> personal.newAccount()
Passphrase:
Repeat passphrase:
"0xc881f183d0d8cb7c3393c29e826a25b43631f910"

查看刚刚创建的用户:

1
2
> eth.accounts
["0xfb9cc019fc650a1699d05b7fb564b83c3a72b64d", "0xc6b5702b15a3794374e28f41f36e1e8dbdd564df"]

账户默认会保存在数据目录的 data0/keystore 文件夹中。可以查看其中的文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"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() 可以查看账户余额

1
2
3
4
> eth.getBalance(eth.accounts[0])
0
> eth.getBalance(eth.accounts[1])
0

目前两个账户的以太币余额都是0,要使账户有余额,可以从其他账户转账过来,或者通过挖矿来获得以太币奖励。

4.3 启动&停止挖矿

通过 miner.start() 启动挖矿

1
> miner.start(3)

其中 start 的参数表示挖矿使用的线程数。第一次启动挖矿会先生成挖矿所需的 DAG 文件,这个过程有点慢,等进度达到 100% 后,就会开始挖矿,此时屏幕会被挖矿信息刷屏。

停止挖矿,在 console 中输入:

1
miner.stop()

挖到一个区块会奖励5个以太币,挖矿所得的奖励会进入矿工的账户,这个账户叫做coinbase,默认情况下coinbase是本地账户中的第一个账户:

1
2
> eth.coinbase
"0xfb9cc019fc650a1699d05b7fb564b83c3a72b64d"

可以通过 miner.setEtherbase() 将其他账户设置成 coinbase 即可

1
2
3
4
> miner.setEtherbase(eth.accounts[1])
true
> eth.coinbase
"0xc6b5702b15a3794374e28f41f36e1e8dbdd564df"

重新启动挖矿,查看 eth.accounts[1] 是否可以获得以太币

1
2
3
4
> miner.start(3)

//等待几秒后
> miner.stop()

查询账户余额:

1
2
3
4
> 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()将返回值换算成以太币:

1
2
3
4
5
> web3.fromWei(eth.getBalance(eth.accounts[0]),'ether')
280

> web3.fromWei(eth.getBalance(eth.accounts[1]),'ether')
210

4.4 发送交易

我们从账户0转移10个以太币到账户1,首先要解锁账户 0,才能发起交易:

1
2
3
4
> personal.unlockAccount(eth.accounts[0])
Unlock account 0xfb9cc019fc650a1699d05b7fb564b83c3a72b64d
Passphrase:
true

发起交易

1
2
3
4
5
> 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 的余额:

1
2
> web3.fromWei(eth.getBalance(eth.accounts[1]),'ether')
210

发现账户余额没有发生改变,此时交易已经提交到区块链,但还未被处理,这可以通过用 txpool.status 命令可以看到本地交易池中有一个待确认的交易:

1
2
3
4
5
> txpool.status
{
pending: 1,
queued: 0
}

其中有一条pending的交易,pending表示已提交但还未被处理的交易。

要使交易被处理,必须要挖矿。这里我们启动挖矿,然后等待挖到一个区块之后就停止挖矿:

1
2
3
>miner.start(1);admin.sleepBlocks(1);miner.stop()
> web3.fromWei(eth.getBalance(eth.accounts[1]),'ether')
225.000378

发现账户收到了账户的钱,还多了5个以太币。其实多出的5个以太币是挖矿奖励。

4.5 查看交易和区块

查看当前区块总数:

1
2
> eth.blockNumber
99

通过区块号查看区块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
> 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 值包含在上面交易返回值中)查看交易:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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为例,实际上两者同样东西的不同叫法。

1
npm install -g ethereumjs-testrpc

2)查看版本:

1
testrpc -version
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
$ 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,命令如下:

1
npm install -g truffle

5)安装成功后,输入truffle–version,输出如下:

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
$ 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:

1
npm install -g solc

注意,安装后的命令是solcjs,这是用来编译智能合约代码的。

7)运行测试

​ 首先运行TestRPC,在命令行中直接通过testrpc命令可以启动,接着开始初始化Truffle目录,命令如下:

1
2
mkdir mytruffle && cd mytruffle
truffle init

这个命令其实就是下载一个项目框架,也可以直接通过网址https://github.com/trufflesuite/truffle-init-webpack下载后解压缩,复制到相应目录效果是一样的。

或者按照上述网址的readme.md操作也可以。

初始化完成后,Trufflebox项目目录如下:

其中,contracts中存放的是合约,truffle compile的时候就会在这里面寻找合约文件,migrations目录里面存放的JS文件,它帮助部署合约到以太坊网络中,他们表示了进行部署任务的步骤,truffle.js文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
// 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

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

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目录中:

1
sudo truffle compile

10)编译如果没有问题,则会有如下提示:

1
2
3
$ truffle compile
Compiling .\contracts\Greeter.sol...
Writing artifacts to .\build\contracts

11)看到生了一个build目录,编译没有问题就可以部署了,进入到migrations目录,编辑2_deploy_contracts.js,在最后一行插入“deployer.deploy”(合约名),编辑内容如下

1
2
3
4
5
6
7
8
9
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)编辑保存后执行部署命令:

1
truffle migrate --reset //如果报错就检查下truffle.js中配置的rpc port和实际运行中的port是否一致

Greeter合约部署完成,我的Greeter的地址为: 0xc2787552e7a9f3796b147dfa22cad41a7e58fb0b,可以通过这个地址与合约进行交互。

13)注意,在操作过程中一个要保证TestRPC是开启的,命令执行成功后,在TestRPC中可以看到响应,接下来调用一下合约中的方法,要调用合约的功能,得与TestRPC模拟节点交互,首先进入到控制台,命令如下:

1
truffle console

14)进入到控制台后,执行如下命令:(与合约进行交互)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ 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、部署

先解锁账户(之前做过),然后必须先开启挖矿:

1
> miner.start()

切换到之前创建的私链环境,在另一个窗口中运行:

1
truffle migrate --reset

看到以下类似信息声明以及成功部署。

1
2
3
4
5
6
7
8
9
$ 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 窗口中,执行以下命令

1
2
3
4
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,添加我们刚创建的合约。

第一行填入合约地址,第二行给合约起个名字,第三个填入合约的ABI串,即合约编译truffle compile后的 build/contracts/Greeter.json 文件中 abi 的值。

添加之后,就可以在合约界面看到刚添加的合约:

点击 Greeter,进入合约详情页,在这里可以执行合约。

选择要执行的函数,输入字符串,点击 EXECUTE。确保私有链中挖矿在进行,提交成功之后,界面的Greet字符串会自动刷新为新赋予的值。

Greet 值已改变。

在主界面可查看所有的交易,点击左上角 WALLETS,最下面有最近的交易列表。

至此,以太坊私有链环境已搭建完成,合约也能正常工作。关于怎么编写合约,移步 solidity文档

Contents
  1. 搭建以太坊私链并部署智能合约
    1. 1、下载
    2. 2、编译
    3. 3、搭建私有链
      1. 3.1 配置初始状态
      2. 3.2 初始化:写入创世区块
      3. 3.3 启动私有链节点
    4. 4、控制台操作
      1. 4.1 创建账户
      2. 4.2 查看账户余额
      3. 4.3 启动&停止挖矿
      4. 4.4 发送交易
      5. 4.5 查看交易和区块
      6. 4.6 小结
    5. 5、使用TestRPC与Truffle组合搭建智能合约
      1. 5.1 智能合约环境搭建
      2. 5.2 发布合约到私有链
      3. 5.3 通过以太坊钱包来查看部署合约