利用vagrant搭建quorum的7nodes节点:关于增加节点的方法

quorum区联盟块链7nodes例子,新增节点无法加入联盟的解决办法

  • 利用vagrant搭建quorum的7nodes节点:关于增加节点的方法
    • 教程指导链接
    • 前期准备
    • 搭建quorum平台
    • 增加节点问题
    • 查看源代码
      • istanbul-init.sh文件
      • istanbul-start.sh文件
    • 思路整理
    • 解决办法
    • 总结

利用vagrant搭建quorum的7nodes节点:关于增加节点的方法

quorum是jp摩根推出的区块链平台。它既可以搭建私人链又可以搭建联盟链,本事的共识算法也包括Raft、Istanbul BFT、PoA三种。我初次学习是利用Istabul BFT搭建联盟链。搭建的过程很快,但在后续增加节点入联盟的时候无法正确加入,研究了一阵后,新加入了一个节点。于是写了这篇文章,希望对后面初学的人,少走一点弯路。

教程指导链接

搭建过程我参考了两个链接:

  1. https://github.com/jpmorganchase/quorum-examples,JP摩根在Github上的代码教程;
  2. https://www.trufflesuite.com/tutorials/building-dapps-for-quorum-private-enterprise-blockchains,Truffle上的搭建教程,想自己部署合约的可以看看这个;

前期准备

  • VirtualBox
  • Vagrant

下载这两个软件并安装

搭建quorum平台

  1. 新建文件夹
mkdir workspace
cd workspace
  1. 下载quorum-examples
git clone https://github.com/jpmorganchase/quorum-examples
  1. 建立并连接虚拟机
vagrant up
vagrant ssh

虚拟系统默认位置在 c:user\用户名\VirtualBox VMs目录下,可以用压缩软件打开自行查看内部文件,7nodes节点在home/vagrant文件夹内,但无法更改,更改需要其它软件。还有vagrant up阶段会下载大量东西,最好搭梯子,不然有时速度很慢。
4. 配置、启动7nodes例子

 cd quorum-examples/7nodes/
 #./<consensu>-init.sh,不同共识算法分别启动不同-init.sh,
 #consensu = raft/istanbul/clique,后面也的-start.sh文件一样。
 ./istanbul-init.sh  
 ./istanbul-start.sh

在开始之前,最好多看看各个文件夹的README。quorum支持三种共识算法,Raft、Istanbul BFT、PoA,可以自行选择不同的共识算法。不同算法,代码不同。默认管理器也有tessera和constellation两种,两个功能类似,用哪个都可以。
我自己选的IBFT算法,IBFT默认选用tessera交易管理器,要更改直接在后面加constellation即可。

./istanbul-init.sh  constellation
  1. 启动cakeshop图形化窗口
./cakeshop-start.sh

最后打开http://localhost:8999/,即可查看交易信息等。

增加节点问题

在建立好以后,本以为一切大功告成,但是问题来了。我按照网上教程增加新的节点,节点可以增加,但始终无法加入联盟。很多方法用

admin.addPeer("enode:....")

返回都是true,但是始终无法在admin.peers中查看到。

查看源代码

网上方法行不通了,就打算自己查看源代码,了解系统默认节点建立过程。
我们在执行过程中,总共只干了两步

 ./istanbul-init.sh  
 ./istanbul-start.sh

istanbul-init.sh文件

  1. 首先查看 istanbul-init.sh文件
1.#!/bin/bash
2.set -u
3.set -e
.........
47.echo "[*] Cleaning up temporary data directories"
48.rm -rf qdata
49.mkdir -p qdata/logs
.......
70.genesisFile=./istanbul-genesis.json
71.tempGenesisFile=
.....
80.for i in `seq 1 ${
     numNodes}`
81.do
    echo "[*] Configuring node ${i}"
    mkdir -p qdata/dd${
     i}/{
     keystore,geth}
    if [[ "$istanbulTools" == "true" ]]; then
        iMinus1=$(($i - 1))
        cp ${
     iMinus1}/nodekey qdata/dd${
     i}/geth/nodekey
    else
        cp raft/nodekey${
     i} qdata/dd${
     i}/geth/nodekey
    fi
    cp ${
     permNodesFile} qdata/dd${
     i}/static-nodes.json
     if ! [[ -z "${STARTPERMISSION+x}" ]] ; then
        cp ${
     permNodesFile} qdata/dd${
     i}/permissioned-nodes.json
     fi
     cp keys/key${
     i} qdata/dd${
     i}/keystore
     geth --datadir qdata/dd${
     i} init $genesisFile
