区块链-下一场变更

Step by Step 搭建私有链集群(Docker版本)

2018-04-13  本文已影响141人  f9b6fcef1d35

前言

最近研究了一段时间以太坊,各种专业术语解释,各种文档资料查看,尝试多种方案,最佳入手的体验私有链模式还是Docker,所以本文将详细描述利用Docker一步一步搭建私有链集群的过程。说是集群,其实不过搭建3个节点,没有优化过程,旨在给大家提供一个可以模拟入门的教程。

私有链:控制权限集中在某个公司或者组织,读取权限可以根据业务情况进行公开或者限定范围内公开,应用范围比较广泛。有关公有链、联盟链和私有链之间的关系,建议详细延伸阅读原版英文说明 Public and Private Blockchains

前提准备:

目标:


1. 服务器环境配置

1.1 安装EPEL

相关软件我们基本全部采用yum进行安装,由于一些软件包只包含在EPEL资源库中,所以必须引入EPEL源,以便整个演示能够顺利进行。

# yum install -y epel-release
1.2 安装必要的软件包

演示过程中,我们需要下载github项目,所以必须安装git;
其他软件主要是方便查看和测试使用;
Docker容器服务是我们主要使用的软件;

# yum install -y git wget tree telnet
# yum install -y docker

查看Docker容器是否安装成功,出现如下的版本号提示,说明成功。

# docker --version
Docker version 1.13.1, build 774336d/1.13.1

2. 配置Docker容器

2.1 启动Docker容器服务
# /bin/systemctl start docker.service
# /bin/systemctl  enable docker.service
设置开机自动启动
2.2 下载官方镜像文件

由于下载镜像需要时间较长,我们提前将需要的ubuntu 镜像下载下来,减少后续的等待时间。本演示我们使用ubuntu作为基础镜像。

# docker pull ubuntu
2.3 定制化Dockerfile文件

为什么使用Dockerfile?以及如何自己写Dockerfile?这里不做更详细的解释,建议用户参看官方说明文档Best practices for writing Dockerfiles

# mkdir /home/ethereum
# cd /home/ethereum
# vi Dockerfile

文件的详细内容如下:

FROM ubuntu

LABEL version="1.0"
LABEL maintainer="zhoulg@outlook.com"

ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install --yes software-properties-common
RUN add-apt-repository ppa:ethereum/ethereum
RUN apt-get update && apt-get install --yes geth

RUN adduser --disabled-login --gecos "ethereum user" eth

COPY genesis.json /home/eth/genesis.json
RUN chown -R eth:eth /home/eth/genesis.json

USER eth
WORKDIR /home/eth

RUN geth init genesis.json

ENTRYPOINT bash

文件内容简要说明:

  • 使用ubuntu:latest 作为基础镜像
  • 更新和安装必要的软件包
  • 创建运行账号,复制必要的文件以及工作目录
  • 运行geth(go-ethereum)初始化程序
2.4 生成genesis.json文件

注意上面的Dockerfile文件里面有一行,COPY命令,复制了本地的一个文件到容器内指定目录下。该文件是运行私有链的必须的配置文件,我们需要手工生成。网络上面有很多genesis.json文件的例子,官方也有很多介绍,这里不做过多的解释,如果用户对于该文件每一项的含义需要更加详细的说明,建议延伸阅读What does each genesis.json parameter mean?
这里我们的演示文件内容如下:

{
  "alloc": {
  },
  "config": {
    "chainId": 15,
    "homesteadBlock": 0,
    "eip155Block": 0,
    "eip158Block": 0
  },
  "nonce": "0x000000000000002a",
  "difficulty": "0x002000",
  "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "coinbase": "0x0000000000000000000000000000000000000000",
  "timestamp": "0x00",
  "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "extraData": "0x",
  "gasLimit": "0x2fefd8"
}

genesis.json必须在所有的节点都是相同的,所以我们直接复制到镜像文件里面,保持统一。

2.5 编译自定义的镜像
# docker build  -t  eth_node . 
Sending build context to Docker daemon 3.072 kB
Step 1/14 : FROM ubuntu
 ---> c9d990395902
Step 2/14 : LABEL version "1.0"
 ---> Running in c2efa7362edd
