Quorum手動搭建指南

最近在研究Quorum, 雖然Quorum官方有7nodesample可以快速創(chuàng)建一個quorum區(qū)塊鏈網(wǎng)絡(luò)置尔,但它隱藏了眾多配置細節(jié)和步驟歪玲,目的是為了新手快速體驗其功能询筏。所以如果希望深入了解其機理并進行定制的話疹娶,手動搭建必不可少,以下將詳細展示搭建步驟饰及。

Quorum是基于以太坊的,最突出的改進在于支持IBFT(Istanbul Byzantine Fault Tolerant)共識協(xié)議以及支持私密交易(private transaction),本教程將搭建支持IBFT和私密交易的擁有三個node和三個transaction manager的Quorum區(qū)塊鏈網(wǎng)絡(luò)播急。

一、規(guī)劃目錄結(jié)構(gòu)及端口

Quorum架構(gòu)圖

根據(jù)Quorum的架構(gòu)售睹,我們創(chuàng)建三個node和三個transaction manager的網(wǎng)絡(luò)桩警,每個node和每個transaction manager都需要有自己的data目錄,因此我們的目錄結(jié)構(gòu)規(guī)劃如下:

/opt/dev/testnet/data   --數(shù)據(jù)文件根目錄
|_node1                      
|     |_dd      --node1數(shù)據(jù)目錄
|     |  |_geth
|     |  |_keystore
|     |_tm      --transaction manager1數(shù)據(jù)目錄
|_node2
|     |_dd      --node2數(shù)據(jù)目錄
|     |  |_geth
|     |  |_keystore
|     |_tm      --transaction manager2數(shù)據(jù)目錄
|_node3
      |_dd      --node3數(shù)據(jù)目錄
      |  |_geth
      |  |_keystore
      |_tm      --transaction manager3數(shù)據(jù)目錄

創(chuàng)建腳本:

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

端口的規(guī)劃如下

#node端口
32001~32003       --rpc服務(wù)端口
31001~31003       --node間peer通信端口
50401~50403       --raft協(xié)議端口

#transaction manager 端口
9101~9104         --tx manager間通信端口
9181~9184         --服務(wù)端口

二昌妹、創(chuàng)建賬戶Account

不同于Hyperledger Fabric擁有MSP組件可以創(chuàng)建管理組織以及證書捶枢,以太坊是完全基于公鏈設(shè)計的握截,并不具備這種PKI的集中管理的機制,Quorum對此并無任何改造和增強烂叔,因此account的生成完全是可以離線操作的谨胞,而賬戶的本質(zhì)其實就是一個橢圓曲線的私鑰。本文之所以要預(yù)先準備賬戶是為了在之后的node console中方便的使用web3的api, 因為有些api是要求節(jié)點中配置賬戶信息的蒜鸡。

本文使用的Quorum版本是V2.2.3
),請自行下載解壓出geth可執(zhí)行文件胯努,創(chuàng)建賬戶的命令如下, 提示輸入密碼的時候可以直接回車不進行設(shè)置:

\>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

三逢防、創(chuàng)建nodekey

nodekey是node的唯一標識叶沛,需要配置在node數(shù)據(jù)文件路徑中,以太坊nodekey生成的標準命令是使用bootnode --genkey=nodekey命令忘朝,但若使用IBFT共識的話灰署,對nodekey是有嚴格要求的, validator id是根據(jù)nodekey生成的,所以如果選用IBFT共識我們就需要使用istanbul-tool來生成nodekey局嘁。istanbul-tool需要從git
)上clone溉箕,自行構(gòu)建出istanbul命令,然后在一個臨時目錄/opt/dev/quorum/testnet/tmp中執(zhí)行:

\>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*

根據(jù)我們的參數(shù)--num 3导狡,命令將創(chuàng)建3個nodekey, 打開其中一個可以看到:

\>more 0/nodekey
c3828ef20a55925c60587f63359798249fb8c20992ef68ce9d0ff10abfd8c858

手動將三個nodekey文件分別拷貝到相應(yīng)node的數(shù)據(jù)路徑

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 聯(lián)盟鏈的node是有準入限制的,體現(xiàn)在permissioned-nodes.json文件中旱捧,permissioned-nodes.json同時可以用作靜態(tài)節(jié)點配置独郎,所以在本例中static-nodes.json, permissioned-nodes.json兩個文件是相同的:
首先打開我們使用istanbul命令創(chuàng)建的static-nodes.json文件

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

根據(jù)我們之前的端口規(guī)劃,修改端口如下:

[
"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"
]

然后將文件配置到相應(yīng)的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

五枚赡、創(chuàng)建創(chuàng)世文件genesis.json并初始化區(qū)塊鏈

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

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

內(nèi)容修改如下:

