区块链开发(一)搭建基于以太坊go-ethereum的私有链环境

通过各方资料了解学习之后,决定自己开始搭建基于以太坊go-ethereum的私有链环境。由于本人的电脑系统为win10,为避免window环境出现过多莫名其妙的问题,特意通过vm搭建了一台ubuntu17.10版本的虚拟系统。以下内容均基于ubuntu17.10系统。

1. go-ethereum客户端

1.1. 下载地址&参考手册

首先,可以查看一下go-ethereum项目在git上的地址:
https://github.com/ethereum/Go-ethereum

可以在点击项目上的wiki标签,也可以通过一下地址访问wiki:
https://github.com/ethereum/Go-ethereum/wiki/Building-Ethereum

在wiki页面选择ubuntu系统的安装说明,也可以直接访问下面链接:
https://github.com/ethereum/go-ethereum/wiki/Installation-Instructions-for-Ubuntu

在wiki页面选择mac系统的安装说明,也可以直接访问下面链接:
https://github.com/ethereum/go-ethereum/wiki/Installation-Instructions-for-Mac

在wiki页面选择windows系统的安装说明,也可以直接访问下面链接:
https://github.com/ethereum/go-ethereum/wiki/Installation-instructions-for-Windows

可以在点击项目上的wiki标签,也可以通过一下地址访问wiki:
https://github.com/ethereum/go-ethereum/wiki/private-network

2. 安装步骤

2.1. ubuntu

更换apt-get为国内阿里云源

备份系统默认的源(没有root权限的前面加sudo)

cp /etc/apt/sources.list /etc/apt/sources.list.bak

修改/etc/apt/sources.list

# deb cdrom:[Ubuntu 16.04 LTS _Xenial Xerus_ - Release amd64 (20160420.1)]/ xenial main restricted
deb-src http://archive.ubuntu.com/ubuntu xenial main restricted #Added by software-properties
deb http://mirrors.aliyun.com/ubuntu/ xenial main restricted
deb-src http://mirrors.aliyun.com/ubuntu/ xenial main restricted multiverse universe #Added by software-properties
deb http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted
deb-src http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted multiverse universe #Added by software-properties
deb http://mirrors.aliyun.com/ubuntu/ xenial universe
deb http://mirrors.aliyun.com/ubuntu/ xenial-updates universe
deb http://mirrors.aliyun.com/ubuntu/ xenial multiverse
deb http://mirrors.aliyun.com/ubuntu/ xenial-updates multiverse
deb http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse #Added by software-properties
deb http://archive.canonical.com/ubuntu xenial partner
deb-src http://archive.canonical.com/ubuntu xenial partner
deb http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted
deb-src http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted multiverse universe #Added by software-properties
deb http://mirrors.aliyun.com/ubuntu/ xenial-security universe
deb http://mirrors.aliyun.com/ubuntu/ xenial-security multiverse

执行更新

sudo apt-get update     //不执行是不生效的

2.1.1. 安装 geth

sudo apt-get install software-properties-common
sudo add-apt-repository -y ppa:ethereum/ethereum
sudo apt-get update
sudo apt-get install ethereum   
somnus@ubuntu:~$ geth version
Geth
Version: 1.8.2-stable
Git Commit: b8b9f7f4476a30a0aaf6077daade6ae77f969960
Architecture: amd64
Protocol Versions: [63 62]
Network Id: 1
Go Version: go1.9.4
Operating System: linux
GOPATH=
GOROOT=/usr/lib/go-1.9
somnus@ubuntu:~$ 

2.1.2. 安装 solc

sudo apt-get install solc -y
somnus@ubuntu:~$ solc --version
solc, the solidity compiler commandline interface
Version: 0.4.19+commit.c4cbbb05.Linux.g++               

2.2. Windows

访问 https://geth.ethereum.org/downloads/
下载并安装 Geth for Windows      

2.3. Mac OS

brew tap ethereum/ethereum
brew install ethereum       

3. 创世区块

创建一个文件夹来存储你的私链数据

mkdir -p ethereum && cd ethereum    

3.1. 初始化创世区块

{
  "nonce": "0x0000000000000042",
  "difficulty": "0x0f0000",
  "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "coinbase": "0x0000000000000000000000000000000000000000",
  "timestamp": "0x00",
  "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa",
  "gasLimit": "0x4c4b40",
  "config": { "chainId": 15, "homesteadBlock": 0, "eip155Block": 0, "eip158Block": 0 },
  "alloc": { } }

参数解释:

参数名称 参数描述
mixhash 与nonce配合用于挖矿,由上一个区块的一部分生成的hash。注意他和nonce的设置需要满足以太坊的Yellow paper, 4.3.4. Block Header Validity, (44)章节所描述的条件。
nonce nonce就是一个64位随机数,用于挖矿,注意他和mixhash的设置需要满足以太坊的Yellow paper, 4.3.4. Block Header Validity, (44)章节所描述的条件。
difficulty 设置当前区块的难度,如果难度过大,cpu挖矿就很难,这里设置较小难度
alloc 用来预置账号以及账号的以太币数量,因为私有链挖矿比较容易,所以我们不需要预置有币的账号,需要的时候自己创建即可以。
coinbase 矿工的账号,随便填
timestamp 设置创世块的时间戳
parentHash 上一个区块的hash值,因为是创世块,所以这个值是0
extraData 附加信息,随便填,可以填你的个性信息
gasLimit 该值设置对GAS的消耗总量限制,用来限制区块能包含的交易信息总和,因为我们是私有链,所以填最大。

初始化创世区块

默认目录是root/ethereum/ 你可以通过--datadir 参数指定目录