...... 其他信息省略,软件包下载需要一段时间......
Step 14/14 : ENTRYPOINT bash
 ---> Running in 69887d7b1b15
 ---> 32bb44f69667
Removing intermediate container 69887d7b1b15
Successfully built 32bb44f69667

最后出现Successfully 说明创建自定义镜像成功
查看镜像列表验证下

# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED              SIZE
eth_node            latest              32bb44f69667        About a minute ago   287 MB
docker.io/ubuntu    latest              c9d990395902        14 hours ago         113 MB

其中 eth_node 就是我们刚才自定义的镜像文件,接下来我们的演示将主要以这个镜像文件为基础。

3. 搭建私有链节点

3.1 启动3个节点

由于我们是在一台宿主机上面运行,所以我们需要规划好节点的对应端口号,默认geth 运行端口为 8545,我们定义节点如下:
+节点一名称:eth_node_1 映射宿主机端口号:8545
+节点二名称:eth_node_2 映射宿主机端口号:8546
+节点三名称:eth_node_3 映射宿主机端口号:8547

节点一启动

# docker run --rm -it -p 8545:8545  --name eth_node_1  eth_node 
默认启动后,已经登录进入容器内部,保持终端不动

容器内部退出终端的命令:ctrl + p + q (不要使用exit命令,会结束容器进程)

节点一已经启动,暂时保持不动
现在,重新打开一个新的终端
节点二启动

# docker run --rm -it -p 8546:8545  --name eth_node_2  eth_node
默认启动后,已经登录进入容器内部,保持终端不动
留意端口映射的不同,和节点名称

再次重新打开一个新的终端
节点三启动

# docker run --rm -it -p 8547:8545  --name eth_node_3  eth_node
默认启动后,已经登录进入容器内部,保持终端不动
留意端口映射的不同,和节点名称

以上我们已经启动了3个节点的容器服务

3.2 查询节点容器信息

检查节点的运行情况

# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                    NAMES
20b354c9dd15        eth_node            "/bin/sh -c bash"   5 seconds ago       Up 4 seconds        0.0.0.0:8547->8545/tcp   eth_node_3
f5d2b9558a3b        eth_node            "/bin/sh -c bash"   17 seconds ago      Up 16 seconds       0.0.0.0:8546->8545/tcp   eth_node_2
4cd4ed065442        eth_node            "/bin/sh -c bash"   56 seconds ago      Up 55 seconds       0.0.0.0:8545->8545/tcp   eth_node_1

几个重要的信息点,节点的名称,节点对应的ID(前4个字母即可),以及对应的端口映射,容器的运行状态
查询节点对应的IP地址

# 查询节点一的IP
# docker inspect 4cd4 --format='{{.NetworkSettings.IPAddress}}'
172.17.0.2
# 查询节点二的IP
# docker inspect f5d2 --format='{{.NetworkSettings.IPAddress}}'
172.17.0.3
# 查询节点三的IP
# docker inspect 20b3 --format='{{.NetworkSettings.IPAddress}}'
172.17.0.4

查询后对应的信息如下,需要记录下来备用:

节点 IP地址 映射端口 容器ID
eth_node_1 172.17.0.2 8545 4cd4
eth_node_2 172.17.0.3 8546 f5d2
eth_node_3 172.17.0.4 8547 20b3
3.3 配置节点容器
3.3.1 创建初始钱包

创建初始节点钱包,用于接收记账奖励,每个节点都需要创建。节点记账(挖矿)程序运行前,必须先建立初始的钱包,用于接收记账奖励,否则会无法启动记账程序。
%进入节点一(eth_node_1)的终端,创建初始钱包。为了减少交互输入密码步骤,我们简单采用密码文件的形式,正式环境不能这么操作。

# echo "123456" > mypassword.txt
# geth --password mypassword.txt account new
INFO [04-16|02:32:39] Maximum peer count                       ETH=25 LES=0 total=25
Address: {5edd8d6c2ea0328f54f91cd7714d1f9bbcb9911d}

记录下生成的钱包地址:5edd8d6c2ea0328f54f91cd7714d1f9bbcb9911d
查看钱包列表

# geth account list
INFO [04-16|02:35:02] Maximum peer count                       ETH=25 LES=0 total=25
Account #0: {5edd8d6c2ea0328f54f91cd7714d1f9bbcb9911d} keystore:///home/eth/.ethereum/keystore/UTC--2018-04-16T02-32-39.350890163Z--5edd8d6c2ea0328f54f91cd7714d1f9bbcb9911d