96.done
97.
98.#Initialise Tessera configuration
99../tessera-init.sh
101.#Initialise Cakeshop configuration
102../cakeshop-init.sh
103.rm -f $tempGenesisFile $permNodesFile

中间没用的就没写了,第49行建立qdata/logs文件夹,第70行将istanbul-genesis.json赋给genesiFile,中间还有一些if语句,但是对我这个模式没影响,就没贴出来了。第82-94行则分别将一些自带的文件复制进去。nodekey后面生成新节点会生成,permissioned-node.json为自带默认文件,可以自己更改,我没改。
到第95步就很关键,利用genesisFile建立第一个块,而这个genesisFile根据条件不同,可以是不同的文件。同一个区块链,必须是同一个创世块,我这里是istanbul-genesis.json,所以我后期新建节点必须基于istanbul-genesis.json这个建立区块链。
完了以后就是执行tessera-init.sh。

2.查看tessera-init.sh

1.#!/usr/bin/env bash
2.# Initialise data for Tessera nodes.
3.# This script will normally perform initialisation for 7 nodes, however
.......
34.numNodes=7
35.if [[ -f qdata/numberOfNodes ]]; then
36.    numNodes=`cat qdata/numberOfNodes`
37.fi
.......
67.# Dynamically create the config for peers, depending on numNodes
68.getPeerIPs	# get list of IP addresses for peers
69.peerList=
70.for i in `seq 1 ${
     numNodes}`
71.do
72.    if [[ $i -ne 1 ]]; then
        peerList="$peerList,"
    fi

    portNum=$((9000 + $i))

    hostIP=${
     peerIPList[$i]}
    if [[ "$hostIP" == "" ]]; then
        hostIP="127.0.0.1"
        echo "[*] WARNING: host IP for node $i not found in permissioned-nodes.json, defaulting to '${hostIP}'"
    fi

    peerList="${
     peerList}
        {
     
            \"url\": \"http://${
     hostIP}:${
     portNum}\"
        }"
88.done
89.
90.# Write the config for the Tessera nodes
currentDir=$(pwd)
for i in `seq 1 ${
     numNodes}`
do
    DDIR="${currentDir}/qdata/c${i}"
    mkdir -p ${
     DDIR}
    mkdir -p qdata/logs
    if [ "$encryptorType" == "NACL" ]; then
        cp "keys/tm${i}.pub" "${DDIR}/tm.pub"
        cp "keys/tm${i}.key" "${DDIR}/tm.key"
    fi
    rm -f "${DDIR}/tm.ipc"

103.    serverPortP2P=$((9000 + ${
     i}))
104.    serverPortThirdParty=$((9080 + ${
     i}))
105.    serverPortEnclave=$((9180 + ${
     i}))
.....
277.#create a copy of private-contract.js where the public key of the tessera node (privateFor) is replaced with 278.the newly generated key of the last node (numNodes)
279.if [ "$encryptorType" == "EC" ]; then
280.    oldKey=$(cat keys/tm7.pub)
281.    newKey=$(cat qdata/c${
     numNodes}/tm.pub)
282.    #replace all / with \/ in the newKey (otherwise sed complains about it)
283.    newKey=$(echo $newKey | sed 's/\//\\\//g')
284.    echo "OldKey: $oldKey NewKey: $newKey"
285.    newFileName=qdata/ec-${
     ENCRYPTOR_EC_ELLIPTIC_CURVE:-secp256r1}-private-contract.js
286.    sed  "s/\"$oldKey\"/\"$newKey\"/g" private-contract.js > $newFileName
287.    echo "private-contract sample generated in $newFileName"
288.fi

第34-37行是确定numNode的数字的,默认是7,但是如果qdata文件下的numberOfNodes不是7,则将numberOfNodes中的数字赋值给numNode。这个numNode直接决定后面的一系列配置。
第67-88行是对各个节点端口的配置,默认ip是127.0.0.1,端口从9000依次开始分配。
第90-105是将密钥对复制到qdata文件内,并将一些服务器端口分配,特别serverPortThirdParty,这个是后面的cakeshop需要用到的。
后面都是一些常规操作。Istanbul-init.sh最后一步是./cakeshop-init.sh,,cakeshop-init.sh就是复制一些文件,没得什么看的,而且当时想法就是cakeshop最多影响图形化窗口,对内在联系没得影响。