somnus@ubuntu:~/ethereum$ geth --datadir data init genesis.json
INFO [04-01|19:53:03] Maximum peer count                       ETH=25 LES=0 total=25
INFO [04-01|19:53:03] Allocated cache and file handles         database=/home/somnus/ethereum/data/geth/chaindata cache=16 handles=16
INFO [04-01|19:53:03] Writing custom genesis block 
INFO [04-01|19:53:03] Persisted trie from memory database      nodes=0 size=0.00B time=2.123µs gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [04-01|19:53:03] Successfully wrote genesis state         database=chaindata                                 hash=611596424d04
INFO [04-01|19:53:03] Allocated cache and file handles         database=/home/somnus/ethereum/data/geth/lightchaindata cache=16 handles=16
INFO [04-01|19:53:03] Writing custom genesis block 
INFO [04-01|19:53:03] Persisted trie from memory database      nodes=0 size=0.00B time=2.565µs gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [04-01|19:53:03] Successfully wrote genesis state         database=lightchaindata                                 hash=611596424d04

somnus@ubuntu:~/ethereum$ find data 
data
data/keystore
data/geth
data/geth/chaindata
data/geth/chaindata/LOCK
data/geth/chaindata/LOG
data/geth/chaindata/MANIFEST-000000
data/geth/chaindata/CURRENT
data/geth/chaindata/000001.log
data/geth/lightchaindata
data/geth/lightchaindata/LOCK
data/geth/lightchaindata/LOG
data/geth/lightchaindata/MANIFEST-000000
data/geth/lightchaindata/CURRENT
data/geth/lightchaindata/000001.log 

目录结构

data
├── geth
│  ├── chaindata
│  │  ├── 000001.log
│  │  ├── CURRENT
│  │  ├── LOCK
│  │  ├── LOG
│  │  └── MANIFEST-000000
│  └── lightchaindata
│      ├── 000001.log
│      ├── CURRENT
│      ├── LOCK
│      ├── LOG
│      └── MANIFEST-000000
└── keystore    

3.2. 启动节点

somnus@ubuntu:~/ethereum$ geth --networkid 15 --datadir data --rpc --rpcaddr 172.16.61.17 --rpcport 8545 --rpcapi web3,eth,personal --nodiscover console 
INFO [04-01|20:00:31] Maximum peer count                       ETH=25 LES=0 total=25
INFO [04-01|20:00:31] Starting peer-to-peer node               instance=Geth/v1.8.2-stable-b8b9f7f4/linux-amd64/go1.9.4
INFO [04-01|20:00:31] Allocated cache and file handles         database=/home/somnus/.ethereum/geth/chaindata cache=768 handles=512
INFO [04-01|20:00:31] Writing default main-net genesis block 
INFO [04-01|20:00:31] Persisted trie from memory database      nodes=12356 size=2.34mB time=41.622179ms gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [04-01|20:00:31] Initialised chain configuration          config="{ChainID: 1 Homestead: 1150000 DAO: 1920000 DAOSupport: true EIP150: 2463000 EIP155: 2675000 EIP158: 2675000 Byzantium: 4370000 Constantinople: <nil> Engine: ethash}"
INFO [04-01|20:00:31] Disk storage enabled for ethash caches   dir=/home/somnus/.ethereum/geth/ethash count=3
INFO [04-01|20:00:31] Disk storage enabled for ethash DAGs     dir=/home/somnus/.ethash               count=2
INFO [04-01|20:00:31] Initialising Ethereum protocol           versions="[63 62]" network=123456
INFO [04-01|20:00:31] Loaded most recent local header          number=0 hash=d4e567…cb8fa3 td=17179869184
INFO [04-01|20:00:31] Loaded most recent local full block      number=0 hash=d4e567…cb8fa3 td=17179869184
INFO [04-01|20:00:31] Loaded most recent local fast block      number=0 hash=d4e567…cb8fa3 td=17179869184
INFO [04-01|20:00:31] Regenerated local transaction journal    transactions=0 accounts=0
INFO [04-01|20:00:31] Starting P2P networking 
INFO [04-01|20:00:31] HTTP endpoint opened                     url=http://127.0.0.1:8545 cors=* vhosts=localhost
INFO [04-01|20:00:31] RLPx listener up                         self="enode://50e818e4a6ecaba459ebb3edacaaab170e206d16410a52ea67e41324228382ec91dbfea1e324ade4708fc32fa8b952b15b75b0b87cdb307ed8b9629b4d42640d@[::]:30303?discport=0"
INFO [04-01|20:00:31] IPC endpoint opened                      url=/home/somnus/.ethereum/geth.ipc
Welcome to the Geth JavaScript console!

instance: Geth/v1.8.2-stable-b8b9f7f4/linux-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
 >

启动私有节点所需参数

参数名称 参数描述
identity 区块链的标示,随便填写,用于标示目前网络的名字
init 指定创世块文件的位置,并创建初始块
datadir 设置当前区块链网络数据存放的位置
port 网络监听端口
rpc 启动rpc通信,可以进行智能合约的部署和调试
rpcapi 设置允许连接的rpc的客户端,一般为db,eth,net,web3
rpcaddr HTTP-RPC 服务ip地址(默认127.0.0.1)
rpcport 指定 HTTP-RPC 服务监听端口号(默认为 8545);
networkid 设置当前区块链的网络ID,用于区分不同的网络,是一个数字,如果在默认模式下启动,网络ID为1,代码会自动连接到以太坊主链
console 启动命令行模式,可以在Geth中执行命令
nodiscover 关闭节点发现机制,防止加入有同样初始配置的陌生节点。