PS:keystore指向的加密的密钥文件


同样在eth_node_2 和 eth_node_3 上面执行同样的命令,全部创建好初始钱包地址。

3.3.2 创建用户钱包

同时创建一个用户钱包,稍后将用于本节点钱包转账以及不同节点间钱包转账。

# geth --password mypassword.txt account new
INFO [04-16|05:34:15] Maximum peer count                       ETH=25 LES=0 total=25
Address: {ef41fbddb7fd8cc42076c7613f98c533d55e8d99}

记录下用户钱包地址,后续将会使用到。
查看创建的钱包地址列表

# geth account list
INFO [04-16|05:34:58] Maximum peer count                       ETH=25 LES=0 total=25
Account #0: {5edd8d6c2ea0328f54f91cd7714d1f9bbcb9911d} keystore:///home/eth/.ethereum/keystore/UTC--2018-04-16T02-32-39.350890163Z--5edd8d6c2ea0328f54f91cd7714d1f9bbcb9911d
Account #1: {ef41fbddb7fd8cc42076c7613f98c533d55e8d99} keystore:///home/eth/.ethereum/keystore/UTC--2018-04-16T05-34-15.740848716Z--ef41fbddb7fd8cc42076c7613f98c533d55e8d99

#0号钱包地址为初始的钱包。
同样在eth_node_2 和 eth_node_3 上面执行同样的命令,全部创建一个用户钱包备用。

3.3.3 开始运行记账程序(也叫挖矿程序)

准备工作全部完成后,现在开始启动记账(挖矿)程序,需要指定一些参数。
节点一(eth_node_1)

# geth --identity="NODE_1" --networkid="500" --verbosity=1 --mine --minerthreads=1 --rpc --rpcaddr 0.0.0.0 console
Welcome to the Geth JavaScript console!

instance: Geth/NODE_1/v1.8.2-stable-b8b9f7f4/linux-amd64/go1.9.4
coinbase: 0x5edd8d6c2ea0328f54f91cd7714d1f9bbcb9911d
at block: 0 (Thu, 01 Jan 1970 00:00:00 UTC)
 datadir: /home/eth/.ethereum
 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

>eth.blockNumber
0
>... 一般需要等待10分钟左右创建创世块...
>eth.blockNumber
7

节点二(eth_node_2)

# geth --identity="NODE_2" --networkid="500" --verbosity=1 --mine --minerthreads=1 --rpc  --rpcaddr 0.0.0.0 console
Welcome to the Geth JavaScript console!

instance: Geth/NODE_2/v1.8.2-stable-b8b9f7f4/linux-amd64/go1.9.4
coinbase: 0xa849dc84dc2500ac4abb872b125c7e67012f6ec2
at block: 0 (Thu, 01 Jan 1970 00:00:00 UTC)
 datadir: /home/eth/.ethereum
 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

> 

节点三(eth_node_3)

# geth --identity="NODE_3" --networkid="500" --verbosity=1 --mine --minerthreads=1 --rpc  --rpcaddr 0.0.0.0 console
Welcome to the Geth JavaScript console!

instance: Geth/NODE_3/v1.8.2-stable-b8b9f7f4/linux-amd64/go1.9.4
coinbase: 0x26214c499202e2e6021f820e58c1eff2b1a252f9
at block: 0 (Thu, 01 Jan 1970 00:00:00 UTC)
 datadir: /home/eth/.ethereum
 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

>

留意3个节点的启动参数不同

参数 说明
--identity 每个节点必须唯一
--networkid 在集群里面保持一致,所有的节点都相同
--verbosity 有几个级别0=silent, 1=error, 2=warn, 3=info, 4=core, 5=debug, 6=detail (default: 3)
--mine 开始启动记账(挖矿)程序
--rpc 允许RPC
--rpcport 指定的RPC端口号,演示保持一样,宿主机映射不一样
3.3.4 命令参数简要说明

所有的参数的详细解释说明,请参看geth命令行参数说明

4 配置节点集群

4.1 查询节点信息IPC RPC 参数

所有的节点都开始记账(挖矿)程序后,我们就需要配置集群的节点信息,让3个节点能够互相发现,并同步信息。