{
    "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, 并修改了我們希望的預(yù)置balance余額,對于quorum來說balance沒什么用處贫橙,初始化一個較大的值即可贪婉。istanbul命令已經(jīng)幫我們配置了"isQuorum"以及"istanbul"的屬性,不必我們再去設(shè)置:

{
      "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是無關(guān)的幅垮。很多人把這個validator理解成了account腰池,將account id用istanbul工具編碼后配置到了genesis.json的extraData字段中,這樣做的話,實際的共識發(fā)起者是根據(jù)nodekey生成的validator, 區(qū)塊打包共識的時候發(fā)現(xiàn)發(fā)起者并不在extraData編碼的validators列表中示弓,則為非法發(fā)起者讳侨,導(dǎo)致無法成功達成共識。

然后使用genesis.json初始化三個node節(jié)點

\>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跨跨、創(chuàng)建秘鑰對

首先下載或自行編譯, 圖省事可以docker pull quorumengineering/tessera:0.9, 然后啟動一個容器,從容器的/tessera/tessera-app.jar 位置拷貝到宿主機/opt/dev/quorum/testnet/tmp/tessera-app-0.9.jar拍皮,然后執(zhí)行如下命令歹叮,遇到彈出輸入密碼時同樣可以直接回車:

\>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=

創(chuàng)建三個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

執(zhí)行如下命令后臺啟動三個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啟動成功后咆耿,會在相應(yīng)的路徑下生成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

因為前面賬戶創(chuàng)建時候我們沒有設(shè)置密碼爹橱,所以建立三個空的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

執(zhí)行如下命令后臺啟動三個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區(qū)塊鏈網(wǎng)絡(luò)就已經(jīng)啟動成功了萨螺。

九、驗證

我們部署一個私有合約愧驱,以此來驗證我們的區(qū)塊鏈網(wǎng)絡(luò)

首先連接到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

> 

貼入以下內(nèi)容部署一個私有合約, 例子來自官方的7nodesample, 注意修改其中的"privateFor"字段為tessera秘鑰對中3.pub的內(nèi)容慰技,表明由node1發(fā)起的這個合約只同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持有私有數(shù)據(jù)艾帐,因此我們對node1、node2和node3分別調(diào)用智能合約盆偿,看看結(jié)果如何:
對于node1柒爸,我們繼續(xù)使用剛才的console

> simple.get()
42

結(jié)果是我們創(chuàng)建合約時候賦值的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

> 

為了調(diào)用我們剛才創(chuàng)建的智能合約事扭,simple.at處應(yīng)該使用地址0xc48ba0d7ea03ab25a5f264845b848d847d391fc4捎稚,注意你應(yīng)該把地址修改成你自己剛剛部署的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")

結(jié)果:

> 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

接著我們調(diào)用合約:

> simple.get()
0

結(jié)果符合預(yù)期求橄,node2應(yīng)該看不到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
> 

可以看到結(jié)果符合預(yù)期今野,node3讀出了私有合約的值42。

至此我們已經(jīng)成功搭建了基于Quorum的使用IBFT共識的支持私有合約的區(qū)塊鏈網(wǎng)絡(luò)罐农,下一篇文章將講述在搭建后進行動態(tài)增加節(jié)點以及增加IBFT投票validator的操作腥泥。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市啃匿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖溯乒,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件夹厌,死亡現(xiàn)場離奇詭異,居然都是意外死亡裆悄,警方通過查閱死者的電腦和手機矛纹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來光稼,“玉大人或南,你說我怎么就攤上這事“” “怎么了采够?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長冰垄。 經(jīng)常有香客問我蹬癌,道長,這世上最難降的妖魔是什么虹茶? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任逝薪,我火速辦了婚禮,結(jié)果婚禮上蝴罪,老公的妹妹穿的比我還像新娘董济。我一直安慰自己,他們只是感情好要门,可當我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布虏肾。 她就那樣靜靜地躺著,像睡著了一般暂衡。 火紅的嫁衣襯著肌膚如雪询微。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天狂巢,我揣著相機與錄音撑毛,去河邊找鬼。 笑死唧领,一個胖子當著我的面吹牛藻雌,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播斩个,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼胯杭,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了受啥?” 一聲冷哼從身側(cè)響起做个,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤鸽心,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后居暖,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體顽频,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年太闺,在試婚紗的時候發(fā)現(xiàn)自己被綠了糯景。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡省骂,死狀恐怖蟀淮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情钞澳,我是刑警寧澤怠惶,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站略贮,受9級特大地震影響甚疟,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜逃延,卻給世界環(huán)境...
    茶點故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一览妖、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧揽祥,春花似錦讽膏、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至料按,卻和暖如春奄侠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背载矿。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工垄潮, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人闷盔。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓弯洗,卻偏偏與公主長得像,于是被迫代替她去往敵國和親逢勾。 傳聞我的和親對象是個殘疾皇子牡整,可洞房花燭夜當晚...
    茶點故事閱讀 45,691評論 2 361

推薦閱讀更多精彩內(nèi)容