请注意,在我们的genesis.json文件里networkid(15)“chainid”必须是相同的。

//在需要日志文件的情况下
geth --networkid 15 --datadir data --rpc --rpcaddr 172.16.61.17 --rpcport 8545 --rpcapi web3,eth,personal --nodiscover console 2>>geth.log

在已启动的情况下如何进入终端

geth attach ipc:/usr/local/ethereum/data/geth.ipc 

在其它服务器中如何进入终端

geth attach http://121.43.162.28:8545 

这是一个交互式的 JavaScript 执行环境,在这里面可以执行 JavaScript 代码,其中 > 是命令提示符。在这个环境里也内置了一些用来操作以太坊的 JavaScript 对象,可以直接使用这些对象。这些对象主要包括:

  • eth:包含一些跟操作区块链相关的方法;
  • net:包含一些查看p2p网络状态的方法;
  • admin:包含一些与管理节点相关的方法;
  • miner:包含启动&停止挖矿的一些方法;
  • personal:主要包含一些管理账户的方法;
  • txpool:包含一些查看交易内存池的方法;
  • web3:包含了以上对象,还包含一些单位换算的方法。

4. 用户

如跳过这一步骤,进入挖矿会报如下错误,需要设置一个挖矿的账号

Error: etherbase missing: etherbase address must be explicitly specified  
    at web3.js:3104:20  
    at web3.js:6191:15  
    at web3.js:5004:36  
    at <anonymous>:1:1  

4.1. 查看账户

//方式一
somnus@ubuntu:~/ethereum$ personal.listAccounts
//方式二
somnus@ubuntu:~/ethereum$ eth.accounts
//方式三
somnus@ubuntu:~/ethereum$ geth account list 
Account #0: {83fda0ba7e6cfa8d7319d78fa0e6b753a2bcb5a6} keystore:///home/neo/.ethereum/keystore/UTC--2018-01-20T04-04-06.786586541Z--83fda0ba7e6cfa8d7319d78fa0e6b753a2bcb5a6
Account #1: {e8abf98484325fd6afc59b804ac15804b978e607} keystore:///home/neo/.ethereum/keystore/UTC--2018-01-20T06-11-23.608902164Z--e8abf98484325fd6afc59b804ac15804b978e607 

4.2. 创建用户

//方式一(参数为此账户的密码)
personal.newAccount("123456")
//方式二(也可以先创建账户,然后输入密码:)
personal.newAccount()

默认挖矿账号就是第一个设置的账号,当然也可以自行设置

eth.coinbase
miner.setEtherbase(eth.accounts[0])

查看区块数据

eth.blockNumber

5. 余额

> eth.getBalance(eth.accounts[0])
70000000000000000000
eth.getBalance()返回的余额是以太币的最小面额wei,将wei转换为以太币ether。

//primary = eth.accounts[0]
//balance = web3.fromWei(eth.getBalance(primary), "ether"); 

> web3.fromWei(eth.getBalance(eth.accounts[0]), "ether")
70    

备注eth单位解释:
kwei (1000 Wei)
mwei (1000 KWei)
gwei (1000 mwei)
szabo (1000 gwei)
finney (1000 szabo)
ether (1000 finney)

也就是说ether =wei * 10^18,也就是说精度可以达到18位。

//wei转ether
> web3.fromWei(1000000000000000000,"ether")
"1"
//ether转wei
> web3.toWei(1)
"1000000000000000000"

ps:web3命令
https://ethereumbuilders.gitbooks.io/guide/content/en/ethereum_javascript_api.html

6. 使用节点进行挖矿

6.1. 启动矿工开始挖矿

miner.start(1)

这里的1表示只使用一个线程运行,第一次运行时将开始创建DAG文件,只需等待进度条到100,则将开始挖矿。 实际你看到的挖矿速度很快,这是因为我们已经在初始化创世区块时配置为:"nonce": "0x0000000000000042"。 “0x42”难度能让你在私有测试网链上快速挖以太币。

提示

挖矿时必然有矿工账户,而系统默认使用创建的第一个账号。