> admin.peers
[]

默认都是空的,没有其他节点信息
现在查询出来3个节点的参数
节点一(eth_node_1)

> admin.nodeInfo.enode
"enode://4250b0b6b1029b0874a5c5b0806299b04206ef21c22a0a5a476fe87ae622beeca69c55b4e000110d2cb2a72da3af15325bb4e43d4ee6d19111ec4319256348e1@[::]:30303"

我们将末尾的 [::]替换为我们的内网IP地址,前面有记录,172.17.0.2
现在节点一对应的修改如下:
enode://4250b0b6b1029b0874a5c5b0806299b04206ef21c22a0a5a476fe87ae622beeca69c55b4e000110d2cb2a72da3af15325bb4e43d4ee6d19111ec4319256348e1@172.17.0.2:30303

其他2个节点执行同样的操作和替换,实例如下:
eth_node_1:"enode://4250b0b6****6348e1@172.17.0.2:30303"
eth_node_2:"enode://ddd066c0****35f75c5@172.17.0.3:30303"
eth_node_3:"enode://af5e0fd27****31d170@172.17.0.4:30303"
为了演示,中间的字符省略

4.2 添加节点

现在我们已经得到所有节点的URL信息,我们需要让其他节点能够发现另外的2个节点,手工添加上对应的参数。

在所有的节点都执行如下的定义
> enode1 = "enode://4250b0b6b1029b0874***48e1@172.17.0.2:30303"
> 
> enode2 = "enode://ddd066c0442662976ac***75c5@172.17.0.3:30303"
> 
> enode3 = "enode://af5e0fd27bf9480621****d170@172.17.0.4:30303"
> 

在每个几点上面增加其他节点的信息
节点一(eth_node_1)

> admin.addPeer(enode2)
true
> admin.addPeer(enode3)
true

节点二(eth_node_2)

> admin.addPeer(enode1)
true
> admin.addPeer(enode3)
true

节点三(eth_node_3)

> admin.addPeer(enode1)
true
> admin.addPeer(enode2)
true
4.3 查询节点集群信息

所有的节点都添加信息后,再次查看节点情况

> admin.peers
[{
    caps: ["eth/63"],
    id: "af5e0fd27bf94806213b824ac0814c45106bbc834e406fd617f838af61dd98fd131c83c2394145678367ee0d80ea55a066283a4f7586793143c35aa2c431d170",
    name: "Geth/NODE_3/v1.8.2-stable-b8b9f7f4/linux-amd64/go1.9.4",
    network: {
      inbound: false,
      localAddress: "172.17.0.2:37019",
      remoteAddress: "172.17.0.4:30303",
      static: true,
      trusted: false
    },
    protocols: {
      eth: {
        difficulty: 8192,
        head: "0x8e3caef2da8f25e5172a86bac2dbd2611fa4de6c3cc28c301186d537afc77943",
        version: 63
      }
    }
}, {
    caps: ["eth/63"],
    id: "ddd066c0442662976ac3765d80929ad76ea0420b915b69ac7ca5bc7002b37f180b6129af7e7a9e0dc2a5d9153578a05810a409a57878ef919ddc2edd735f75c5",
    name: "Geth/NODE_2/v1.8.2-stable-b8b9f7f4/linux-amd64/go1.9.4",
    network: {
      inbound: false,
      localAddress: "172.17.0.2:42844",
      remoteAddress: "172.17.0.3:30303",
      static: true,
      trusted: false
    },
    protocols: {
      eth: {
        difficulty: 4337792,
        head: "0xb54837c3a85bac126297b3b6be302f22b31aa66c78f7089584b7d787808cf5b4",
        version: 63
      }
    }
}]

其他2个节点的信息基本一致,除了remoteAddress不一样

4.4 查询节点区块同步情况

节点都加入集群后,在所有的节点查看区块情况,应该所有的节点都是一样的,或者由于延迟相差几个区块。

> eth.blockNumber
282

这样我们的私有链集群都搭建成功了,所有的节点区块保持一致。

4.5 监控节点区块同步情况

接下来,不要关闭上面的3个节点终端,留待备用。
我们重新打开一个宿主机的终端,尝试通过接口监控节点的区块同步情况,这里我们仅演示下nodejs。
为了运行nodejs 程序,我们需要安装nodejs软件包

