0x00 序言
最近不论是币圈还是技术圈,区块链都这么火热,17 年 9 月 ICO 在国内被取缔,没过几天国内所有数字货币交易所都被叫停,消息一出无数韭菜被迫割肉。谁料币圈在脱离中国市场后一路高歌猛击,比特币为首的数字货币在此之后一路拉升再创新高。而 17 年一开始数字货币的基石——区块链技术更是被捧上天。更有甚者将 18 年称作区块链元年。这是继人工智能之后的又一个被广泛看好的技术。并且区块链的应用落地将比人工智能门槛更低,马太效应已经显现,会有大量资本涌入,也隐藏着很多机会。
0x01 配置创世区块
搭建私有链前需要创建一个 json 配置文件,用于初始化创世区块。这里作者将创建一个名为 genesis.json
的文件。
内容如下:
{ "config": { "chainId": 1000, "homesteadBlock": 0, "eip155Block": 0, "eip158Block": 0 }, "coinbase" : "0x0000000000000000000000000000000000000000", "difficulty" : "0x400", "extraData" : "Oh My God!", "gasLimit" : "0x2fefd8", "nonce" : "0x0000000000000042", "mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000", "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", "timestamp" : "0x00", "alloc": {}}
配置说明:
config.chainId // 区块链的ID,在 geth 命令中的 --networkid 参数需要与 chainId 的值一致config.homesteadBlock // Homestead 硬分叉区块高度,不需要关注config.eip155Block // EIP 155 硬分叉高度,不需要关注config.eip158Block // EIP 158 硬分叉高度,不需要关注coinbase // 矿工账号,第一个区块挖出后将给这个矿工账号发送奖励的以太币difficulty // 难度值,越大越难extraData // 附加信息随便填gasLimit // gas 的消耗总量限制,用来限制区块能包含的交易信息总和,因为我们是私有链,所以填最大nonce // 一个 64 位随机数mixhash // 与 nonce 配合用于挖矿,由上一个区块的一部分生成的 hashparentHash // 上一个区块的 hash 值alloc // 预设账号以及账号的以太币数量,私有链挖矿比较容易可以不配置好啦~配置项解释完毕可以开始初始化创世区块了!!!
0x02 初始化创世区块
这里需要用到 geth 的 init 命令。
geth --datadir $HOME/privateNet/bootnode init genesis.json ...INFO [01-13|20:07:29] Successfully wrote genesis state database=lightchaindata hash=49c2dd…aff051
参数解释:
--datadir
指定数据存放的目录 0x03 启动私链起始节点(boot node)
geth --datadir $HOME/privateNet/bootnode \--networkid 1000 \--identity "bootnode" \--port 30303 \--rpc \--rpcport 8545 \--rpccorsdomain "*" \--nodiscover \--verbosity 4 \console...Welcome to the Geth JavaScript console!instance: Geth/v1.7.2-stable/darwin-amd64/go1.9.2 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>
参数说明:
--networkid
与 genesis.json 中的 chainId 一致--identity
设置节点ID--port
geth 节点端口 default: 30303--rpc
启用 rpc 服务 default: 8545--rpcport
rpc 服务端口--rpccorsdomain
这里配置“*”是允许通过任意域名访问,也可以指定具体的域名如:“”--nodiscover
关闭节点自动发现--verbosity
日志等级:0=silent, 1=error, 2=warn, 3=info, 4=debug, 5=detail (default: 3) 提示:
在 geth console 模式下可以使用eth.getBlock(区块号)
查看区块内容 > eth.getBlock(0){ difficulty: 16, extraData: "0x01010101", gasLimit: 3141592, gasUsed: 0, hash: "0xdd97c4469e8bf4054b7c756c1d620ee21d41e5ade129adfbb5ddce3937d6f66e", logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", miner: "0x0000000000000000000000000000000000000000", mixHash: "0x0000000000000000000000000000000000000000000000000000000000000000", nonce: "0x0000000000000042", number: 0, parentHash: "0x0000000000000000000000000000000000000000000000000000000000000000", receiptsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", size: 508, stateRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", timestamp: 0, totalDifficulty: 16, transactions: [], transactionsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", uncles: []}
创世都搞定了接下来的事就更简单了~
0x04 创建新账号
在 geth console 模式下实际是一个 JavaScript 运行环境,有点类似 Node.js 的命令行模式。可以使用一些预设的对象方法执行一些操作。personal
对象下的 newAccount
方法可以创建新的账号,该方法返回一个账号地址。
personal.newAccount("yourPassphrase")"0xe6a8faf90ddc1ec0c335d0a83f660c5de9ec12d1"
0x05 挖矿
挖矿赚取以太比奖励,由于创世区块中难度值设置的很低因此不会很难挖。
miner.start()
挖矿同时可以用 geth attach 命令打开另一个终端查看账户余额
geth attach $HOME/privateNet/bootnode/geth.ipc...> eth.getBalance(eth.accounts[0])
0x06 创建新节点
都说区块链是去中心化的账本那么可以满足于单机挖矿?说干就干接下来创建一个新节点并将太接入我们的主节点。这里需要注意以下几点:
- 新节点的 networkid 要与 boot node 一致
- 需要与 boot node 使用同一个创世区块
-
如果多个节点都在一台机器上注意端口区分,避免端口冲突
--port 30304
--rpcport 9545
geth --datadir $HOME/privateNet/node2 init genesis.jsongeth --datadir $HOME/privateNet/node2 \--fast \--cache 512 \--port 30304 \--networkid 1000 \--identity "node2" \--rpc \--rpcport 9545 \--rpccorsdomain "*" \--nodiscover \--verbosity 4 \console
接来下需要建立节点间的联系
这里有三种方式:
- 使用 admin.addPeer() 命令手动添加 bootnode
在 boot node 上获取 enode 信息:
admin.nodeInfo.enode"enode://xxx"
将 boot node 的 enode 信息写入 node2
admin.addPeer("enode://xxx");
geth console 中显示
DEBUG[01-13|22:54:35] Ethereum peer connected id=b0ac9d91125e7a1b conn=inbound name=Geth/node2/v1.7.2-stable/darwin-amd64/go1.9.2
使用 admin.peers
命令可以看到两个节点之间已经取得联系
# boot node> admin.peers[{ caps: ["eth/63"], id: "b0ac9d91125e7a1bebff6d473cd3a9bcf41243060e52f97c3744053c060f24c9e2c36cc181e589d46ede5559509c7dd2c8528d9d039ee5cc900e0700f56c27e7", name: "Geth/node2/v1.7.2-stable/darwin-amd64/go1.9.2", network: { localAddress: "[::1]:30303", remoteAddress: "[::1]:64087" }, protocols: { eth: { difficulty: 245728379, head: "0x65ae6da1d42ab4ea75bc468e47912cc61cd3a51b5d406482904c7d0c51785548", version: 63 } }}]
# node2> admin.peers[{ caps: ["eth/62", "eth/63"], id: "8cc33dabb8f5fa2033cf646549e6988418439c8756d96e62f418ebb57ecd0cc2cfbf7ceab4450d262331d8d4fe68358200030e6a16988f2ebe393bf6214380fe", name: "Geth/bootnode/v1.7.2-stable/darwin-amd64/go1.9.2", network: { localAddress: "[::1]:64087", remoteAddress: "[::1]:30303" }, protocols: { eth: { difficulty: 245728379, head: "0x65ae6da1d42ab4ea75bc468e47912cc61cd3a51b5d406482904c7d0c51785548", version: 63 } }}]
- 使用 static-nodes.json 文件
除了在 geth console 下使用 admin.addPeer()
也可在 --datadir
指定的目录中添加 static-nodes.json 文件让节点取得联系
[ "enode://xxx" // boot node 地址]
- 使用命令行
--bootnodes
参数指定 enode
0x07 节点间服务自发现
// 假设有 node2,node3 都加入 bootnode,验证node2 node3 之间能否自动服务发现