> miner.start(1)
INFO [01-19|21:06:43] Updated mining threads                   threads=1
INFO [01-19|21:06:43] Transaction pool price threshold updated price=18000000000
INFO [01-19|21:06:43] Starting mining operation 
null
> INFO [01-19|21:06:43] Commit new mining work                   number=1 txs=0 uncles=0 elapsed=717.552µs
INFO [01-19|21:06:46] Generating ethash verification cache     epoch=0 percentage=91 elapsed=3.000s
INFO [01-19|21:06:46] Generated ethash verification cache      epoch=0 elapsed=3.273s
INFO [01-19|21:06:51] Generating DAG in progress               epoch=0 percentage=0  elapsed=5.056s
INFO [01-19|21:06:56] Generating DAG in progress               epoch=0 percentage=1  elapsed=10.140s
INFO [01-19|21:07:01] Generating DAG in progress               epoch=0 percentage=2  elapsed=15.119s
INFO [01-19|21:07:06] Generating DAG in progress               epoch=0 percentage=3  elapsed=19.924s
INFO [01-19|21:07:11] Generating DAG in progress               epoch=0 percentage=4  elapsed=24.739s
INFO [01-19|21:07:16] Generating DAG in progress               epoch=0 percentage=5  elapsed=29.473s
INFO [01-19|21:07:22] Generating DAG in progress               epoch=0 percentage=6  elapsed=35.641s
INFO [01-19|21:07:26] Generating DAG in progress               epoch=0 percentage=7  elapsed=40.374s
INFO [01-19|21:07:31] Generating DAG in progress               epoch=0 percentage=8  elapsed=45.134s
INFO [01-19|21:07:36] Generating DAG in progress               epoch=0 percentage=9  elapsed=49.908s
INFO [01-19|21:07:41] Generating DAG in progress               epoch=0 percentage=10 elapsed=54.633s    
......
......
......
INFO [01-19|21:22:43] Generated ethash verification cache      epoch=0 elapsed=15m57.328s
INFO [01-19|21:22:47] Generating ethash verification cache     epoch=1 percentage=17 elapsed=3.031s
INFO [01-19|21:22:50] Generating ethash verification cache     epoch=1 percentage=34 elapsed=6.056s
INFO [01-19|21:22:53] Generating ethash verification cache     epoch=1 percentage=49 elapsed=9.562s
INFO [01-19|21:22:57] Generating ethash verification cache     epoch=1 percentage=70 elapsed=13.115s
INFO [01-19|21:23:00] Generating ethash verification cache     epoch=1 percentage=90 elapsed=16.123s
INFO [01-19|21:23:01] Generated ethash verification cache      epoch=1 elapsed=17.576s
INFO [01-19|21:23:19] Generating DAG in progress               epoch=1 percentage=0  elapsed=18.198s
INFO [01-19|21:23:32] Successfully sealed new block            number=1 hash=e2b5b9…9b1bfe
INFO [01-19|21:23:32] ? mined potential block                  number=1 hash=e2b5b9…9b1bfe
INFO [01-19|21:23:32] Commit new mining work                   number=2 txs=0 uncles=0 elapsed=1.188ms
INFO [01-19|21:23:37] Generating DAG in progress               epoch=1 percentage=1  elapsed=35.913s
INFO [01-19|21:23:41] Successfully sealed new block            number=2 hash=62db3f…e27b50
INFO [01-19|21:23:41] ? mined potential block                  number=2 hash=62db3f…e27b50
INFO [01-19|21:23:41] Commit new mining work                   number=3 txs=0 uncles=0 elapsed=772.239µs
INFO [01-19|21:23:43] Successfully sealed new block            number=3 hash=34384b…c387f2
INFO [01-19|21:23:43] ? mined potential block                  number=3 hash=34384b…c387f2
INFO [01-19|21:23:43] Commit new mining work                   number=4 txs=0 uncles=0 elapsed=1.002ms
INFO [01-19|21:23:55] Generating DAG in progress               epoch=1 percentage=2  elapsed=53.757s
INFO [01-19|21:24:13] Generating DAG in progress               epoch=1 percentage=3  elapsed=1m11.561s
INFO [01-19|21:24:30] Generating DAG in progress               epoch=1 percentage=4  elapsed=1m28.986s
INFO [01-19|21:24:30] Successfully sealed new block            number=4 hash=681970462135
INFO [01-19|21:24:30] ? mined potential block                  number=4 hash=681970462135
INFO [01-19|21:24:30] Commit new mining work                   number=5 txs=0 uncles=0 elapsed=833.629µs
INFO [01-19|21:24:36] Successfully sealed new block            number=5 hash=7b058b…d2f07a
INFO [01-19|21:24:36] ? mined potential block                  number=5 hash=7b058b…d2f07a
INFO [01-19|21:24:36] Commit new mining work                   number=6 txs=0 uncles=0 elapsed=897.815µs
INFO [01-19|21:24:43] Successfully sealed new block            number=6 hash=a5fc3d…b1221e
INFO [01-19|21:24:43] ? block reached canonical chain          number=1 hash=e2b5b9…9b1bfe
INFO [01-19|21:24:43] ? mined potential block                  number=6 hash=a5fc3d…b1221e
INFO [01-19|21:24:43] Commit new mining work                   number=7 txs=0 uncles=0 elapsed=758.061µs
INFO [01-19|21:24:47] Successfully sealed new block            number=7 hash=003b02…e886fd
INFO [01-19|21:24:47] ? block reached canonical chain          number=2 hash=62db3f…e27b50
INFO [01-19|21:24:47] ? mined potential block                  number=7 hash=003b02…e886fd
INFO [01-19|21:24:47] Commit new mining work                   number=8 txs=0 uncles=0 elapsed=920.862µs
INFO [01-19|21:24:48] Generating DAG in progress               epoch=1 percentage=5  elapsed=1m46.827s
INFO [01-19|21:25:06] Generating DAG in progress               epoch=1 percentage=6  elapsed=2m4.338s
INFO [01-19|21:25:23] Successfully sealed new block            number=8 hash=fd23c9…361c65
INFO [01-19|21:25:23] ? block reached canonical chain          number=3 hash=34384b…c387f2
INFO [01-19|21:25:23] ? mined potential block                  number=8 hash=fd23c9…361c65
INFO [01-19|21:25:23] Commit new mining work                   number=9 txs=0 uncles=0 elapsed=825.737µs
INFO [01-19|21:25:23] Generating DAG in progress               epoch=1 percentage=7  elapsed=2m22.061s

6.2. 停止挖矿

> miner.stop()
true
>

6.3. 查看所挖金额

> eth.getBalance(eth.accounts[0])
7000000000000000000

这是一个交互式的 JavaScript 执行环境,在这里面可以执行 JavaScript 代码,其中 > 是命令提示符。在这个环境里也内置了一些用来操作以太坊的 JavaScript 对象,可以直接使用这些对象。这些对象主要包括:

  • eth:包含一些跟操作区块链相关的方法;
  • net:包含一些查看p2p网络状态的方法;
  • admin:包含一些与管理节点相关的方法;
  • miner:包含启动&停止挖矿的一些方法;
  • personal:主要包含一些管理账户的方法;
  • txpool:包含一些查看交易内存池的方法;
  • web3:包含了以上对象,还包含一些单位换算的方法。