# yum install -y nodejs
# node -v
v6.14.0

本监控脚步我们引用了 node-json-rpc 库,需要提前安装

# npm install  node-json-rpc
npm: relocation error: npm: symbol SSL_set_cert_cb, version libssl.so.10 not defined in file libssl.so.10 with link time reference

发现报错了,这里是由于openssl库的问题导致的,重新安装openssl库

# yum install -y openssl

然后再次执行 npm 安装程序

# npm install node-json-rpc
/home/ethereum
└── node-json-rpc@0.0.1 

OK,安装成功。
默认库文件就在当前目录下

# tree node_modules/
node_modules/
└── node-json-rpc
    ├── lib
    │   ├── index.js
    │   ├── rpcauth.js
    │   ├── rpcclient.js
    │   └── rpcserver.js
    ├── LICENSE
    ├── package.json
    ├── README.md
    └── test
        ├── etc
        │   ├── optsclient.js
        │   └── optsserver.js
        └── rpc.js

4 directories, 10 files

运行命令行NodeJS 的监控同步程序

# node  monitor.js 
ETH_NODE_2 coinbase: 0xa849dc84dc2500ac4abb872b125c7e67012f6ec2
ETH_NODE_3 block number: 556
ETH_NODE_1 coinbase: 0x5edd8d6c2ea0328f54f91cd7714d1f9bbcb9911d
ETH_NODE_1 block number: 556
ETH_NODE_2 block number: 556
ETH_NODE_3 coinbase: 0x26214c499202e2e6021f820e58c1eff2b1a252f9
ETH_NODE_1 coinbase: 0x5edd8d6c2ea0328f54f91cd7714d1f9bbcb9911d
ETH_NODE_3 coinbase: 0x26214c499202e2e6021f820e58c1eff2b1a252f9
ETH_NODE_2 coinbase: 0xa849dc84dc2500ac4abb872b125c7e67012f6ec2
ETH_NODE_1 block number: 557
ETH_NODE_2 block number: 557
ETH_NODE_3 block number: 557
......

上面我们看见3个节点的区块都保持一致,私链集群运行成功。
由于是单服务器上面做的演示,这时候宿主机的负载就有点大了,区块的产生也不会很快。

5 钱包转账

5.1 查询初始钱包余额

接下来开始演示钱包的相关操作
进入节点一(eth_node_1)终端,查看钱包余额

>  eth.accounts
["0x5edd8d6c2ea0328f54f91cd7714d1f9bbcb9911d", "0xef41fbddb7fd8cc42076c7613f98c533d55e8d99"]
> node1_w1 = eth.accounts[0]
>  eth.getBalance(node1_w1)
3.013125e+21
> web3.fromWei(eth.getBalance(node1_w1))
3037.03125

web3.fromWei 函数将余额转换为易读的单位wei,便于后续演示。
其他节点基本相同。

5.2 查询用户钱包余额

现在查看用户的钱包的余额情况

>  eth.accounts
["0x5edd8d6c2ea0328f54f91cd7714d1f9bbcb9911d", "0xef41fbddb7fd8cc42076c7613f98c533d55e8d99"]
> node1_w2 = eth.accounts[1]
> web3.fromWei(eth.getBalance(node1_w2))
0

现在用户钱包还是空的,没有余额

5.3 初始钱包->用户钱包转账

我们尝试做一笔转账操作
在节点一(eth_node_1),从初始钱包->用户钱包,转账40个Wei

>  eth.sendTransaction({from: node1_w1, to: node1_w2, value: web3.toWei(40, "ether")})
Error: authentication needed: password or unlock
    at web3.js:3143:20
    at web3.js:6347:15
    at web3.js:5081:36
    at <anonymous>:1:1

我们发现报错了,密码不对或者需要解锁
我们先把初始账号进行解锁操作

> personal.unlockAccount(node1_w1, "123456", 300)   
true

解锁钱包,需要执行我们生成钱包的时候指定的密码,这里是123456,解锁时间300秒。
返回true 说明解锁成功,继续我们的转账操作

> eth.sendTransaction({from: node1_w1, to: node1_w2, value: web3.toWei(40, "ether")})
"0x844c074011fc256e8c3ae8e0a50f7a149c5785afcb78e750e091be90bbede22e"