istanbul-start.sh文件

  1. 查看istanbul-start.sh文件
1.#!/bin/bash
2.set -u
3.set -e
....
52.privacyImpl=tessera
53.tesseraOptions=
54.blockPeriod=5
55.verbosity=3
.....
98.numNodes=7
99.if [[ -f qdata/numberOfNodes ]]; then
100.    numNodes=`cat qdata/numberOfNodes`
fi

if [ "$privacyImpl" == "tessera" ]; then
  echo "[*] Starting Tessera nodes"
  ./tessera-start.sh ${
     tesseraOptions}
elif [ "$privacyImpl" == "constellation" ]; then
  echo "[*] Starting Constellation nodes"
  ./constellation-start.sh
elif [ "$privacyImpl" == "tessera-remote" ]; then
  echo "[*] Starting tessera nodes"
  ./tessera-start-remote.sh ${
     tesseraOptions}
else
113.  echo "Unsupported privacy implementation: ${privacyImpl}"
114.  usage
115.fi

117.echo "[*] Starting ${numNodes} Ethereum nodes with ChainID and NetworkId of $NETWORK_ID"
QUORUM_GETH_ARGS=${
     QUORUM_GETH_ARGS:-}
set -v
ARGS="--nodiscover --verbosity ${verbosity} --istanbul.blockperiod ${blockPeriod} --networkid $NETWORK_ID --syncmode full --mine --minerthreads 1 --rpc --rpccorsdomain=* --rpcvhosts=* --rpcaddr 0.0.0.0 --rpcapi admin,eth,debug,miner,net,shh,txpool,personal,web3,quorum,istanbul,quorumPermission  --unlock 0 --password passwords.txt $QUORUM_GETH_ARGS"

basePort=21000
baseRpcPort=22000
for i in `seq 1 ${
     numNodes}`
do
    port=$(($basePort + ${
     i} - 1))
    rpcPort=$(($baseRpcPort + ${
     i} - 1))
    permissioned=
    if ! [[ -z "${STARTPERMISSION+x}" ]] ; then
        permissioned="--permissioned"
    fi

133.    PRIVATE_CONFIG=qdata/c${
     i}/tm.ipc nohup geth --datadir qdata/dd${
     i} ${
     ARGS} ${
     permissioned} --rpcport ${
     rpcPort} --port ${
     port} 2>>qdata/logs/${
     i}.log &
134.done

set +v

echo
echo "All nodes configured. See 'qdata/logs' for logs, and run e.g. 'geth attach qdata/dd1/geth.ipc' to attach to the first Geth node."
echo "To test sending a private transaction from Node 1 to Node 7, run './runscript.sh private-contract.js'"

exit 0

第52-55行是后面117-134行节点配置的一些默认参数。
第98-115行则是根据不同的管理器,选择不同的.sh文件启动,我选择的是tessera,则会启动tessera-start.sh文件,同时第98-101行又是对numNodes值的确定。
第117-134则是对节点参数的配置,其中rpcport会影响后面cakeshop的参数。

  1. 查看tessera-start.sh文件
1.#!/bin/bash
2.# Start Tessera nodes.
3.# This script will normally start up 7 nodes, however
4.# if file qdata/numberOfNodes exists then the script will read number
5.# of nodes from that file.
......
85.echo Config type $TESSERA_CONFIG_TYPE

87.numNodes=7
88.if [[ -f qdata/numberOfNodes ]]; then
89.    numNodes=`cat qdata/numberOfNodes`
90.fi

92.echo "[*] Starting $numNodes Tessera node(s)"
93.
currentDir=`pwd`
for i in `seq 1 ${
     numNodes}`
do
    DDIR="qdata/c$i"
    mkdir -p ${
     DDIR}
    mkdir -p qdata/logs
    rm -f "$DDIR/tm.ipc"

    DEBUG=""
    if [ "$remoteDebug" == "true" ]; then
      DEBUG="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=500$i -Xdebug"
    fi

    #Only set heap size if not specified on command line
    MEMORY=
    if [[ ! "$jvmParams" =~ "Xm" ]]; then
      MEMORY="-Xms128M -Xmx128M"
    fi

    CMD="java $jvmParams $DEBUG $MEMORY -jar ${tesseraJar} -configfile $DDIR/tessera-config$TESSERA_CONFIG_TYPE$i.json"
    echo "$CMD >> qdata/logs/tessera$i.log 2>&1 &"
    ${
     CMD} >> "qdata/logs/tessera$i.log" 2>&1 &
    sleep 1
