美文网首页程序员
Quorum手动搭建指南

Quorum手动搭建指南

作者: Tyler_1c10 | 来源:发表于2019-04-27 16:30 被阅读3次

    最近在研究Quorum, 虽然Quorum官方有7nodesample可以快速创建一个quorum区块链网络,但它隐藏了众多配置细节和步骤,目的是为了新手快速体验其功能。所以如果希望深入了解其机理并进行定制的话,手动搭建必不可少,以下将详细展示搭建步骤。

    Quorum是基于以太坊的,最突出的改进在于支持IBFT(Istanbul Byzantine Fault Tolerant)共识协议以及支持私密交易(private transaction),本教程将搭建支持IBFT和私密交易的拥有三个node和三个transaction manager的Quorum区块链网络。

    一、规划目录结构及端口

    Quorum架构图

    根据Quorum的架构,我们创建三个node和三个transaction manager的网络,每个node和每个transaction manager都需要有自己的data目录,因此我们的目录结构规划如下:

    /opt/dev/testnet/data   --数据文件根目录
    |_node1                      
    |     |_dd      --node1数据目录
    |     |  |_geth
    |     |  |_keystore
    |     |_tm      --transaction manager1数据目录
    |_node2
    |     |_dd      --node2数据目录
    |     |  |_geth
    |     |  |_keystore
    |     |_tm      --transaction manager2数据目录
    |_node3
          |_dd      --node3数据目录
          |  |_geth
          |  |_keystore
          |_tm      --transaction manager3数据目录
    

    创建脚本:

    mkdir -p /opt/dev/quorum/testnet/data 
    cd /opt/dev/quorum/testnet/data
    mkdir -p node1/dd/geth/
    mkdir -p node1/dd/keystore/
    mkdir -p node1/tm
    mkdir -p node2/dd/geth/
    mkdir -p node2/dd/keystore/
    mkdir -p node2/tm
    mkdir -p node3/dd/geth/
    mkdir -p node3/dd/keystore/
    mkdir -p node3/tm
    

    端口的规划如下

    #node端口
    32001~32003       --rpc服务端口
    31001~31003       --node间peer通信端口
    50401~50403       --raft协议端口
    
    #transaction manager 端口
    9101~9104         --tx manager间通信端口
    9181~9184         --服务端口
    

    二、创建账户Account

    不同于Hyperledger Fabric拥有MSP组件可以创建管理组织以及证书,以太坊是完全基于公链设计的,并不具备这种PKI的集中管理的机制,Quorum对此并无任何改造和增强,因此account的生成完全是可以离线操作的,而账户的本质其实就是一个椭圆曲线的私钥。本文之所以要预先准备账户是为了在之后的node console中方便的使用web3的api, 因为有些api是要求节点中配置账户信息的。

    本文使用的Quorum版本是V2.2.3
    ),请自行下载解压出geth可执行文件,创建账户的命令如下, 提示输入密码的时候可以直接回车不进行设置:

    \>geth --datadir=/opt/dev/quorum/testnet/data/node1/dd/ account new
    \>geth --datadir=/opt/dev/quorum/testnet/data/node2/dd/ account new
    \>geth --datadir=/opt/dev/quorum/testnet/data/node3/dd/ account new
    

    在datadir路径keystore下会生成用户私钥文件,打开其中一个查看:

    \>ll /opt/dev/quorum/testnet/data/node1/dd/keystore
    -rw------- 1 root root  491 Apr 24 16:19 UTC--2019-04-24T08-19-29.095420057Z--9affedff10f7229c680819d5eeb12c3624f6baeb
    
    \>more /opt/dev/quorum/testnet/data/node1/dd/keystore/UTC--2019-04-24T08-19-29.095420057Z--9affedff10f7229c680819d5eeb12c3624f6baeb
    {"address":"9affedff10f7229c680819d5eeb12c3624f6baeb","crypto":{"cipher":"aes-128-ctr","ciphertext":"18e7b1d213c913524e0ba16025e3bd99bcff2e9dd572874b2ffb4d55bc4cb2dd"
    ,"cipherparams":{"iv":"b8f2a0e7347f47d21c8181d49f639a67"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"c0933ccaa243e91789696127f0bc9c4268aed0
    5a0dc2c8da8cc053faac516b28"},"mac":"119ce5ab369c2dc5e1ee07f814ebd6d83f9eeb04a94336e8da873edb9d904ae8"},"id":"40458a80-fb2e-47cd-84ee-b8d42c37ca1b","version":3}
    

    json中的address即为账户地址:9affedff10f7229c680819d5eeb12c3624f6baeb.刚才的命令生成的三个账户地址如下:

    账户1: 9affedff10f7229c680819d5eeb12c3624f6baeb
    账户2: 5c8822ab6af840f8c3e52fe9a71c43f90672728e
    账户3: 915779c113cffecac6583310d80a8def09546272
    

    三、创建nodekey

    nodekey是node的唯一标识,需要配置在node数据文件路径中,以太坊nodekey生成的标准命令是使用bootnode --genkey=nodekey命令,但若使用IBFT共识的话,对nodekey是有严格要求的, validator id是根据nodekey生成的,所以如果选用IBFT共识我们就需要使用istanbul-tool来生成nodekey。istanbul-tool需要从git
    )上clone,自行构建出istanbul命令,然后在一个临时目录/opt/dev/quorum/testnet/tmp中执行:

    \>mkdir -p /opt/dev/quorum/testnet/tmp/
    \>cd /opt/dev/quorum/testnet/tmp/
    
    \>istanbul setup --num 3 --nodes --quorum --save --verbose
    
    \>ll
    drwxr-xr-x 2 root root 4096 Apr 25 16:22 0/
    drwxr-xr-x 2 root root 4096 Apr 25 16:22 1/
    drwxr-xr-x 2 root root 4096 Apr 25 16:22 2/
    -rwxr-xr-x 1 root root 1524 Apr 25 16:22 genesis.json*
    -rwxr-xr-x 1 root root  500 Apr 25 16:22 static-nodes.json*
    

    根据我们的参数--num 3,命令将创建3个nodekey, 打开其中一个可以看到:

    \>more 0/nodekey
    c3828ef20a55925c60587f63359798249fb8c20992ef68ce9d0ff10abfd8c858
    

    手动将三个nodekey文件分别拷贝到相应node的数据路径

    cp 0/nodekey /opt/dev/quorum/testnet/data/node1/dd/geth/nodekey
    cp 1/nodekey /opt/dev/quorum/testnet/data/node2/dd/geth/nodekey
    cp 2/nodekey /opt/dev/quorum/testnet/data/node3/dd/geth/nodekey
    

    四、static-nodes.json, permissioned-nodes.json文件

    Quorum 联盟链的node是有准入限制的,体现在permissioned-nodes.json文件中,permissioned-nodes.json同时可以用作静态节点配置,所以在本例中static-nodes.json, permissioned-nodes.json两个文件是相同的:
    首先打开我们使用istanbul命令创建的static-nodes.json文件

    \>vim /opt/dev/quorum/testnet/tmp/static-nodes.json
    

    根据我们之前的端口规划,修改端口如下:

    [
    "enode://f3c5520a8bea82dcbf28412e61c0f225fc8c5dbd9e729529b91d3755def5583b761bf94d11c434659dd4f8ffba696a1258cb8a8ce8e5ba25c2d3f25a965375a2@127.0.0.1:31001?discport=0&raftport=50401",
    "enode://926c705394776e3fe25d79f9e290444a424ffcd3855fb212af049abb80d658a59f3f5843876d587f33419e262b96d60a14c914c6076312e7e9af8c93e36f9c3b@127.0.0.1:31002?discport=0&raftport=50402",
    "enode://8a6690de0099da4fbba4eb741f875b15b1adc823149d85302cddeaf5c0c77e504b78fe23e93ce199fdb1c0959954c95031d0959c861fb2ce6ecfac50a49768d0@127.0.0.1:31003?discport=0&raftport=50403"
    ]
    

    然后将文件配置到相应的node路径中:

    cp /opt/dev/quorum/testnet/tmp/static-nodes.json  /opt/dev/quorum/testnet/data/node1/dd/static-nodes.json
    cp /opt/dev/quorum/testnet/tmp/static-nodes.json  /opt/dev/quorum/testnet/data/node1/dd/permissioned-nodes.json
    cp /opt/dev/quorum/testnet/tmp/static-nodes.json  /opt/dev/quorum/testnet/data/node2/dd/static-nodes.json
    cp /opt/dev/quorum/testnet/tmp/static-nodes.json  /opt/dev/quorum/testnet/data/node2/dd/permissioned-nodes.json
    cp /opt/dev/quorum/testnet/tmp/static-nodes.json  /opt/dev/quorum/testnet/data/node3/dd/static-nodes.json
    cp /opt/dev/quorum/testnet/tmp/static-nodes.json  /opt/dev/quorum/testnet/data/node3/dd/permissioned-nodes.json
    

    五、创建创世文件genesis.json并初始化区块链

    可以使用之前istanbul命令生成的genesis.json

    \>vim /opt/dev/quorum/testnet/tmp/genesis.json
    

    内容修改如下:

    {
        "alloc": {
          "0x9affedff10f7229c680819d5eeb12c3624f6baeb": {
            "balance": "1000000000000000000000000000"
          },
          "0x5c8822ab6af840f8c3e52fe9a71c43f90672728e": {
            "balance": "1000000000000000000000000000"
          },
          "0x915779c113cffecac6583310d80a8def09546272": {
            "balance": "1000000000000000000000000000"
          }
        },
        "coinbase": "0x0000000000000000000000000000000000000000",
        "config": {
          "homesteadBlock": 0,
          "byzantiumBlock": 0,
          "chainId": 10,
          "eip150Block": 0,
          "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
          "eip155Block": 0,
          "eip158Block": 0,
          "isQuorum": true,
          "istanbul": {
            "epoch": 30000,
            "policy": 0
          }
        },
        "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000f885f83f9490ccaba53ed0c2979d4659692ca3b0ecc385fd7094a02f4bb093989222608d25c6c57a5b40526f679694f0fa966e6efa633080d1603e7daea5176de87d82b8410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0",
        "gasLimit": "0xE0000000",
        "difficulty": "0x1",
        "mixHash": "0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365",
        "nonce": "0x0",
        "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
        "timestamp": "0x00"
    }
    

    解释一下修改了哪些地方,其实就是把alloc部分的account换成了我们刚才生成的account, 并修改了我们希望的预置balance余额,对于quorum来说balance没什么用处,初始化一个较大的值即可。istanbul命令已经帮我们配置了"isQuorum"以及"istanbul"的属性,不必我们再去设置:

    {
          "isQuorum": true,
          "istanbul": {
            "epoch": 30000,
            "policy": 0
          }
    }
    

    另外着重说一下"extraData"这个属性:

    {
    "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000f885f83f9490ccaba53ed0c2979d4659692ca3b0ecc385fd7094a02f4bb093989222608d25c6c57a5b40526f679694f0fa966e6efa633080d1603e7daea5176de87d82b8410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0"
    }
    

    这个属性的值是istanbul工具将合法的validator编码而成的二进制字段,我们可以使用istanbul命令来解码看一下:

    \>istanbul extra decode --extradata 0x0000000000000000000000000000000000000000000000000000000000000000f885f83f9490ccaba53ed0c2979d4659692ca3b0ecc385fd7094a02f4bb093989222608d25c6c57a5b40526f679694f0fa966e6efa633080d1603e7daea5176de87d82b8410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0
    vanity:  0x0000000000000000000000000000000000000000000000000000000000000000
    validator:  0x90ccABA53ed0C2979D4659692CA3B0EcC385FD70
    validator:  0xA02f4bB093989222608D25C6C57a5B40526F6796
    validator:  0xF0FA966E6EfA633080d1603E7dAea5176De87d82
    seal: 0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
    

    可以看到解码出来三个validator,这里需要特别注意的是,这三个validator并不是account,而是从三个nodekey生成的id,也就是说三个node在进行共识投票的时候,跟node上配置的account是无关的。很多人把这个validator理解成了account,将account id用istanbul工具编码后配置到了genesis.json的extraData字段中,这样做的话,实际的共识发起者是根据nodekey生成的validator, 区块打包共识的时候发现发起者并不在extraData编码的validators列表中,则为非法发起者,导致无法成功达成共识。

    然后使用genesis.json初始化三个node节点

    \>geth --datadir /opt/dev/quorum/testnet/data/node1/dd init /opt/dev/quorum/testnet/tmp/genesis.json
    \>geth --datadir /opt/dev/quorum/testnet/data/node2/dd init /opt/dev/quorum/testnet/tmp/genesis.json
    \>geth --datadir /opt/dev/quorum/testnet/data/node3/dd init /opt/dev/quorum/testnet/tmp/genesis.json
    

    六、配置transaction manager

    1、创建秘钥对

    首先下载或自行编译, 图省事可以docker pull quorumengineering/tessera:0.9, 然后启动一个容器,从容器的/tessera/tessera-app.jar 位置拷贝到宿主机/opt/dev/quorum/testnet/tmp/tessera-app-0.9.jar,然后执行如下命令,遇到弹出输入密码时同样可以直接回车:

    \>cd /opt/dev/quorum/testnet/tmp
    
    \>alias tessera="java -jar /opt/dev/quorum/testnet/tmp/tessera-app-0.9.jar"
    
    \>tessera -keygen -filename 1
    \>tessera -keygen -filename 2
    \>tessera -keygen -filename 3
    
    \>ll
    -rw-r--r-- 1 root root  109 Apr 25 11:38 1.key
    -rw-r--r-- 1 root root   44 Apr 25 11:38 1.pub
    -rw-r--r-- 1 root root  109 Apr 25 11:38 2.key
    -rw-r--r-- 1 root root   44 Apr 25 11:38 2.pub
    -rw-r--r-- 1 root root  109 Apr 25 11:38 3.key
    -rw-r--r-- 1 root root   44 Apr 25 11:38 3.pub
    

    会生成三对秘钥对:
    1.key

    {
       "type" : "unlocked",
       "data" : {
          "bytes" : "oBQw7B/TivaynIT9SQTx5Ni1jNV1M5s/J6+1r7KlCJ8="
       }
    }
    

    1.pub

    NPaOkPjlF3WFgA1WaqtANE0tqX/M8Rdr1h4SzQX0ghQ=
    

    2.key

    {
       "type" : "unlocked",
       "data" : {
          "bytes" : "/FAEF3msNOcWNkLkzUdSdNLFvFSJgddjwV2HOWTV/Rk="
       }
    }
    

    2.pub

    T8olcFvm2JojQd616k1MIx/Gm2IEZPkyV4GutVvrPgM=
    

    3.key

    {
       "type" : "unlocked",
       "data" : {
          "bytes" : "ZR00hvY4nCiG8sWQFasKvwGtOBi0b2oxNriVdMN++MY="
       }
    }
    

    3.pub

    Uc952L7QFuk8R5sGtjpfHb9EM+X6pTFRixVa+XgPBjc=
    

    创建三个tessera.json配置文件,将下面文件中的keyData改为你自己的:
    tessera-1.json

        {
          "useWhiteList": false,
          "jdbc": {
            "username": "sa",
            "password": "",
            "url": "jdbc:h2:/opt/dev/quorum/testnet/data/node1/tm/db;MODE=Oracle;TRACE_LEVEL_SYSTEM_OUT=0",
            "autoCreateTables": true
          },
          "serverConfigs":[
          {
            "app":"ThirdParty",
            "enabled": true,
            "serverAddress": "http://127.0.0.1:9181",
            "communicationType" : "REST"
          },
          {
            "app":"Q2T",
            "enabled": true,
            "serverAddress": "unix:/opt/dev/quorum/testnet/data/node1/tm/tm.ipc",
            "communicationType" : "REST"
          },
          {
            "app":"P2P",
            "enabled": true,
            "serverAddress": "http://127.0.0.1:9101",
            "sslConfig": {
              "tls": "OFF",
              "generateKeyStoreIfNotExisted": true,
              "serverKeyStore": "/opt/dev/quorum/testnet/data/node1/tm/server-keystore",
              "serverKeyStorePassword": "quorum",
              "serverTrustStore": "/opt/dev/quorum/testnet/data/node1/tm/server-truststore",
              "serverTrustStorePassword": "quorum",
              "serverTrustMode": "TOFU",
              "knownClientsFile": "/opt/dev/quorum/testnet/data/node1/tm/knownClients",
              "clientKeyStore": "/opt/dev/quorum/testnet/data/node1/tm/client-keystore",
              "clientKeyStorePassword": "quorum",
              "clientTrustStore": "/opt/dev/quorum/testnet/data/node1/tm/client-truststore",
              "clientTrustStorePassword": "quorum",
              "clientTrustMode": "TOFU",
              "knownServersFile": "/opt/dev/quorum/testnet/data/node1/tm/knownServers"
            },
            "communicationType" : "REST"
          }
          ],
          "peer": [
             {
                 "url": "http://127.0.0.1:9101"
             },
             {
                 "url": "http://127.0.0.1:9102"
             },
             {
                 "url": "http://127.0.0.1:9103"
             }
          ],
          "keys": {
            "passwords": [],
            "keyData": [
              {
                "config": {"data":{"bytes":"oBQw7B/TivaynIT9SQTx5Ni1jNV1M5s/J6+1r7KlCJ8="},"type":"unlocked"},
                "publicKey": "NPaOkPjlF3WFgA1WaqtANE0tqX/M8Rdr1h4SzQX0ghQ="
              }
            ]
          },
          "alwaysSendTo": []
        }
    

    tessera-2.json

        {
          "useWhiteList": false,
          "jdbc": {
            "username": "sa",
            "password": "",
            "url": "jdbc:h2:/opt/dev/quorum/testnet/data/node2/tm/db;MODE=Oracle;TRACE_LEVEL_SYSTEM_OUT=0",
            "autoCreateTables": true
          },
          "serverConfigs":[
          {
            "app":"ThirdParty",
            "enabled": true,
            "serverAddress": "http://127.0.0.1:9182",
            "communicationType" : "REST"
          },
          {
            "app":"Q2T",
            "enabled": true,
            "serverAddress": "unix:/opt/dev/quorum/testnet/data/node2/tm/tm.ipc",
            "communicationType" : "REST"
          },
          {
            "app":"P2P",
            "enabled": true,
            "serverAddress": "http://127.0.0.1:9102",
            "sslConfig": {
              "tls": "OFF",
              "generateKeyStoreIfNotExisted": true,
              "serverKeyStore": "/opt/dev/quorum/testnet/data/node2/tm/server-keystore",
              "serverKeyStorePassword": "quorum",
              "serverTrustStore": "/opt/dev/quorum/testnet/data/node2/tm/server-truststore",
              "serverTrustStorePassword": "quorum",
              "serverTrustMode": "TOFU",
              "knownClientsFile": "/opt/dev/quorum/testnet/data/node2/tm/knownClients",
              "clientKeyStore": "/opt/dev/quorum/testnet/data/node2/tm/client-keystore",
              "clientKeyStorePassword": "quorum",
              "clientTrustStore": "/opt/dev/quorum/testnet/data/node2/tm/client-truststore",
              "clientTrustStorePassword": "quorum",
              "clientTrustMode": "TOFU",
              "knownServersFile": "/opt/dev/quorum/testnet/data/node2/tm/knownServers"
            },
            "communicationType" : "REST"
          }
          ],
          "peer": [
             {
                 "url": "http://127.0.0.1:9101"
             },
             {
                 "url": "http://127.0.0.1:9102"
             },
             {
                 "url": "http://127.0.0.1:9103"
             }
          ],
          "keys": {
            "passwords": [],
            "keyData": [
              {
                "config": {"data":{"bytes":"/FAEF3msNOcWNkLkzUdSdNLFvFSJgddjwV2HOWTV/Rk="},"type":"unlocked"},
                "publicKey": "T8olcFvm2JojQd616k1MIx/Gm2IEZPkyV4GutVvrPgM="
              }
            ]
          },
          "alwaysSendTo": []
        }
    

    tessera-3.json

        {
          "useWhiteList": false,
          "jdbc": {
            "username": "sa",
            "password": "",
            "url": "jdbc:h2:/opt/dev/quorum/testnet/data/node3/tm/db;MODE=Oracle;TRACE_LEVEL_SYSTEM_OUT=0",
            "autoCreateTables": true
          },
          "serverConfigs":[
          {
            "app":"ThirdParty",
            "enabled": true,
            "serverAddress": "http://127.0.0.1:9183",
            "communicationType" : "REST"
          },
          {
            "app":"Q2T",
            "enabled": true,
            "serverAddress": "unix:/opt/dev/quorum/testnet/data/node3/tm/tm.ipc",
            "communicationType" : "REST"
          },
          {
            "app":"P2P",
            "enabled": true,
            "serverAddress": "http://127.0.0.1:9103",
            "sslConfig": {
              "tls": "OFF",
              "generateKeyStoreIfNotExisted": true,
              "serverKeyStore": "/opt/dev/quorum/testnet/data/node3/tm/server-keystore",
              "serverKeyStorePassword": "quorum",
              "serverTrustStore": "/opt/dev/quorum/testnet/data/node3/tm/server-truststore",
              "serverTrustStorePassword": "quorum",
              "serverTrustMode": "TOFU",
              "knownClientsFile": "/opt/dev/quorum/testnet/data/node3/tm/knownClients",
              "clientKeyStore": "/opt/dev/quorum/testnet/data/node3/tm/client-keystore",
              "clientKeyStorePassword": "quorum",
              "clientTrustStore": "/opt/dev/quorum/testnet/data/node3/tm/client-truststore",
              "clientTrustStorePassword": "quorum",
              "clientTrustMode": "TOFU",
              "knownServersFile": "/opt/dev/quorum/testnet/data/node3/tm/knownServers"
            },
            "communicationType" : "REST"
          }
          ],
          "peer": [
             {
                 "url": "http://127.0.0.1:9101"
             },
             {
                 "url": "http://127.0.0.1:9102"
             },
             {
                 "url": "http://127.0.0.1:9103"
             }
          ],
          "keys": {
            "passwords": [],
            "keyData": [
              {
                "config": {"data":{"bytes":"ZR00hvY4nCiG8sWQFasKvwGtOBi0b2oxNriVdMN++MY="},"type":"unlocked"},
                "publicKey": "Uc952L7QFuk8R5sGtjpfHb9EM+X6pTFRixVa+XgPBjc="
              }
            ]
          },
          "alwaysSendTo": []
        }
    

    七、启动transaction manager - tessera

    执行如下命令后台启动三个tessera transaction manager

    \>nohup java -Xms128M -Xmx128M -jar /opt/dev/quorum/testnet/tmp/tessera-app-0.9.jar -configfile /opt/dev/quorum/testnet/tmp/tessera-1.json >> /opt/dev/quorum/testnet/data/node1/tm.log &
    \>nohup java -Xms128M -Xmx128M -jar /opt/dev/quorum/testnet/tmp/tessera-app-0.9.jar -configfile /opt/dev/quorum/testnet/tmp/tessera-2.json >> /opt/dev/quorum/testnet/data/node2/tm.log &
    \>nohup java -Xms128M -Xmx128M -jar /opt/dev/quorum/testnet/tmp/tessera-app-0.9.jar -configfile /opt/dev/quorum/testnet/tmp/tessera-3.json >> /opt/dev/quorum/testnet/data/node3/tm.log &
    

    tessera transaction manager启动成功后,会在相应的路径下生成ipc文件

    /opt/dev/quorum/testnet/data/node1/tm/tm.ipc
    /opt/dev/quorum/testnet/data/node2/tm/tm.ipc
    /opt/dev/quorum/testnet/data/node3/tm/tm.ipc
    

    八、启动quorum node

    因为前面账户创建时候我们没有设置密码,所以建立三个空的password.txt文件

    touch /opt/dev/quorum/testnet/data/node1/passwords.txt
    touch /opt/dev/quorum/testnet/data/node2/passwords.txt
    touch /opt/dev/quorum/testnet/data/node3/passwords.txt
    

    执行如下命令后台启动三个quorum node, quorum node和tessera transaction manager通过ipc进行通信

    \>export PRIVATE_CONFIG=/opt/dev/quorum/testnet/data/node1/tm/tm.ipc
    \>nohup geth --identity node1-istanbul --datadir /opt/dev/quorum/testnet/data/node1/dd --permissioned --nodiscover --verbosity 5 --networkid 10 --rpc --rpcaddr 0.0.0.0 --rpcport 32001 --rpcapi admin,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum,istanbul --port 31001 --unlock 0 --password /opt/dev/quorum/testnet/data/node1/passwords.txt --emitcheckpoints --istanbul.blockperiod 1 --mine --minerthreads 1 --syncmode full >> /opt/dev/quorum/testnet/data/node1/dd.log &
    
    \>export PRIVATE_CONFIG=/opt/dev/quorum/testnet/data/node2/tm/tm.ipc 
    \>nohup geth --identity node2-istanbul --datadir /opt/dev/quorum/testnet/data/node2/dd --permissioned --nodiscover --verbosity 5 --networkid 10 --rpc --rpcaddr 0.0.0.0 --rpcport 32002 --rpcapi admin,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum,istanbul --port 31002 --unlock 0 --password /opt/dev/quorum/testnet/data/node2/passwords.txt --emitcheckpoints --istanbul.blockperiod 1 --mine --minerthreads 1 --syncmode full >> /opt/dev/quorum/testnet/data/node2/dd.log &
    
    \>export PRIVATE_CONFIG=/opt/dev/quorum/testnet/data/node3/tm/tm.ipc
    \>nohup geth --identity node3-istanbul --datadir /opt/dev/quorum/testnet/data/node3/dd --permissioned --nodiscover --verbosity 5 --networkid 10 --rpc --rpcaddr 0.0.0.0 --rpcport 32003 --rpcapi admin,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum,istanbul --port 31003 --unlock 0 --password /opt/dev/quorum/testnet/data/node3/passwords.txt --emitcheckpoints --istanbul.blockperiod 1 --mine --minerthreads 1 --syncmode full >> /opt/dev/quorum/testnet/data/node3/dd.log &
    

    至此我们的Quorum区块链网络就已经启动成功了。

    九、验证

    我们部署一个私有合约,以此来验证我们的区块链网络

    首先连接到node1的console

    \>geth attach /opt/dev/quorum/testnet/data/node1/dd/geth.ipc
    zmm: cfgPath is  PRIVATE_CONFIG
    Welcome to the Geth JavaScript console!
    
    instance: Geth/node1-istanbul/v1.8.18-stable-f681cbf3(quorum-v2.2.3)/linux-amd64/go1.12.1
    coinbase: 0x90ccaba53ed0c2979d4659692ca3b0ecc385fd70
    at block: 4 (Sat, 27 Apr 2019 15:56:13 CST)
     datadir: /opt/dev/quorum/testnet/data/node1/dd
     modules: admin:1.0 debug:1.0 eth:1.0 istanbul:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0
    
    > 
    

    贴入以下内容部署一个私有合约, 例子来自官方的7nodesample, 注意修改其中的"privateFor"字段为tessera秘钥对中3.pub的内容,表明由node1发起的这个合约只同node3进行私有

    \>more 3.pub 
    Uc952L7QFuk8R5sGtjpfHb9EM+X6pTFRixVa+XgPBjc=
    
    a = eth.accounts[0]
    web3.eth.defaultAccount = a;
    
    // abi and bytecode generated from simplestorage.sol:
    // > solcjs --bin --abi simplestorage.sol
    var abi = [{"constant":true,"inputs":[],"name":"storedData","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"x","type":"uint256"}],"name":"set","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"retVal","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[{"name":"initVal","type":"uint256"}],"payable":false,"type":"constructor"}];
    
    var bytecode = "0x6060604052341561000f57600080fd5b604051602080610149833981016040528080519060200190919050505b806000819055505b505b610104806100456000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632a1afcd914605157806360fe47b11460775780636d4ce63c146097575b600080fd5b3415605b57600080fd5b606160bd565b6040518082815260200191505060405180910390f35b3415608157600080fd5b6095600480803590602001909190505060c3565b005b341560a157600080fd5b60a760ce565b6040518082815260200191505060405180910390f35b60005481565b806000819055505b50565b6000805490505b905600a165627a7a72305820d5851baab720bba574474de3d09dbeaabc674a15f4dd93b974908476542c23f00029";
    
    var simpleContract = web3.eth.contract(abi);
    var simple = simpleContract.new(42, {from:web3.eth.accounts[0], data: bytecode, gas: 0x47b760, privateFor: ["Uc952L7QFuk8R5sGtjpfHb9EM+X6pTFRixVa+XgPBjc="]}, function(e, contract) {
        if (e) {
            console.log("err creating contract", e);
        } else {
            if (!contract.address) {
                console.log("Contract transaction send: TransactionHash: " + contract.transactionHash + " waiting to be mined...");
            } else {
                console.log("Contract mined! Address: " + contract.address);
                console.log(contract);
            }
        }
    });
    

    修改"privateFor"字段之后,将其贴入console:

    instance: Geth/node1-istanbul/v1.8.18-stable-f681cbf3(quorum-v2.2.3)/linux-amd64/go1.12.1
    coinbase: 0x90ccaba53ed0c2979d4659692ca3b0ecc385fd70
    at block: 109 (Sat, 27 Apr 2019 16:17:03 CST)
     datadir: /opt/dev/quorum/testnet/data/node1/dd
     modules: admin:1.0 debug:1.0 eth:1.0 istanbul:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0
    
    > a = eth.accounts[0]
    :false,"type":"function"},{"constant":false,"inputs":[{"name":"x","type":"uint256"}],"name":"set","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"retVal","type":"uint256"}],"payable":false,"type"0x9affedff10f7229c680819d5eeb12c3624f6baeb"
    > web3.eth.defaultAccount = a;
    "0x9affedff10f7229c680819d5eeb12c3624f6baeb"
    > 
    > // abi and bytecode generated from simplestorage.sol:
    undefined
    > // > solcjs --bin --abi simplestorage.sol
    undefined
    > var abi = [{"constant":true,"inputs":[],"name":"storedData","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"x","type":"uint256"}],"name":"set","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"retVal","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[{"name":"initVal","type":"uint256"}],"payable":false,"type":"constructor"}];
    undefined
    > 
    > var bytecode = "0x6060604052341561000f57600080fd5b604051602080610149833981016040528080519060200190919050505b806000819055505b505b610104806100456000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632a1afcd914605157806360fe47b11460775780636d4ce63c146097575b600080fd5b3415605b57600080fd5b606160bd565b6040518082815260200191505060405180910390f35b3415608157600080fd5b6095600480803590602001909190505060c3565b005b341560a157600080fd5b60a760ce565b6040518082815260200191505060405180910390f35b60005481565b806000819055505b50565b6000805490505b905600a165627a7a72305820d5851baab720bba574474de3d09dbeaabc674a15f4dd93b974908476542c23f00029";
    undefined
    > 
    > var simpleContract = web3.eth.contract(abi);
    undefined
    > var simple = simpleContract.new(42, {from:web3.eth.accounts[0], data: bytecode, gas: 0x47b760, privateFor: ["Uc952L7QFuk8R5sGtjpfHb9EM+X6pTFRixVa+XgPBjc="]}, function(e, contract) {
    ...... if (e) {
    ......... console.log("err creating contract", e);
    ......... } else {
    ......... if (!contract.address) {
    ............ console.log("Contract transaction send: TransactionHash: " + contract.transactionHash + " waiting to be mined...");
    ............ } else {
    ............ console.log("Contract mined! Address: " + contract.address);
    ............ console.log(contract);
    ............ }
    ......... }
    ...... });
    Contract transaction send: TransactionHash: 0x74efa531fa5e8c1962a0b7cd2c1f3c34e5f19890cec27a1f90438edb074bda51 waiting to be mined...
    undefined
    > Contract mined! Address: 0xc48ba0d7ea03ab25a5f264845b848d847d391fc4
    [object Object]
    > 
    

    看到下方的Contract mined! Address: 0x5f71775e74bc96902c31df3205aca9a968811a42则说明IBFT工作正常,成功出块,接下来我们验证private隐私性,因为我们使用了privateFor,只允许node3持有私有数据,因此我们对node1、node2和node3分别调用智能合约,看看结果如何:
    对于node1,我们继续使用刚才的console

    > simple.get()
    42
    

    结果是我们创建合约时候赋值的42,接着打开node2终端

    \>geth attach /opt/dev/quorum/testnet/data/node2/dd/geth.ipc
    zmm: cfgPath is  PRIVATE_CONFIG
    Welcome to the Geth JavaScript console!
    
    instance: Geth/node2-istanbul/v1.8.18-stable-f681cbf3(quorum-v2.2.3)/linux-amd64/go1.12.1
    coinbase: 0xa02f4bb093989222608d25c6c57a5b40526f6796
    at block: 445 (Sat, 27 Apr 2019 16:22:39 CST)
     datadir: /opt/dev/quorum/testnet/data/node2/dd
     modules: admin:1.0 debug:1.0 eth:1.0 istanbul:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0
    
    > 
    

    为了调用我们刚才创建的智能合约,simple.at处应该使用地址0xc48ba0d7ea03ab25a5f264845b848d847d391fc4,注意你应该把地址修改成你自己刚刚部署的contract地址,在node2的console贴入如下代码:

    a = eth.accounts[0]
    web3.eth.defaultAccount = a;
    
    // abi and bytecode generated from simplestorage.sol:
    // > solcjs --bin --abi simplestorage.sol
    var abi = [{"constant":true,"inputs":[],"name":"storedData","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"x","type":"uint256"}],"name":"set","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"retVal","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[{"name":"initVal","type":"uint256"}],"payable":false,"type":"constructor"}];
    
    var simpleContract = web3.eth.contract(abi);
    var simple = simpleContract.at("0xc48ba0d7ea03ab25a5f264845b848d847d391fc4")
    

    结果:

    > a = eth.accounts[0]
    :false,"type":"function"},{"constant":false,"inputs":[{"name":"x","type":"uint256"}],"name":"set","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"retVal","type":"uint256"}],"payable":false,"type"0x5c8822ab6af840f8c3e52fe9a71c43f90672728e"
    > web3.eth.defaultAccount = a;
    "0x5c8822ab6af840f8c3e52fe9a71c43f90672728e"
    > 
    > // abi and bytecode generated from simplestorage.sol:
    undefined
    > // > solcjs --bin --abi simplestorage.sol
    undefined
    > var abi = [{"constant":true,"inputs":[],"name":"storedData","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"x","type":"uint256"}],"name":"set","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"retVal","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[{"name":"initVal","type":"uint256"}],"payable":false,"type":"constructor"}];
    undefined
    > 
    > var simpleContract = web3.eth.contract(abi);
    undefined
    > var simple = simpleContract.at("0xc48ba0d7ea03ab25a5f264845b848d847d391fc4")
    undefined
    

    接着我们调用合约:

    > simple.get()
    0
    

    结果符合预期,node2应该看不到node1和node3的私有合约
    接下来以同样的方式打开node3的console

    \>geth attach /opt/dev/quorum/testnet/data/node3/dd/geth.ipc
    zmm: cfgPath is  PRIVATE_CONFIG
    Welcome to the Geth JavaScript console!
    
    instance: Geth/node3-istanbul/v1.8.18-stable-f681cbf3(quorum-v2.2.3)/linux-amd64/go1.12.1
    coinbase: 0xf0fa966e6efa633080d1603e7daea5176de87d82
    at block: 754 (Sat, 27 Apr 2019 16:27:48 CST)
     datadir: /opt/dev/quorum/testnet/data/node3/dd
     modules: admin:1.0 debug:1.0 eth:1.0 istanbul:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0
    
    > a = eth.accounts[0]
    :false,"type":"function"},{"constant":false,"inputs":[{"name":"x","type":"uint256"}],"name":"set","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"retVal","type":"uint256"}],"payable":false,"type"0x915779c113cffecac6583310d80a8def09546272"
    > web3.eth.defaultAccount = a;
    "0x915779c113cffecac6583310d80a8def09546272"
    > 
    > // abi and bytecode generated from simplestorage.sol:
    undefined
    > // > solcjs --bin --abi simplestorage.sol
    undefined
    > var abi = [{"constant":true,"inputs":[],"name":"storedData","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"x","type":"uint256"}],"name":"set","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"retVal","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[{"name":"initVal","type":"uint256"}],"payable":false,"type":"constructor"}];
    undefined
    > 
    > var simpleContract = web3.eth.contract(abi);
    undefined
    > var simple = simpleContract.at("0xc48ba0d7ea03ab25a5f264845b848d847d391fc4")
    undefined
    > simple.get()
    42
    > 
    

    可以看到结果符合预期,node3读出了私有合约的值42。

    至此我们已经成功搭建了基于Quorum的使用IBFT共识的支持私有合约的区块链网络,下一篇文章将讲述在搭建后进行动态增加节点以及增加IBFT投票validator的操作。

    相关文章

      网友评论

        本文标题:Quorum手动搭建指南

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