私有链集群

在私有网络中建立多个节点组成的集群,并互相发现,产生交易.

创世区块

集群中所有的节点必须报有相同的genesis state, 否则节点无法关联上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"config": {
"chainId": 180,
"homesteadBlock": 0,
"eip155Block": 0,
"eip158Block": 0
},
"alloc": {
"fad9684b077d9046ff2e293a375d29e2ebc5445a": {
"balance": "30000000000000000"
}
},
"coinbase": "0x0000000000000000000000000000000000000000",
"difficulty": "0x20000",
"extraData": "",
"gasLimit": "0x2fefd8",
"nonce": "0x0000000000000042",
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"timestamp": "0x00"
}

搭建私有链节点

参考<<搭建私有链(Private Chain)并进行挖矿和交易>>,

  1. 启动节点1作为boot node,

    1
    2
    3
    4
    # Node 1 - boot node 1
    geth --datadir ./data/1 init genesis.json
    cp -R ./keystore/ ./data/1/keystore/
    geth --datadir ./data/1 --identity "boot-node-1" --networkid 1808923373394438348 --verbosity 6 --ipcdisable --port 30301 --rpcport 8101 console 2>> ./logs/1.log

    可以查看帐号信息和当前节点信息:

    1
    2
    3
    4
    5
    6
    > personal.listAccounts
    ["0xfad9684b077d9046ff2e293a375d29e2ebc5445a"]
    > admin.peers
    []
    > admin.nodeInfo.enode
    "enode://19c2f5b1d4e3ea0d4b042eb3b631b0eb7648c442df687926d8b7063e2d50404e1566c8a052e5c57925b20df9a8b0fa2511679df20c37100b62e6f1446cf5bfe3@[::]:30301"
  2. 启动节点3作为挖矿节点
    --mine启动节点3作为挖矿节点, 再通过--bootnodes配置关联节点1

    1
    2
    3
    4
    5
    6
    7
    # Node 3 - miner node
    geth --datadir ./data/3 init genesis.json
    cp -R ./keystore/ ./data/3/keystore/
    geth --datadir ./data/3 --identity "miner-node" --networkid 1808923373394438348 --verbosity 4 --ipcdisable --port 30303 --rpcport 8103 \
    --minerthreads=1 --etherbase fad9684b077d9046ff2e293a375d29e2ebc5445a \
    --bootnodes "enode://19c2f5b1d4e3ea0d4b042eb3b631b0eb7648c442df687926d8b7063e2d50404e1566c8a052e5c57925b20df9a8b0fa2511679df20c37100b62e6f1446cf5bfe3@127.0.0.1:30301" \
    console 2>> ./logs/3.log

    节点3起来后,检查节点联系:

    1
    2
    3
    4
    5
    6
    > personal.listAccounts
    ["0xfad9684b077d9046ff2e293a375d29e2ebc5445a"]
    > admin.peers
    [{
    ...
    }]

    注意: 通过IP 127.0.0.1替代, 外部主机也可以通过外部IP建立关联. (可以通过 ifconfig|grep netmask|awk '{print $2}' 可获取IP.)

  3. 启动节点2也作为boot node:

    1
    2
    3
    4
    # Node 2 - boot node 2
    geth --datadir ./data/2 init genesis.json
    cp -R ./keystore/ ./data/2/keystore/
    geth --datadir ./data/2 --identity "boot-node-2" --networkid 1808923373394438348 --verbosity 4 --ipcdisable --port 30302 --rpcport 8102 console 2>> ./logs/2.log

    节点2起来后,可以查看帐号信息和当前节点信息:

    1
    2
    3
    4
    5
    6
    > personal.listAccounts
    ["0xfad9684b077d9046ff2e293a375d29e2ebc5445a"]
    > admin.peers
    []
    > admin.nodeInfo.enode
    "enode://dd4b2503ad79644d0591daa7b11bcb04ce62fe4798a1747b62a8289e3dbd0cdb770478dcc44e42a00ff2ddcfde1cfeb98fbf36769474bfcddb07704b75e0f026@[::]:30302"
  4. 关联节点2 & 3:
    在节点3控制台通过admin.addPeer()命令关联节点2 & 3, 再检查节点1, 2 & 3的peers:

    1
    2
    3
    4
    5
    6
    > addmin.addPeer('enode://dd4b2503ad79644d0591daa7b11bcb04ce62fe4798a1747b62a8289e3dbd0cdb770478dcc44e42a00ff2ddcfde1cfeb98fbf36769474bfcddb07704b75e0f026@127.0.0.1:30302')
    true
    > admin.peers
    [
    ...
    ]
  5. 查看账余额
    检查初始账户的余额为30000000000000000, 通过miner.start(1)让矿工开工:

    1
    2
    3
    4
    > miner.start(1)
    null
    > eth.getBalance('0xfad9684b077d9046ff2e293a375d29e2ebc5445a')
    30000000000000000

    金额在变化, 矿工已开工.

  6. 启动节点4为交易节点
    通过--bootnodes将两个boot node都关联上:

    1
    2
    3
    4
    5
    6
    # Node 4 - transaction node 1
    geth --datadir ./data/4 init genesis.json
    cp -R ./keystore/ ./data/4/keystore/
    geth --datadir ./data/4 --identity "transaction-node-1" --networkid 1808923373394438348 --verbosity 4 --ipcdisable --port 30304 --rpcport 8104 \
    --bootnodes "enode://19c2f5b1d4e3ea0d4b042eb3b631b0eb7648c442df687926d8b7063e2d50404e1566c8a052e5c57925b20df9a8b0fa2511679df20c37100b62e6f1446cf5bfe3@127.0.0.1:30301,enode://dd4b2503ad79644d0591daa7b11bcb04ce62fe4798a1747b62a8289e3dbd0cdb770478dcc44e42a00ff2ddcfde1cfeb98fbf36769474bfcddb07704b75e0f026@127.0.0.1:30302" \
    console 2>> ./logs/4.log
  7. 一个交易节点发生交易
    通过miner.stop()关停矿区, 在交易节点上新加帐号, 并转账:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    > personal.newAccount("acct1")
    "0xfe14b8ed857f4482602d9438f651c94e8a53b31f"
    > eth.getBalance('0xfe14b8ed857f4482602d9438f651c94e8a53b31f')
    0
    > eth.getBalance('0xfad9684b077d9046ff2e293a375d29e2ebc5445a')
    890029999999999999995
    > personal.unlockAccount('0xfad9684b077d9046ff2e293a375d29e2ebc5445a')
    Unlock account 0xfad9684b077d9046ff2e293a375d29e2ebc5445a
    Passphrase: [password]
    true
    > eth.sendTransaction({from:'0xfad9684b077d9046ff2e293a375d29e2ebc5445a',to:'0xfe14b8ed857f4482602d9438f651c94e8a53b31f',value:5})
    "0x01afeaac31ad8226950d45c91e637021546071978eb0589ecc7ba03f9b7f7302"
    > eth.getTransaction('0x01afeaac31ad8226950d45c91e637021546071978eb0589ecc7ba03f9b7f7302') // 产看交易细节
    {
    blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
    blockNumber: null,
    from: "0xfad9684b077d9046ff2e293a375d29e2ebc5445a",
    gas: 90000,
    gasPrice: 18000000000,
    hash: "0x01afeaac31ad8226950d45c91e637021546071978eb0589ecc7ba03f9b7f7302",
    input: "0x",
    nonce: 0,
    r: "0x79b8930fa1efbf3352636b8f1723ff040fb4d477489d5e424b1da36ab799c59f",
    s: "0x371bbebf6d5e1f568f2169d1c581c7e79f8c92ce28d9873ec6c23095e9d6092e",
    to: "0xfe14b8ed857f4482602d9438f651c94e8a53b31f",
    transactionIndex: 0,
    v: "0x18b",
    value: 5
    }
    > eth.getBalance('0xfe14b8ed857f4482602d9438f651c94e8a53b31f')
    0
    > eth.getBalance('0xfad9684b077d9046ff2e293a375d29e2ebc5445a')
    890029999999999999995
    > txpool.status // 查看交易池
    {
    pending: 1,
    queued: 0
    }

    矿工开工miner.start(1), 再来查看交易:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    > txpool.status  // 查看交易池
    {
    pending: 0,
    queued: 0
    }
    > eth.getBalance('0xfe14b8ed857f4482602d9438f651c94e8a53b31f')
    5
    > eth.getBalance('0xfad9684b077d9046ff2e293a375d29e2ebc5445a')
    890029999999999999990
    > eth.getTransaction('0x01afeaac31ad8226950d45c91e637021546071978eb0589ecc7ba03f9b7f7302')
    {
    blockHash: "0x2dfa70b0383b49f69ce8863ac453108f52921f0880c0fb4b4e14230cd82b5186",
    blockNumber: 127,
    from: "0xfad9684b077d9046ff2e293a375d29e2ebc5445a",
    gas: 90000,
    gasPrice: 18000000000,
    hash: "0x01afeaac31ad8226950d45c91e637021546071978eb0589ecc7ba03f9b7f7302",
    input: "0x",
    nonce: 0,
    r: "0x79b8930fa1efbf3352636b8f1723ff040fb4d477489d5e424b1da36ab799c59f",
    s: "0x371bbebf6d5e1f568f2169d1c581c7e79f8c92ce28d9873ec6c23095e9d6092e",
    to: "0xfe14b8ed857f4482602d9438f651c94e8a53b31f",
    transactionIndex: 0,
    v: "0x18b",
    value: 5
    }
  8. 交易节点之间发生交易
    static node的方式关联节点, 配置 static-nodes.json

    1
    2
    3
    4
    [
    "enode://19c2f5b1d4e3ea0d4b042eb3b631b0eb7648c442df687926d8b7063e2d50404e1566c8a052e5c57925b20df9a8b0fa2511679df20c37100b62e6f1446cf5bfe3@127.0.0.1:30301",
    "enode://dd4b2503ad79644d0591daa7b11bcb04ce62fe4798a1747b62a8289e3dbd0cdb770478dcc44e42a00ff2ddcfde1cfeb98fbf36769474bfcddb07704b75e0f026@127.0.0.1:30302"
    ]

    启动节点5为第二个交易节点:

    1
    2
    3
    4
    5
    6
    # Node 5 - transaction node 2
    geth --datadir ./data/5 init genesis.json
    cp -R ./keystore/ ./data/5/keystore/
    cp -R ./static-nodes.json ./data/5/
    geth --datadir ./data/5 --identity transaction-node-2 --networkid 1808923373394438348 --verbosity 4 --ipcdisable --port 30305 --rpcport 8105 \
    console 2>> ./logs/5.log

    为节点5创建帐号:

    1
    2
    3
    4
    5
    6
    > personal.newAccount('acct2')
    "0x210bb5dc90b655622ae4be01a37c26ec35b83ed2"
    > eth.getBalance('0xfe14b8ed857f4482602d9438f651c94e8a53b31f')
    1650000000010
    > eth.getBalance('0x210bb5dc90b655622ae4be01a37c26ec35b83ed2')
    0

    从节点4开始交易:

    1
    2
    3
    4
    5
    6
    > personal.unlockAccount('0xfe14b8ed857f4482602d9438f651c94e8a53b31f')
    Unlock account 0xfe14b8ed857f4482602d9438f651c94e8a53b31f
    Passphrase: [password]
    true
    > eth.sendTransaction({from:'0xfe14b8ed857f4482602d9438f651c94e8a53b31f',to:'0x210bb5dc90b655622ae4be01a37c26ec35b83ed2',value:5})
    "0x01afeaac31ad8226950d45c91e637021546071978eb0589ecc7ba03f9b7f7302"

    注意:

    1. 当前节点必须包含转出账户, 否则无法交易
    2. 通过 static-nodes.json 的方式创建起来的节点, 是只能发现文件中配置的节点, 无法发现集群中中其他的节点, 这样可以用于做网络隔离.
  9. 关停boot node
    当关停boot node时, 会发现两个通过boot node简历关联的节点还是可以发现彼此的. boot node在集群中充当一个审批人的角色, 一个新的节点在需要给boot node发申请, 由boot node决定它是否可以加入这个圈子. boot node同意它的申请后, 它从boot node得到集群中其他节点的信息, 然后它并会与这些节点建立联系. 这时, boot node就已经完成了他的任务, 后续它的存在是可有可无的.

疑问:

  1. 节点集群中的单个节点上创建的帐号如何同步到其他的节点上

补充

节点发现协议

sequenceDiagram New Node ->> Boot Node: ping Boot Node -->> New Node: pong New Node ->> Boot Node: findnode Boot Node -->> New Node: neighbors

参考:

  1. http://blog.csdn.net/wo541075754/article/details/78926177
  2. http://www.cnblogs.com/zl03jsj/p/6876064.html
  3. http://blog.csdn.net/ddffr/article/details/78732256
  4. http://blog.csdn.net/ddffr/article/details/78912026
  5. https://github.com/ethereum/wiki/wiki/Node-discovery-protocol
  6. https://github.com/ethereum/devp2p/blob/master/rlpx.md#node-discovery