返回的是交易号

5.4 查询挂起的交易
> eth.pendingTransactions
[]
这里如果区块同步的慢,可以看到挂起的交易
出现[],说明我们的交易已经同步了
> eth.getTransaction('0x844c074011fc256e8c3ae8e0a50f7a149c5785afcb78e750e091be90bbede22e')
{
  blockHash: "0x54c455696b0321e58cee3e4ea9d50da98ee5842d6d2db5bcdd774de56d5f688d",
  blockNumber: 851,
  from: "0x5edd8d6c2ea0328f54f91cd7714d1f9bbcb9911d",
  gas: 90000,
  gasPrice: 18000000000,
  hash: "0x844c074011fc256e8c3ae8e0a50f7a149c5785afcb78e750e091be90bbede22e",
  input: "0x",
  nonce: 0,
  r: "0xf17019be2e3d55ede4a5fde3333a4f1a8164b34004764143d74565db77985df1",
  s: "0x48f5ad381e776de128cc9a137a3979b3f3de94cf6412e3a35213d1e7592c0315",
  to: "0xef41fbddb7fd8cc42076c7613f98c533d55e8d99",
  transactionIndex: 0,
  v: "0x41",
  value: 40000000000000000000
}

由于我们3个节点是同步的,所以在所有的区块,都查看到交易

5.5 查询用户钱包余额

交易确认后,我们就可以在用户钱包查看到余额

>  web3.fromWei(eth.getBalance(node1_w2))
40

这样我们在同一个节点的转账操作就成功了。

5.6 跨节点钱包转账

现在我们进行在不同节点之间的钱包转账操作
上一步操作中,我们节点一的用户钱包,已经有40wei,这一步我们转移到节点二用户钱包 15wei,转移到节点三用户钱包 10wei。
节点一(eth_node_1)

> eth.accounts
["0x5edd8d6c2ea0328f54f91cd7714d1f9bbcb9911d", "0xef41fbddb7fd8cc42076c7613f98c533d55e8d99"]

节点二(eth_node_2)

> eth.accounts
["0xa849dc84dc2500ac4abb872b125c7e67012f6ec2", "0xbe38275ed6414268698294a9a1c06909e8b96a5a"]

节点三(eth_node_3)

> eth.accounts
["0x26214c499202e2e6021f820e58c1eff2b1a252f9", "0x48aa3d8e793e8d2da00b23ecea1555c3b82e3262"]

重新登录节点一(eth_node_1)
先确认用户钱包余额

> web3.fromWei(eth.getBalance('0xef41fbddb7fd8cc42076c7613f98c533d55e8d99'))
40
>  web3.fromWei(eth.getBalance('0xbe38275ed6414268698294a9a1c06909e8b96a5a'))
0
> web3.fromWei(eth.getBalance('0x48aa3d8e793e8d2da00b23ecea1555c3b82e3262'))
0

开始转账操作,同样转账前,我们需要先解锁节点一的用户钱包

> personal.unlockAccount('0xef41fbddb7fd8cc42076c7613f98c533d55e8d99', "123456", 300)
true
> eth.sendTransaction({from: '0xef41fbddb7fd8cc42076c7613f98c533d55e8d99', to: '0xbe38275ed6414268698294a9a1c06909e8b96a5a', value: web3.toWei(15, "ether")})
"0x6a60647492fcaea93a721ab7dc761a8fade6fd44d7f0283ee38fb5d146773c4e"
> eth.sendTransaction({from: '0xef41fbddb7fd8cc42076c7613f98c533d55e8d99', to: '0x48aa3d8e793e8d2da00b23ecea1555c3b82e3262', value: web3.toWei(10, "ether")})
"0xd95914eeccc1ec05fd22c15f29e0d3515b904268074124aa9f669fd529efcae1"