6.4. 转账

转出账号中有10000个以太币


> web3.fromWei(eth.getBalance(eth.accounts[0]))
10000

转入账号目前是 100 

> web3.fromWei(eth.getBalance(eth.accounts[1]))
100

解锁传出账号,否则不能转出。personal.unlockAccount(账号)或者personal.unlockAccount(账号,"password", 15000)

> personal.unlockAccount(eth.accounts[0])
true

转账操作

> eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[1], value: web3.toWei(10000, "ether")})
INFO [04-19|12:00:35] Submitted transaction                    fullhash=0xa5572bee3609c772e2f3c0b05df52a0ce5b75e401dc00c6c0365b4f36b93df27 recipient=0x7053133f9C5803a71C1a0FEdAD3E3a3138e2225B
"0xa5572bee3609c772e2f3c0b05df52a0ce5b75e401dc00c6c0365b4f36b93df27"
 //查看待确认交易的详情 //使用命令web3.eth.getTransaction:
> web3.eth.getTransaction("0xa5572bee3609c772e2f3c0b05df52a0ce5b75e401dc00c6c0365b4f36b93df27")
{
  blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
  blockNumber: null,
  from: "0x4d047e01cd56f878443b11eb2417a936f40d69f7",
  gas: 90000,
  gasPrice: 18000000000,
  hash: "0xa5572bee3609c772e2f3c0b05df52a0ce5b75e401dc00c6c0365b4f36b93df27",
  input: "0x",
  nonce: 13,
  r: "0x4670f7999a0466a80870d8d69a4c0dab2597b055e1c850b063990d431cdb046a",
  s: "0x38f5275a006a1b74ef889a73ac096142f294d69505d60239384a706d9a3cc44c",
  to: "0x7053133f9c5803a71c1a0fedad3e3a3138e2225b",
  transactionIndex: 0,
  v: "0x42",
  value: 1e+22
}
 //由于交易还未确认,所以所属区块为null,blockNumber: null。 //主要参数含义 //gas:需要的gas数量 //gasPrice:交易的gas单价[18,000,000,000(wei)=18(GWei)] //由此可以算出,我们需要交纳的手续费为=90,000*18(GWei)=1620,000(Gwei)=0.00162(ether)

如果你现在查看转入账号,你会发现余额仍然是 0 ,交易还未成功写进区块,写进区块的方式是挖矿,所以你必须执行挖矿

> miner.start(1)
null
> INFO [04-19|12:51:18] Starting mining operation 
INFO [04-19|12:51:18] Commit new mining work                   number=6713 txs=1 uncles=0 elapsed=363.708µs
INFO [04-19|12:51:26] Successfully sealed new block            number=6713 hash=260c89…44b141
INFO [04-19|12:51:26]  mined potential block                  number=6713 hash=260c89…44b141
INFO [04-19|12:51:26] Commit new mining work                   number=6714 txs=0 uncles=0 elapsed=196.425µs
INFO [04-19|12:51:28] Successfully sealed new block            number=6714 hash=b690a2…b38e1a
INFO [04-19|12:51:28]  mined potential block                  number=6714 hash=b690a2…b38e1a
INFO [04-19|12:51:28] Commit new mining work                   number=6715 txs=0 uncles=0 elapsed=115.331µs

第三行第四行显示成功密封了一个新的区块。

稍后几分钟,再次查看转入账号,将会看到有10000个以太币入账。传出账号会减少10000个以太币,同时仍然继续挖矿中。

> web3.fromWei(eth.getBalance(eth.accounts[0]), "ether")
10100
//再次查看交易详情
> web3.eth.getTransaction("0xa5572bee3609c772e2f3c0b05df52a0ce5b75e401dc00c6c0365b4f36b93df27")
{
  blockHash: "0x260c89fdcef4783c22b8cf537fa69c22ee9b7c05bae5ca3aa98c8a3b7944b141",
  blockNumber: 6713,
  from: "0x4d047e01cd56f878443b11eb2417a936f40d69f7",
  gas: 90000,
  gasPrice: 18000000000,
  hash: "0xa5572bee3609c772e2f3c0b05df52a0ce5b75e401dc00c6c0365b4f36b93df27",
  input: "0x",
  nonce: 13,
  r: "0x4670f7999a0466a80870d8d69a4c0dab2597b055e1c850b063990d431cdb046a",
  s: "0x38f5275a006a1b74ef889a73ac096142f294d69505d60239384a706d9a3cc44c",
  to: "0x7053133f9c5803a71c1a0fedad3e3a3138e2225b",
  transactionIndex: 0,
  v: "0x42",
  value: 1e+22
}

7. 私有链直接部署智能合约

7.1. 编写智能合约代码

pragma solidity ^0.4.4;

contract calculator { 

    function multiply(uint a) returns(uint d){

        return a * 7;
    }

}

7.2. 检查coinbase账号余额

> account1 = web3.eth.coinbase
"0x2abf46d8b0d940cdeedd55872bc0648add40227d"
> web3.eth.getBalance(account1)
0
> 

如果余额大于0,继续,否则,开始挖矿。

> miner.start();
null
> 

如果你觉得差不多了,可以运行下面的命令停止挖矿。

> miner.stop();
true

7.3. 解锁coinbase账号,我们通过coinbase账号来付费部署合约

123456: 换成你的密码。

> personal.unlockAccount(web3.eth.coinbase, '123456') 
true

