转载:以太坊联盟链quorum搭建(一)

作者: 万年小学生 | 来源:发表于2018-08-16 11:13 被阅读174次

    原文链接:https://blog.csdn.net/zhj_fly/article/details/80560744

    关于quorum的介绍可以看一些这几篇文章:

    https://blog.csdn.net/about_blockchain/article/details/78684901

    https://blog.csdn.net/about_blockchain/article/details/78750686

    https://blog.csdn.net/about_blockchain/article/details/78814873

    本文参考官网教程:https://github.com/jpmorganchase/quorum-examples/tree/master/examples/7nodes

    官网教程中创建了7个节点,由这7个节点来模拟联盟链网络。

    作为联盟链,节点接入是有权限要求的,通过permissioned-nodes.json文件和permissioned参数来控制哪些节点可以和本节点建立连接,后面会详解。对原来教程中有些比较省略,本文会将一些省略的地方介绍。

    一、下载资源

    1、首先安装quorum

    git clone --recursive https://github.com/jpmorganchase/quorum.git

    cd quorum

    make all

    编译完后,quorum/build/bin目录下会生成如下文件列表

    abigen bootnode evm examples faucet geth p2psim puppeth rlpdump swarm wnode

    将上面这些加入系统path中。

    2、获取costellation-node可执行文件

    https://github.com/jpmorganchase/constellation/releases

    下载下来后解压,得到constellation-node,加入系统PATH中。

    3、下载教程样例:

    git clone https://github.com/jpmorganchase/quorum-examples

    二、搭建raft共识的联盟链

    因为联盟间一般是存在信任关系,因此可以使用raft共识算法,这个算法可以容忍节点挂掉,但不能容忍拜占庭错误,即不能存在恶意节点。这样就不用以太坊中的pow算法了,节省cup、内存,而且出块时间块。另外,这个算法中不会存在空块,节省大量空间,如果没有交易,那么生成区块就暂停。

    1、初始化,创建7个节点,其中1-4号节点是permissioned节点,即这些节点只允许和permissioned-nodes.json中的节点建立连接,其他节点无法连接到这个节点。初始化的脚本如下:

    raft-init.sh

    #!/bin/bash

    set -u

    set -e

    echo"[*] Cleaning up temporary data directories"

    rm -rf qdata

    mkdir -p qdata/logs

    echo"[*] Configuring node 1 (permissioned)"

    mkdir -p qdata/dd1/{keystore,geth}

    cp permissioned-nodes.json qdata/dd1/static-nodes.json

    cp permissioned-nodes.json qdata/dd1/

    cp keys/key1 qdata/dd1/keystore

    cp raft/nodekey1 qdata/dd1/geth/nodekey

    geth --datadir qdata/dd1 init genesis.json

    echo"[*] Configuring node 2 (permissioned)"

    mkdir -p qdata/dd2/{keystore,geth}

    cp permissioned-nodes.json qdata/dd2/static-nodes.json

    cp permissioned-nodes.json qdata/dd2/

    cp keys/key2 qdata/dd2/keystore

    cp raft/nodekey2 qdata/dd2/geth/nodekey

    geth --datadir qdata/dd2 init genesis.json

    echo"[*] Configuring node 3 (permissioned)"

    mkdir -p qdata/dd3/{keystore,geth}

    cp permissioned-nodes.json qdata/dd3/static-nodes.json

    cp permissioned-nodes.json qdata/dd3/

    cp keys/key6 qdata/dd3/keystore

    cp keys/key3 qdata/dd3/keystore

    cp raft/nodekey3 qdata/dd3/geth/nodekey

    geth --datadir qdata/dd3 init genesis.json

    echo"[*] Configuring node 4 (permissioned)"

    mkdir -p qdata/dd4/{keystore,geth}

    cp permissioned-nodes.json qdata/dd4/static-nodes.json

    cp permissioned-nodes.json qdata/dd4/

    cp keys/key4 qdata/dd4/keystore

    cp raft/nodekey4 qdata/dd4/geth/nodekey

    geth --datadir qdata/dd4 init genesis.json

    echo"[*] Configuring node 5"

    mkdir -p qdata/dd5/{keystore,geth}

    cp permissioned-nodes.json qdata/dd5/static-nodes.json

    cp keys/key5 qdata/dd5/keystore

    cp raft/nodekey5 qdata/dd5/geth/nodekey

    geth --datadir qdata/dd5 init genesis.json

    echo"[*] Configuring node 6"

    mkdir -p qdata/dd6/{keystore,geth}

    cp permissioned-nodes.json qdata/dd6/static-nodes.json

    cp raft/nodekey6 qdata/dd6/geth/nodekey

    cp keys/key7 qdata/dd6/keystore

    geth --datadir qdata/dd6 init genesis.json

    echo"[*] Configuring node 7"

    mkdir -p qdata/dd7/{keystore,geth}

    cp permissioned-nodes.json qdata/dd7/static-nodes.json

    cp raft/nodekey7 qdata/dd7/geth/nodekey

    cp keys/key8 qdata/dd7/keystore

    geth --datadir qdata/dd7 init genesis.json

    以第一个节点的配置为例解释命令:

    第一行:创建节点1的目录

    第二行:permissioned-nodes.json中存了已经设置好的enode值,即节点的地址,通过这个值,运行geth客户端时可以将不同的节点连接起来。与普通以太坊的geth客户端不同,这里需要一个static-nodes,应该是指定和哪些节点建立连接吧。如果启动geth时不指定--permissioned,启动后本节点就会寻找static-node里面的节点建立连接。

    另外,这个enode的值是与nodekey相关的,就是第五行中的nodekey文件,两者应该是一一对应的。所以第五行中将nodekey复制到这个目录中,就相当于把一个节点的唯一标识复制过去。nodekey的生成方法:运行bootnode -genkey nodekey即可生成nodekey文件,再运行bootnode -nodekey nodekey就会输出本节点的enode信息,记录下来即可。

    第三行:将permissioned-node.json复制到节点1的目录下,这里面记录了哪些节点可以与本节点建立连接,不在这个列表里的节点是不能和本节点连接的。

    第四行:将一个账户文件复制到节点1下的目录,启动geth后,用eth.accounts查账户,就会显示这个文件中的账户地址。

    第五行:将nodekey复制到节点1目录下,它作用如上面所述。

    第六行:根据创世区块信息genesis.json初始化区块链。

    2、启动constellation和geth节点

    (1)constellation:可以将其看成是分布式密钥服务器、PGP加密和邮件传输代理的合并,用它来实现quorum的私有机制。

    使用方法:生成密钥constellation-node --generatekeys node,就会生成node.pub和node.key两个密钥文件,分别是公私钥文件。如果在生成是使用密码加密,那么在启动contellation-node时是需要输入密码的,为了方便,下面的方法中不输入密码。

    下面来看启动节点的constellation-start.sh脚本:

    #!/bin/bash

    set -u

    set -e

    foriin{1..7}

    do

    DDIR="qdata/c$i"

        mkdir -p $DDIR

        mkdir -p qdata/logs

    cp"keys/tm$i.pub""$DDIR/tm.pub"

    cp"keys/tm$i.key""$DDIR/tm.key"

    rm -f"$DDIR/tm.ipc"

    CMD="constellation-node --url=https://127.0.0.$i:900$i/ --port=900$i --workdir=$DDIR --socket=tm.ipc --publickeys=tm.pub --privatekeys=tm.key --othernodes=https://127.0.0.1:9001/"

    echo"$CMD >> qdata/logs/constellation$i.log 2>&1 &"

    $CMD >>"qdata/logs/constellation$i.log"2>&1&

    done

    DOWN=true

    while$DOWN;do

    sleep0.1

    DOWN=false

    foriin{1..7}

    do

    if[ ! -S"qdata/c$i/tm.ipc"]; then

    DOWN=true

    fi

        done

    done

    其中,

    第五行是代表一共启动7个节点,需要在不同的节点目录

    第7-9行:建立节点目录

    第10-11行:将之前生成的公私钥对放到对应几点目录中

    第12行:删除之前存在的旧的 .ipc文件(如果存在的话)

    第13行:启动constellation-node节点,具体参数参考https://github.com/jpmorganchase/constellation/blob/master/sample.conf

    第15行:将控制台的输出定向到指定文件里,如果启动错误,可以去对应文件查看错误内容

    第18-28行:等待所有constellation-node启动完毕,其中24行 -S 是查看对应文件是否存在。

    可以运行./constellation-start.sh来启动测试,启动完毕后输入ps -ef | grep constellation-node可以查看进程情况,输入killall constellation-node 来终止程序运行。

    (2)下面来启动7个geth节点

    raft-start.sh

    #!/bin/bash

    set -u

    set -e

    mkdir -p qdata/logs

    echo"[*] Starting Constellation nodes"

    ./constellation-start.sh

    echo"[*] Starting Ethereum nodes"

    set -v

    ARGS="--nodiscover --raft --rpc --rpcaddr 0.0.0.0 --rpcapi admin,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum --emitcheckpoints"

    PRIVATE_CONFIG=qdata/c1/tm.ipc nohup geth --datadir qdata/dd1 $ARGS --permissioned --raftport50401--rpcport22000--port21000--unlock0--password passwords.txt2>>qdata/logs/1.log &

    PRIVATE_CONFIG=qdata/c2/tm.ipc nohup geth --datadir qdata/dd2 $ARGS --permissioned --raftport50402--rpcport22001--port21001--unlock0--password passwords.txt2>>qdata/logs/2.log &

    PRIVATE_CONFIG=qdata/c3/tm.ipc nohup geth --datadir qdata/dd3 $ARGS --permissioned --raftport50403--rpcport22002--port21002--unlock0--password passwords.txt2>>qdata/logs/3.log &

    PRIVATE_CONFIG=qdata/c4/tm.ipc nohup geth --datadir qdata/dd4 $ARGS --permissioned --raftport50404--rpcport22003--port21003--unlock0--password passwords.txt2>>qdata/logs/4.log &

    PRIVATE_CONFIG=qdata/c5/tm.ipc nohup geth --datadir qdata/dd5 $ARGS --raftport50405--rpcport22004--port21004--unlock0--password passwords.txt2>>qdata/logs/5.log &

    PRIVATE_CONFIG=qdata/c6/tm.ipc nohup geth --datadir qdata/dd6 $ARGS --raftport50406--rpcport22005--port21005--unlock0--password passwords.txt2>>qdata/logs/6.log &

    PRIVATE_CONFIG=qdata/c7/tm.ipc nohup geth --datadir qdata/dd7 $ARGS --raftport50407--rpcport22006--port21006--unlock0--password passwords.txt2>>qdata/logs/7.log &

    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'"

    其中

    第7行:调用上面写的constellation-start.sh脚本,启动7个constellation

    第11行:启动geth节点需要的参数,这里需要制定--raft来使用raft共识方法

    第12-18行,启动7个geth节点,需要指定raftport端口号,用 --permissioned参数来指定那些节点是有连接权限的。

    这样,constellation-node和geth节点都启动起来了,也就是环境已经搭建好了,可以在上面开发了。开发之前先做一下测试。

    3、测试环境

    zhj@zhj-HP:~/project/test_chain/quorum/quorum-examples/examples/7nodes$ ps -ef | grep constellation-node

    zhj171502994409:23pts/300:00:03constellation-node --url=https://127.0.0.1:9001/ --port=9001 --workdir=qdata/c1 --socket=tm.ipc --publickeys=tm.pub --privatekeys=tm.key --othernodes=https://127.0.0.1:9001/

    zhj171742994309:23pts/300:00:02constellation-node --url=https://127.0.0.2:9002/ --port=9002 --workdir=qdata/c2 --socket=tm.ipc --publickeys=tm.pub --privatekeys=tm.key --othernodes=https://127.0.0.1:9001/

    zhj172002994309:23pts/300:00:02constellation-node --url=https://127.0.0.3:9003/ --port=9003 --workdir=qdata/c3 --socket=tm.ipc --publickeys=tm.pub --privatekeys=tm.key --othernodes=https://127.0.0.1:9001/

    zhj172182994309:23pts/300:00:03constellation-node --url=https://127.0.0.4:9004/ --port=9004 --workdir=qdata/c4 --socket=tm.ipc --publickeys=tm.pub --privatekeys=tm.key --othernodes=https://127.0.0.1:9001/

    zhj172262994309:23pts/300:00:03constellation-node --url=https://127.0.0.5:9005/ --port=9005 --workdir=qdata/c5 --socket=tm.ipc --publickeys=tm.pub --privatekeys=tm.key --othernodes=https://127.0.0.1:9001/

    zhj172652994409:23pts/300:00:03constellation-node --url=https://127.0.0.6:9006/ --port=9006 --workdir=qdata/c6 --socket=tm.ipc --publickeys=tm.pub --privatekeys=tm.key --othernodes=https://127.0.0.1:9001/

    zhj172822994309:23pts/300:00:03constellation-node --url=https://127.0.0.7:9007/ --port=9007 --workdir=qdata/c7 --socket=tm.ipc --publickeys=tm.pub --privatekeys=tm.key --othernodes=https://127.0.0.1:9001/

    zhj1769011030009:24pts/600:00:00grep --color=auto constellation-node

    zhj@zhj-HP:~/project/test_chain/quorum/quorum-examples/examples/7nodes$ ps -ef |grep geth

    zhj173542994109:23pts/300:00:05geth --datadir qdata/dd1 --nodiscover --raft --rpc --rpcaddr0.0.0.0--rpcapi admin,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum --emitcheckpoints --permissioned --raftport50401--rpcport22000--port21000--unlock0--password passwords.txt

    zhj173552994109:23pts/300:00:05geth --datadir qdata/dd2 --nodiscover --raft --rpc --rpcaddr0.0.0.0--rpcapi admin,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum --emitcheckpoints --permissioned --raftport50402--rpcport22001--port21001--unlock0--password passwords.txt

    zhj173562994209:23pts/300:00:08geth --datadir qdata/dd3 --nodiscover --raft --rpc --rpcaddr0.0.0.0--rpcapi admin,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum --emitcheckpoints --permissioned --raftport50403--rpcport22002--port21002--unlock0--password passwords.txt

    zhj173572994109:23pts/300:00:05geth --datadir qdata/dd4 --nodiscover --raft --rpc --rpcaddr0.0.0.0--rpcapi admin,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum --emitcheckpoints --permissioned --raftport50404--rpcport22003--port21003--unlock0--password passwords.txt

    zhj173582994109:23pts/300:00:05geth --datadir qdata/dd5 --nodiscover --raft --rpc --rpcaddr0.0.0.0--rpcapi admin,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum --emitcheckpoints --raftport50405--rpcport22004--port21004--unlock0--password passwords.txt

    zhj173592994109:23pts/300:00:04geth --datadir qdata/dd6 --nodiscover --raft --rpc --rpcaddr0.0.0.0--rpcapi admin,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum --emitcheckpoints --raftport50406--rpcport22005--port21005--unlock0--password passwords.txt

    zhj173602994109:23pts/300:00:04geth --datadir qdata/dd7 --nodiscover --raft --rpc --rpcaddr0.0.0.0--rpcapi admin,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum --emitcheckpoints --raftport50407--rpcport22006--port21006--unlock0--password passwords.txt

    zhj1794611030009:29pts/600:00:00grep --color=auto geth

    说明节点都已正常运行,下面连接第一个节点的geth控制台:

    输入geth attach qdata/dd1/geth.ipc,查看基本信息:

    zhj@zhj-HP:~/project/test_chain/quorum/quorum-examples/examples/7nodes$ geth attach qdata/dd1/geth.ipc

    Welcome to the Geth JavaScriptconsole!

    instance: Geth/v1.7.2-stable-fd0e3b9d/linux-amd64/go1.10.1

    coinbase:0xed9d02e382b34818e88b88a309c7fe71e65f419d

    at block:0(Thu,01Jan197008:00:00CST)

    datadir:/home/zhj/project/test_chain/quorum/quorum-examples/examples/7nodes/qdata/dd1

    modules: admin:1.0debug:1.0eth:1.0miner:1.0net:1.0personal:1.0raft:1.0rpc:1.0txpool:1.0web3:1.0

    > eth.accounts

    ["0xed9d02e382b34818e88b88a309c7fe71e65f419d"]

    > acc0 = eth.accounts[0]

    "0xed9d02e382b34818e88b88a309c7fe71e65f419d"

    > eth.getBalance(acc0)

    1e+27

    > eth.blockNumber

    0

    可以看到有一个账户地址,就是复制进去的账户,里面有余额(在创世文件里设置的),现在的区块数目是0,现在没有交易,是不产生区块的,下面进行发送一笔交易。

    连接第7个节点的geth控制台,查看账户和余额:

    zhj@zhj-HP:~/project/test_chain/quorum/quorum-examples/examples/7nodes$ geth attach qdata/dd7/geth.ipc

    Welcome to the Geth JavaScriptconsole!

    instance: Geth/v1.7.2-stable-fd0e3b9d/linux-amd64/go1.10.1

    coinbase:0xa9e871f88cbeb870d32d88e4221dcfbd36dd635a

    at block:0(Thu,01Jan197008:00:00CST)

    datadir:/home/zhj/project/test_chain/quorum/quorum-examples/examples/7nodes/qdata/dd7

    modules: admin:1.0debug:1.0eth:1.0miner:1.0net:1.0personal:1.0raft:1.0rpc:1.0txpool:1.0web3:1.0

    > eth.accounts

    ["0xa9e871f88cbeb870d32d88e4221dcfbd36dd635a"]

    > acc0 =eth.accounts[0]

    "0xa9e871f88cbeb870d32d88e4221dcfbd36dd635a"

    > eth.getBalance(acc0)

    0

    然后从节点1的账户发送一笔钱到节点7的账户:

    > eth.sendTransaction({from:acc0,to:"0xa9e871f88cbeb870d32d88e4221dcfbd36dd635a",value:100000})

    "0xa0da7ef1632361e7b4d37f5258598bce0bbb074935c613c7e565a2c93964807f"

    这时查看账户7的余额:

    > eth.getBalance(acc0)

    100000

    再查看区块数目:

    > eth.blockNumber

    1

    可以看到因为这笔交易已经生成了一个区块。

    如果要发送一笔私有交易,可以用privateFor来指定,例如节点1账户发送一笔私有交易给节点2的账户:

    >eth.sendTransaction({from: acc0,to: acc1,value:10000,privateFor:["QfeDAys9MPDs2XHExtc84jKGHxZg/aj52DTh0vtA3Xc="]})

    "0x241c0d71035cce2891e82a883d010bbf0151e79fc3aace8966a626cf0598e7ff"

    其中acc0是节点1的账户,acc1是节点2的账户,privateFor后面的地址是tm2.pub中的内容,返回的交易的地址,下面查看这笔交易:

    > eth.getTransaction("0x241c0d71035cce2891e82a883d010bbf0151e79fc3aace8966a626cf0598e7ff")

    {

    blockHash:"0x6e74d807e1951e168489e54b23dd39f367f62312b1463a160c8730ad360f48bf",

    blockNumber:1,

    from:"0xed9d02e382b34818e88b88a309c7fe71e65f419d",

    gas:90000,

    gasPrice:18000000000,

    hash:"0x241c0d71035cce2891e82a883d010bbf0151e79fc3aace8966a626cf0598e7ff",

    input:"0x33e358398b2bc693db176718211e9a80b2d2cf58df79e7a760c9b68baa2f41d95a5bec625b3f3eb3b0bb996afb6692e8187164fed745ebd726abd74455895d90",

    nonce:0,

    r:"0xfa734439ccc3f8d54f22a012703d951a0891c2914b1361967e3f755466f778b6",

    s:"0x79ba2235b3d049e3132a32e4bac3d6023f3c460875d098a61c6c829d3c65af7f",

    to:"0xca843569e3427144cead5e4d5999a3d0ccf92b8e",

    transactionIndex:0,

    v:"0x25",

    value:100000

    }

    其中倒数第二行的v字段值是0x25,即37。当值为37、 38时代表是一笔私有交易。

    (7.13补充:我又试了一下,现在quorum还不支持私有交易发送以太币,上面这笔私有交易可以成功发送并被记录在区块里,但是查一下双方的余额时,发现都没有改变。github上开发者是这么说的:链接。但是私有合约可以正常执行,下一篇文章中有具体介绍)

    本篇到此结束,下一篇文章介绍在quorum上部署和执行智能合约。

    相关文章

      网友评论

        本文标题:转载:以太坊联盟链quorum搭建(一)

        本文链接:https://www.haomeiwen.com/subject/akpnbftx.html