done

echo "Waiting until all Tessera nodes are running..."
DOWN=true
k=10
while ${
     DOWN}; do
    sleep 1
    DOWN=false
    for i in `seq 1 ${
     numNodes}`
    do
        if [ ! -S "qdata/c${i}/tm.ipc" ]; then
128.            echo "Node ${i} is not yet listening on tm.ipc"
            DOWN=true
        fi

        set +e
        #NOTE: if using https, change the scheme
        #NOTE: if using the IP whitelist, change the host to an allowed host
        result=$(curl -s http://localhost:900${
     i}/upcheck)
        set -e
        if [ ! "${result}" == "I'm up!" ]; then
            echo "Node ${i} is not yet listening on http"
            DOWN=true
        fi
    done

    k=$((k - 1))
    if [ ${
     k} -le 0 ]; then
        echo "Tessera is taking a long time to start.  Look at the Tessera logs in qdata/logs/ for help diagnosing the problem."
    fi
    echo "Waiting until all Tessera nodes are running..."
148.
149.    sleep 5
150.done

echo "All Tessera nodes started"
exit 0

第87-90行仍然是对numNodes数值的确定,这个数值影响后面的循环次数,而后面都是建立一些系统文件,不需要复制7nodes文件夹内的文件,只与numNodes有关。

至此所以运行文件已经查看完了。基本就与istanbul-init, istanbul-start.sh, tessera-init.sh, tessera-start.sh, istanbul-genesis.json, numberOfNodes这5个文件相关。

思路整理

原始节点过程:
初始化阶段利用istanbul-genesis.json创世块创建区块链,将permissioned-nodes.json文件放进各个文件夹里面,再将已有的密钥对分别复制到各个的文件夹内部,再根据数量对端口进行分配。

启动阶段:对各个节点进行参数配置,不涉及7nodes内部文件的复制。

总结:要向插入新的节点,必须做到三点:(1)利用istanbul-genesis.json创建节点;(2)生成密钥对;(3)修改numberOfNodes。tessera-start.sh阶段(128行)会对tm.ipc文件进行检测,存在则不会再次启动,而istanbul-start.sh会产生tm.ipc文件,因此在加入节点前,不能启动istanbul-start.sh文件。

解决办法

(1)利用istanbul-genesis.json创建节点
在进入7nodes文件夹后,先istanbul-init.sh初始化,创建节点

geth --datadir qdata/dd8 init istanbul-genesis.json

(2)生成密钥对
密钥对需要利用tessera生成,系统自带没得tess,但是vagrant有tessera压缩包,位置在home/vagrant/tessera/tessera.jar。

alias tessera="java -jar /home/vagrant/tessera/tessera.jar" 

将压缩包解压安装并命名为tessera命令,

tessera -keygen -filename tm8

再利用命令生成名字为tm8的命令对,有两个文件,一个公钥,一个私钥,注意,这个命令加地址没用,必须在home/vagrant/quorum-examples/keys中使用这个命令。

(3)修改numberOfNodes
numberOfNodes在home/vagrant/quorum-examples/qdata目录下,

vi qdata/numberOfNodes

将7改成8。
这样再tessera-init.sh重新初始化,再启动就行了。

./tessera-init.sh
./istanbul-start.sh
./cakeshop-start.sh

(4)cakeshop增加节点
通过http://localhost:8999/进入cakeshop,在add node中,geth rpc填写22007,tessera 3rd party填写9088,即可将新加入的节点写入。再通过不同节点的peers增加节点,即可接入联盟链中,在新节点中,区块与其余7点一致。

总结

虽然很简单,但是花了两三天时间才搞好。这个东西比较偏,网上查找的资料也解决不了,写下来,给和我一样的初学者一点解决思路。
本文解决方法基于vagrant 2020.03.18 ubuntu版本,直接在虚拟机内部操作应该也可以,我自己更新到ubuntu 18.04.4也是可行的,quorum用的也是2020.03.30在Github上JP摩根/quorum-examples的数据。

你可能感兴趣的:(区块链)