> eth.pendingTransactions
[]
没有挂起的交易
> eth.sendTransaction({from: '0xef41fbddb7fd8cc42076c7613f98c533d55e8d99', to: '0xbe38275ed6414268698294a9a1c06909e8b96a5a', value: web3.toWei(15, "ether")})
"0x6a60647492fcaea93a721ab7dc761a8fade6fd44d7f0283ee38fb5d146773c4e"
> eth.sendTransaction({from: '0xef41fbddb7fd8cc42076c7613f98c533d55e8d99', to: '0x48aa3d8e793e8d2da00b23ecea1555c3b82e3262', value: web3.toWei(10, "ether")})
"0xd95914eeccc1ec05fd22c15f29e0d3515b904268074124aa9f669fd529efcae1"
> eth.pendingTransactions
[{
    blockHash: null,
    blockNumber: null,
    from: "0xef41fbddb7fd8cc42076c7613f98c533d55e8d99",
    gas: 90000,
    gasPrice: 18000000000,
    hash: "0x6a60647492fcaea93a721ab7dc761a8fade6fd44d7f0283ee38fb5d146773c4e",
    input: "0x",
    nonce: 0,
    r: "0x703aef116bf4226cf5d4ae14c16ab2e365240b951f22c127f0834f4d07f1c91",
    s: "0x5811d14a3ae3abd47eff111648fbdbbef8d13878961981a3d0110e1b6ac43c0",
    to: "0xbe38275ed6414268698294a9a1c06909e8b96a5a",
    transactionIndex: 0,
    v: "0x42",
    value: 15000000000000000000
}, {
    blockHash: null,
    blockNumber: null,
    from: "0xef41fbddb7fd8cc42076c7613f98c533d55e8d99",
    gas: 90000,
    gasPrice: 18000000000,
    hash: "0xd95914eeccc1ec05fd22c15f29e0d3515b904268074124aa9f669fd529efcae1",
    input: "0x",
    nonce: 1,
    r: "0x31d052be55fe988aa06931b807e59b9bd9dd20fa47c076e2135c40022b48df57",
    s: "0x24faf81fb0b88c50ed2fa5f26d9292f0554eeed41e112bffba0bc374a82ad730",
    to: "0x48aa3d8e793e8d2da00b23ecea1555c3b82e3262",
    transactionIndex: 0,
    v: "0x41",
    value: 10000000000000000000
}]
这里可以查到有2笔挂起的交易待确认,延迟一会等交易确认完毕。
> eth.pendingTransactions
[]
5.7 查询用户钱包余额

交易全部确认完毕后,再次查询所有钱包的用户钱包余额

>  web3.fromWei(eth.getBalance('0xbe38275ed6414268698294a9a1c06909e8b96a5a'))
15
> web3.fromWei(eth.getBalance('0x48aa3d8e793e8d2da00b23ecea1555c3b82e3262'))
10
> web3.fromWei(eth.getBalance('0xef41fbddb7fd8cc42076c7613f98c533d55e8d99'))
14.999244

这里我们发现,节点二和节点三的用户钱包,都已经有余额了,说明转账成功了。
节点一的钱包余额不是 40-15-10 = 15,而是比15小一些,这里面主要每一笔交易都需要交易费的,从转出方进行扣除,也就是手续费,手续费支付给参与记账的节点。

6 监控私有链节点运行情况

6.1 eth-netstats

以太坊网络监控,官方网站:https://ethstats.net/
本小节的目标,就是我们搭建一个自己的私链的监控平台
首先,新打开一个宿主机终端,下载源码

# cd  /home/ethereum
# git clone https://github.com/cubedro/eth-netstats.git
# cd eth-netstats
# npm install
# npm install -g grunt-cli
这一步需要一段时间

这时候一般会报告一些错误,主要一些依赖库没有自动安装
我们需要手工安装上

# npm install debug
# npm install lodash logger chalk http  express primus primus-emit primus-spark-latency
# npm install body-parser websockets
# npm install --save ws
# npm install d3 geoip-lite 
# npm install jade
# npm install
# npm install -g grunt-cli

运行成功后,通过浏览器访问页面,默认监听的宿主机的3000端口
在我的演示中:


eth-netstat.png

现在还没有数据,显示的都是空的,接下来我们需要给页面推送数据过来。

6.2 eth-net-intelligence-api

这个是后端服务,与以太坊一起运行并跟踪网络状态,通过JSON-RPC获取信息并通过WebSockets连接到eth-netstats以提供信息。 有关完整安装说明,请前往阅读eth-net-intelligence-api

另外打开一个宿主机的终端

# cd /home/ethereum
# git clone https://github.com/cubedro/eth-net-intelligence-api.git

需要提前安装pm2 软件包