7.4. 获取智能合约字节码和abi

代码拷贝到https://remix.ethereum.orghttps://ethereum.github.io/browser-solidity/,编译,然后点击Details

这里写图片描述

7.5. 将 Browser-solidity编译后的合约部署到Geth

发送部署合约的交易

var calculatorContract = web3.eth.contract([{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"}]);
var calculator = calculatorContract.new(
   {
     from: web3.eth.accounts[0], 
     data: '0x608060405234801561001057600080fd5b5060bb8061001f6000396000f300608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063c6888fa1146044575b600080fd5b348015604f57600080fd5b50606c600480360381019080803590602001909291905050506082565b6040518082815260200191505060405180910390f35b60006007820290509190505600a165627a7a72305820f75ed1716facde6a967c214d7920116d5cc4bc2504682f047a95e82183d8c9bd0029', 
     gas: '4700000'
   }, function (e, contract){
    console.log(e, contract);
    if (typeof contract.address !== 'undefined') {
         console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);
    }
 })
  • from代表合约由哪个账户生成,哪个账户生成,则生成所需的gas就需要该账户承担,默认为eth.accounts[0],因为所有挖矿所得的以太币也默认都存入该账户中。

  • data代表的就是bytecode。是编译后的代码,你的代码越长,这块的字符串越多。

  • gas代表的是为了部署该合约最多准备的gas数量,当然实际上可能用不了这么多gas,具体消耗以实际使用量为准,这里只是设定一个最大量。gas是调用合约要扣除的gas单位,可以理解为以太币,gasether之间有个汇率,汇率受矿机的算率影响会有调整,在公网上,这些gas用于奖励给挖矿者。

  • 最后这段是一个典型的javascript的异步调用的写法,将上面的new方法的结果传递给下一个方法function(e,contract),在下一个方法中处理如果挖矿成功的显示结果。如果合约成功部署在区块上,则在控制台打印出来Contract mined!contract addresstransactionHash等信息。

  • address表示已经部署智能合约的帐户地址,智能合约也相当于一个帐户。

  • transactionHash表示智能合约产生时的hash值,会永久保存到区块链条里面。

输入calculator可以看到合约的一些信息

> calculator
{
  abi: [{
      constant: false,
      inputs: [{...}],
      name: "multiply",
      outputs: [{...}],
      payable: false,
      stateMutability: "nonpayable",
      type: "function"
  }],
  address: "0x35be144ae869bf8cc7ab83f5dd956898e3b127aa",
  transactionHash: "0x3c5d28ef56f225d7d2d6b02106a628fce5ce5b42c9c2ba6205bb3ee3a9deeeed",
  allEvents: function(),
  multiply: function()
}

7.6. 查看本地交易池中待确认的交易

> txpool.status
{ pending: 1, queued: 0 }

可以看到交易池中有一个待确认的交易

7.7. 查看待确认交易的详情

使用命令web3.eth.getTransaction,参数为transactionHash

> web3.eth.getTransaction("0x3c5d28ef56f225d7d2d6b02106a628fce5ce5b42c9c2ba6205bb3ee3a9deeeed")
{
  blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
  blockNumber: null,
  from: "0x4d047e01cd56f878443b11eb2417a936f40d69f7",
  gas: 4700000,
  gasPrice: 18000000000,
  hash: "0x9bd7fe9080ed20a5fbeaa644ca87614b00fcd15582bff897598d991a680fa5d9",
  input: "0x608060405234801561001057600080fd5b5060bb8061001f6000396000f300608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063c6888fa1146044575b600080fd5b348015604f57600080fd5b50606c600480360381019080803590602001909291905050506082565b6040518082815260200191505060405180910390f35b60006007820290509190505600a165627a7a72305820f22b1e35f2de2583454229eaa5609b69b3a3628801c59eecb3361299cc2cdc4b0029",
  nonce: 20,
  r: "0xca66158e7abf3582fedf03dd040083a0838370687569a62badb88089a62f77",
  s: "0x140d6bd716ec6d137eced02987eccc952065d371030784b661051d6f3f630428",
  to: null,
  transactionIndex: 0,
  v: "0x41",
  value: 0
}
> 

由于交易还未确认,所以所属区块为null,blockNumber: null

7.8. 预估手续费

> bytecode = "608060405234801561001057600080fd5b5060bb8061001f6000396000f300608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063c6888fa1146044575b600080fd5b348015604f57600080fd5b50606c600480360381019080803590602001909291905050506082565b6040518082815260200191505060405180910390f35b60006007820290509190505600a165627a7a72305820f75ed1716facde6a967c214d7920116d5cc4bc2504682f047a95e82183d8c9bd0029 "
"608060405234801561001057600080fd5b5060bb8061001f6000396000f300608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063c6888fa1146044575b600080fd5b348015604f57600080fd5b50606c600480360381019080803590602001909291905050506082565b6040518082815260200191505060405180910390f35b60006007820290509190505600a165627a7a72305820f75ed1716facde6a967c214d7920116d5cc4bc2504682f047a95e82183d8c9bd0029"
> web3.eth.estimateGas({data: bytecode})
Error: invalid argument 0: json: cannot unmarshal hex string without 0x prefix into Go struct field CallArgs.data of type hexutil.Bytes
    at web3.js:3104:20
    at web3.js:6191:15
    at web3.js:5004:36
    at <anonymous>:1:1

> bytecode = "0x608060405234801561001057600080fd5b5060bb8061001f6000396000f300608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063c6888fa1146044575b600080fd5b348015604f57600080fd5b50606c600480360381019080803590602001909291905050506082565b6040518082815260200191505060405180910390f35b60006007820290509190505600a165627a7a72305820f75ed1716facde6a967c214d7920116d5cc4bc2504682f047a95e82183d8c9bd0029"
"0x608060405234801561001057600080fd5b5060bb8061001f6000396000f300608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063c6888fa1146044575b600080fd5b348015604f57600080fd5b50606c600480360381019080803590602001909291905050506082565b6040518082815260200191505060405180910390f35b60006007820290509190505600a165627a7a72305820f75ed1716facde6a967c214d7920116d5cc4bc2504682f047a95e82183d8c9bd0029"
> web3.eth.estimateGas({data: bytecode})
98391
> 

备注:字节码前面需要添加0x。手续费大概为98391gas

7.9. 你的合约等待挖矿,开始挖矿,等一会儿,停止

> miner.start()
null
> Contract mined! Address: 0xbf8b24283f2516360d3a4ba1db0df78ae74689db
[object Object]
> miner.stop()
true
> 

7.10. 检查合约是否部署成功

> eth.getCode(calculator.address)
"0x60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063c6888fa114603d575b600080fd5b3415604757600080fd5b605b60048080359060200190919050506071565b6040518082815260200191505060405180910390f35b60006007820290505b9190505600a165627a7a7230582067d7c851e14e862886b6f53dad6825135557fb3a4b691350c94ea5b80605f6770029"
> 

7.11. 调用合约方法

> calculator.multiply.call(6)
42
> //本地调用,不会向区块链网络广播任何东西的合约调用,不会被区块所打包,切记、切记!!!

如果换做其他人需要运行你的智能合约,还可以用如下方式,但需要两个信息:(后面提到的truffle部署智能合约成功后,私有链就按此方式调用)

  1. 智能合约地址Address
  2. 智能合约ABI(Application Binary Interface)ABI其实就是一个有序的用户手册,描述了所有方法的名字和如何调用它们。
> var abi = [{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"}]
> var calc = eth.contract(abi).at("0x35be144ae869bf8cc7ab83f5dd956898e3b127aa")
> calc.multiply.call(6)
> 42

7.12. 再查询交易详情

> web3.eth.getTransaction("0x3c5d28ef56f225d7d2d6b02106a628fce5ce5b42c9c2ba6205bb3ee3a9deeeed")
{
  blockHash: "0x23718f944ea863cdae899b7cf85a298e7a3efc70be09364461d0bf4ef90ae4b5",
  blockNumber: 5900,
  from: "0x4d047e01cd56f878443b11eb2417a936f40d69f7",
  gas: 4700000,
  gasPrice: 18000000000,
  hash: "0x3c5d28ef56f225d7d2d6b02106a628fce5ce5b42c9c2ba6205bb3ee3a9deeeed",
  input: "0x608060405234801561001057600080fd5b5060bb8061001f6000396000f300608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063c6888fa1146044575b600080fd5b348015604f57600080fd5b50606c600480360381019080803590602001909291905050506082565b6040518082815260200191505060405180910390f35b60006007820290509190505600a165627a7a72305820f75ed1716facde6a967c214d7920116d5cc4bc2504682f047a95e82183d8c9bd0029",
  nonce: 21,
  r: "0x2c81c8ff57dfeccf393d7db45a4cee2f66653e80019a26fb3992ece650457d30",
  s: "0xf5db5ebebaa82b16136a29274bffd2351782c9be6f6e4059addf52e58219f48",
  to: null,
  transactionIndex: 0,
  v: "0x41",
  value: 0
}

可以看到已经写到编号为5900的区块上了blockNumber: 5900

8. 创建新节点

都说区块链是去中心化的账本那么可以满足于单机挖矿?说干就干接下来创建一个新节点并将太接入我们的主节点。这里需要注意以下几点:

新节点的 networkid 要与 第一个创建的节点data 一致
需要与 第一个创建的节点data 使用同一个创世区块(也就是同一个genesis.json文件)
如果多个节点都在一台机器上注意端口区分,避免端口冲突
--port 30304
--rpcport 9545

geth --datadir data2 init genesis.json

geth --networkid 15 --datadir data2 --rpc --port 30304 --rpcaddr 172.16.61.17 --rpcport 9545 --nodiscover console

8.1.联盟链互通

上面分别是在两个节点上进行的操作,下面我们需要把两个节点之间建立起链接。首先,我们执行以下命令查看以下节点的peers的情况。

> admin.peers
[]

发现节点并没有链接上任何其他节点,这也是我们的nodiscover参数发挥了效果。

下面就通过分享enode地址的方式来让两个节点建立链接。

> admin.nodeInfo.enode
"enode://aa621c010c685665ef217044dac4d57f4d1d682c682a5b3f92ca23b40982383240a05b680060ce8b0ce020a96c49c9c2c3628c4ea3281845211bd4cf4f03b35c@[::]:30306?discport=0"
>

通过上面命令,我们获得了节点2的encode信息。这是geth用来连接到不同节点的enode信息,在这些不同的节点它们能够分享交易和成功挖掘信息。

其实这个信息如果留心的话,在启动节点的打印日志中已经打印出每个节点的encode信息。比如:

INFO [12-28|19:23:16] RLPx listener up                         self="enode://aa621c010c685665ef217044dac4d57f4d1d682c682a5b3f92ca23b40982383240a05b680060ce8b0ce020a96c49c9c2c3628c4ea3281845211bd4cf4f03b35c@[::]:30303?discport=0"

现在,我们要告知一个节点,另外一个节点的encode信息。首先复制节点2的日志中self等号后面的信息,在节点1的控制台执行以下命令:

> admin.addPeer("enode://aa621c010c685665ef217044dac4d57f4d1d682c682a5b3f92ca23b40982383240a05b680060ce8b0ce020a96c49c9c2c3628c4ea3281845211bd4cf4f03b35c@[::]:30306?discport=0")
true

返回true,说明执行成功。再次验证一下:

> admin.peers
[{
    caps: ["eth/63"],
    id: "aa621c010c685665ef217044dac4d57f4d1d682c682a5b3f92ca23b40982383240a05b680060ce8b0ce020a96c49c9c2c3628c4ea3281845211bd4cf4f03b35c",
    name: "Geth/v1.7.3-stable-4bb3c89d/darwin-amd64/go1.9.2",
    network: {
      localAddress: "[::1]:49426",
      remoteAddress: "[::1]:30306"
    },
    protocols: {
      eth: {
        difficulty: 16384,
        head: "0x942f596f99dc8879b426b59080824662e1f97587353d087487fea0a0e2a2588a",
        version: 63
      }
    }
}]
>

发现节点1已经有一个peer了,同时我们可以看到remoteAddress: “[::1]:30306”,正是我们节点2的端口信息。在节点2执行admin.peers会发现有类似的信息,指向的peer正是节点1的。

查询余额并挖矿

执行查看余额命令:

> eth.getBalance(eth.coinbase0

节点1执行miner.start()进行挖矿,执行miner.stop()停止挖矿。停止挖矿的时候开业忽略控制台输出,只要正确拼写命令回车即可。

当我们在节点1执行挖矿时,我们会发现节点2的控制台出现了这样的日志信息:

> INFO [12-28|20:05:32] Block synchronisation started
INFO [12-28|20:05:33] Imported new state entries               count=1 elapsed=47.661µs processed=1 pending=0 retry=0 duplicate=0 unexpected=0
WARN [12-28|20:05:33] Discarded bad propagated block           number=1 hash=ab49ba…1cf32f
INFO [12-28|20:05:33] Imported new block headers               count=2 elapsed=9.208ms  number=2 hash=738225000e3b ignored=0
INFO [12-28|20:05:33] Imported new chain segment               blocks=2 txs=0 mgas=0.000 elapsed=1.724ms  mgasps=0.000 number=2 hash=738225000e3b
INFO [12-28|20:05:33] Fast sync complete, auto disabling
INFO [12-28|20:05:34] Imported new chain segment               blocks=1 txs=0 mgas=0.000 elapsed=5.978ms  mgasps=0.000 number=3 hash=b069a9…426060
INFO [12-28|20:05:38] Imported new chain segment               blocks=1 txs=0 mgas=0.000 elapsed=6.930ms  mgasps=0.000 number=4 hash=21217e…526253
INFO [12-28|20:05:41] Imported new chain segment               blocks=1 txs=0 mgas=0.000 elapsed=6.419ms  mgasps=0.000 number=5 hash=3fa6ff…cf2794
INFO [12-28|20:05:43] Imported new chain segment               blocks=1 txs=0 mgas=0.000 elapsed=6.557ms  mgasps=0.000 number=6 hash=4c35b9…78b3ec
INFO [12-28|20:05:45] Imported new chain segment               blocks=1 txs=0 mgas=0.000 elapsed=6.514ms  mgasps=0.000 number=7 hash=328e621bd3d3
INFO [12-28|20:05:46] Imported new chain segment               blocks=1 txs=0 mgas=0.000 elapsed=6.513ms  mgasps=0.000 number=8 hash=12287e…0465b5
INFO [12-28|20:06:19] Imported new chain segment               blocks=1 txs=0 mgas=0.000 elapsed=7.048ms  mgasps=0.000 number=9 hash=8e844b…b99d6c
INFO [12-28|20:06:22] Imported new chain segment               blocks=1 txs=0 mgas=0.000 elapsed=8.156ms  mgasps=0.000 number=10 hash=159b36…d4dde5
INFO [12-28|20:06:24] Imported new chain segment               blocks=1 txs=0 mgas=0.000 elapsed=6.549ms  mgasps=0.000 number=11 hash=9691005658a5

也就是说,节点1挖矿,节点2在同步数据信息。

停止节点1的挖矿,并查看coinbase地址金额:

> miner.stop()
true
> eth.getBalance(eth.coinbase)
140000000000000000000
> eth.coinbase
"0x60c8abe58c9dbc52a4ee9f8510f1799c432c0f3e"

这里我们知道了节点一种的地址信息和余额信息,那我们拿节点1的这个地址在节点2的控制台查询一下信息:

> eth.getBalance("0x60c8abe58c9dbc52a4ee9f8510f1799c432c0f3e")
140000000000000000000

很显然,节点2中也能查询到节点1中地址的余额。以上信息说明,节点1节点2的数据是完全同步的。

并且也可以在不同节点运行eth.blockNumber可以查到区块数量是保持一致的

9. How can I run go-ethereum as daemon process on Ubuntu?

  • Create a file geth.service:
[Unit]
Description=Ethereum go client

[Service]
Type=simple
ExecStart=/usr/bin/geth --networkid 15 --datadir /usr/local/ethereum/data --rpc --rpcaddr 172.16.61.17 --rpcport 8545 --nodiscover 2>>/usr/local/ethereum/data/geth.log

[Install]
WantedBy=default.target
  • Enable service:
systemctl --user enable /usr/local/ethereum/geth.service
systemctl --user start geth.service
  • test it
netstat -tunpl|grep 8545

geth attach ipc:/usr/local/ethereum/data/geth.ipc 
阅读更多

更多精彩内容