# npm install -g pm2
# pm2 --version
2.10.2

需要复制一份app.json 文件对应的节点

# cp app.json enode1.json
文件内容如下
# cat enode1.json
[
  {
    "name"              : "enodeAA",
    "script"            : "app.js",
    "log_date_format"   : "YYYY-MM-DD HH:mm Z",
    "merge_logs"        : false,
    "watch"             : false,
    "max_restarts"      : 10,
    "exec_interpreter"  : "node",
    "exec_mode"         : "fork_mode",
    "env":
    {
      "NODE_ENV"        : "production",
      "RPC_HOST"        : "172.17.0.2",
      "RPC_PORT"        : "8545",
      "LISTENING_PORT"  : "30303",
      "INSTANCE_NAME"   : "enodeAA",
      "CONTACT_DETAILS" : "",
      "WS_SERVER"       : "http://192.168.6.116:3000",
      "WS_SECRET"       : "admin",
      "VERBOSITY"       : 2
    }
  }
]

同样再次复制一份生成enode2.json 和 enode3.json

# cat enode2.json
[
  {
    "name"              : "enodeAB",
    "script"            : "app.js",
    "log_date_format"   : "YYYY-MM-DD HH:mm Z",
    "merge_logs"        : false,
    "watch"             : false,
    "max_restarts"      : 10,
    "exec_interpreter"  : "node",
    "exec_mode"         : "fork_mode",
    "env":
    {
      "NODE_ENV"        : "production",
      "RPC_HOST"        : "172.17.0.3",
      "RPC_PORT"        : "8545",
      "LISTENING_PORT"  : "30303",
      "INSTANCE_NAME"   : "enodeAB",
      "CONTACT_DETAILS" : "",
      "WS_SERVER"       : "http://192.168.6.116:3000",
      "WS_SECRET"       : "admin",
      "VERBOSITY"       : 2
    }
  }
]
# cat enode3.json
[
  {
    "name"              : "enodeAC",
    "script"            : "app.js",
    "log_date_format"   : "YYYY-MM-DD HH:mm Z",
    "merge_logs"        : false,
    "watch"             : false,
    "max_restarts"      : 10,
    "exec_interpreter"  : "node",
    "exec_mode"         : "fork_mode",
    "env":
    {
      "NODE_ENV"        : "production",
      "RPC_HOST"        : "172.17.0.4",
      "RPC_PORT"        : "8545",
      "LISTENING_PORT"  : "30303",
      "INSTANCE_NAME"   : "enodeAC",
      "CONTACT_DETAILS" : "",
      "WS_SERVER"       : "http://192.168.6.116:3000",
      "WS_SECRET"       : "admin",
      "VERBOSITY"       : 2
    }
  }
]

开始运行底层数据收集程序

# pm2 start enode1.json
# pm2 start enode2.json
# pm2 start enode3.json
# pm2 list
┌──────────┬────┬──────┬──────┬────────┬─────────┬────────┬─────┬───────────┬──────┬──────────┐
│ App name │ id │ mode │ pid  │ status │ restart │ uptime │ cpu │ mem       │ user │ watching │
├──────────┼────┼──────┼──────┼────────┼─────────┼────────┼─────┼───────────┼──────┼──────────┤
│ enodeAA   │ 0  │ fork │ 3365 │ online │ 85      │ 2s     │ 24% │ 21.3 MB   │ root │ disabled │
│ enodeAB   │ 1  │ fork │ 3351 │ online │ 5       │ 2s     │ 0%  │ 22.2 MB   │ root │ disabled │
│ enodeAC   │ 2  │ fork │ 3333 │ online │ 1       │ 2s     │ 0%  │ 24.1 MB   │ root │ disabled │
└──────────┴────┴──────┴──────┴────────┴─────────┴────────┴─────┴───────────┴──────┴──────────┘
 Use `pm2 show <id|name>` to get more details about an app

状态一栏,如果是online说明运行成功,如果有错误,会提示 errored
可以通过下面的命令找到提示信息,安装需要的依赖库

# pm2 logs

如果报错,需要提前安装好一些依赖库

# npm install  chalk util  web3   debounce geth
6.3 浏览器监控

状态如下图所示


eth3.png

7 备注说明

后续进行持续更新!

上一篇下一篇

猜你喜欢

热点阅读