# 搭建联盟链集群环境

联盟链，是指一些有业务协作或业务交易的成员集合，由这些成员共同参与管理的区块链，通过联盟自治和节点准入准出控制策略，保障联盟的安全健康发展。


## 应用场景

联盟链采用采用QBFT共识，要求节点数满足N>3f，至少需要3f+1个节点（也就是最少需要4台服务器），与单节点docker部署方式更加适用于实际生产环境。
本文介绍快速搭建一个包含4个节点的联盟链开发环境，以及联盟链节点管理。
请提前准备至少5台服务器，其中4台用于搭建联盟链，1台用于体验节点管理。

## 下载软件包

- 下载多节点联盟链集群软件包: [资源下载](https://chain.33.cn/resource)

- 在其中一台节点服务器上解压软件包到任意目录。

  ```
  tar -zxvf chain33_qbft_7fe229c.tar.gz
  ```

  软件包包含以下文件：

  ```
  chain33         -- chain33节点程序
  chain33-cli     -- chain33节点命令行工具
  chain33.toml    -- chain33主链配置文件
  genconfig.sh    -- 节点目录生成工具
  ```

## 修改配置文件

搭建联盟链时，根据具体情况在`chain33.toml`配置文件中修改以下参数。

```
[p2p.sub.gossip]
# 用于p2p隔离。如果相同网络内部署多条链，不同链之间channel值不能配成一样。
channel=1099
[rpc]
# 链的jsonrpc和grpc地址，可以自定义。
jrpcBindAddr=":8801"
grpcBindAddr=":8802"
[consensus.sub.qbft]
# 创世积分所在的地址，可以修改成自己的地址。
· [命令行地址生成方式](../docs/foodTrace.md)
· JAVA-SDK地址生成方式见：https://baas.33.cn/doc/detail/69【创建区块链公私钥和地址】
genesis="14KEKbYtKKQm4wMthSK9J4La4nAiidGozt"
# 创世积分数量
genesisAmount=100000000
[exec.sub.manage]
# 链管理合约的supermanager地址，用于设置一些合约权限相关的配置，地址生成方式参考上述genesis地址。
superManager=["14KEKbYtKKQm4wMthSK9J4La4nAiidGozt"]
```

## 生成节点目录

1.在其中一台节点服务器中使用工具生成4台服务器对应的节点目录。
假设4台联盟链节点服务器的IP分别为`172.27.0.2`、 `172.27.0.5`、`172.27.0.9`、 `172.27.0.15`。

```
  # 生成四个节点的区块链程序及配置文件。（在云服务器上部署，存在外网地址和内网地址的情况下建议使用内网IP，千兆网络P2P传输性能更好。）  
  ./genconfig.sh "172.27.0.2,172.27.0.5,172.27.0.9,172.27.0.15"
```

2.将生成的节点目录，分别发送到对应的节点服务器。

```
#将172.27.0.2文件发送到IP为172.27.0.2的节点服务器指定目录中，如root目录。
scp -r ./172.27.0.2 root@172.27.0.2:/root
#需要输入节点服务器的登录密码。文件发送需要一定时间，请耐心等待。
root@172.27.0.2's password: 
chain33    
```

**注意**： 4台服务器之间防火墙需要放行13802（p2p端口）、33001(共识端口)，以及8801(区块链对外jsonrpc端口)。

## 启动节点

1.登录到各个节点执行启动命令，顺序不分前后，建议在一分钟内全都启动。

```
nohup ./chain33 -f chain33.toml >/dev/null 2>&1 &
```

2.执行命令，查看进程

```
ps -ef | grep -v grep | grep chain33
```

3、执行命令，查询节点同步状态(同步时间需要5分钟左右)。
返回结果为true则说明运行成功。

```
./chain33-cli net is_sync
```

4.执行命令，查询p2p节点信息。

```
./chain33-cli net peer
```

返回所有节点信息：

```json
"peers": [
        {
            "addr": "10.0.0.1",
            "port": 13802,
            "name": "03a237a51519e0c094ceb01fc3ae08f8907ec3e7276bce207bfcb59930bae166c1",
            "mempoolSize": 0,
            "self": false,
            "header": {
                "version": 0,
                "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
                "txHash": "0x22db70a26130f2fe4758fd65bf016949c1c46f5642b4a556f8a0ff7a41350898",
                "stateHash": "0xc34a7713273f1ac2256355c754b6c8ed9b9c726b84d16866ccaa4b7df29d7cc8",
                "height": 0,
                "blockTime": 1514533394,
                "txCount": 1,
                "hash": "0xc8799befacd9709a5f4dfa68d6fd53a37ccce83352997a132adfed7f46747757",
                "difficulty": 0
            }
        },
        {
            "addr": "10.0.0.2",
            "port": 13802,
            "name": "02d3f515ff59216cb7ccfbed29c1f63346c3e75c7d9eb77387cf8a5d49c2ca1f1a",
            "mempoolSize": 0,
            "self": false,
            "header": {
                "version": 0,
                "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
                "txHash": "0x22db70a26130f2fe4758fd65bf016949c1c46f5642b4a556f8a0ff7a41350898",
                "stateHash": "0xc34a7713273f1ac2256355c754b6c8ed9b9c726b84d16866ccaa4b7df29d7cc8",
                "height": 0,
                "blockTime": 1514533394,
                "txCount": 1,
                "hash": "0xc8799befacd9709a5f4dfa68d6fd53a37ccce83352997a132adfed7f46747757",
                "difficulty": 0
            }
        },
        {
            "addr": "10.0.0.3",
            "port": 13802,
            "name": "02d6a5442db7adcb455e100643525bf14ccdc4f726f0871553646fd1f50fafe5d7",
            "mempoolSize": 0,
            "self": true,
            "header": {
                "version": 0,
                "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
                "txHash": "0x22db70a26130f2fe4758fd65bf016949c1c46f5642b4a556f8a0ff7a41350898",
                "stateHash": "0xc34a7713273f1ac2256355c754b6c8ed9b9c726b84d16866ccaa4b7df29d7cc8",
                "height": 0,
                "blockTime": 1514533394,
                "txCount": 1,
                "hash": "0xc8799befacd9709a5f4dfa68d6fd53a37ccce83352997a132adfed7f46747757",
                "difficulty": 0
            }
        },
        {
            "addr": "10.0.0.4",
            "port": 13802,
            "name": "02d6a5442db7adcb455e100643525bf14ccdc4f726f0871553646fd1f50fafe5d7",
            "mempoolSize": 0,
            "self": true,
            "header": {
                "version": 0,
                "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
                "txHash": "0x22db70a26130f2fe4758fd65bf016949c1c46f5642b4a556f8a0ff7a41350898",
                "stateHash": "0xc34a7713273f1ac2256355c754b6c8ed9b9c726b84d16866ccaa4b7df29d7cc8",
                "height": 0,
                "blockTime": 1514533394,
                "txCount": 1,
                "hash": "0xc8799befacd9709a5f4dfa68d6fd53a37ccce83352997a132adfed7f46747757",
                "difficulty": 0
            }
        }
    ]
}
```

如果执行出错，请检查`logs/chain33.log`文件中的错误信息。

## 管理节点

Chain33联盟链节点有两种角色，一种是验证节点，一种是共识节点。

- 验证节点：只同步区块，广播交易但不参与共识。
- 共识节点：参与共识的节点。
  验证节点可以经由全网共识后变成共识节点，同时共识节点也可以由全网共识后退化成验证节点。

### 添加验证节点

1.在其中一台联盟链节点服务器的节点目录下，将`chain33`、`chain33-cli`、`chain33.toml`和`genesis.json（公钥文件）`拷贝到新节点任意目录上 （chain33.toml和genesis.json不要做任何修改）。

```
#假设拷贝到新节点172.27.0.16的“/root/fold”目录下。
scp -r ./chain33 chain33-cli chain33.toml genesis.json root@172.27.0.16:/root/fold
```

2.登录新节点服务器，进入指定目录，然后生成私钥文件。

```
# 生成私钥文件，-n：生成数量，-t：签名算法，默认生成私钥文件“priv_validator_0.json”。
./chain33-cli qbft gen_file -n 1 -t bls
#将“priv_validator_0.json”重命名为“priv_validator.json”。
MV priv_validator_0.json priv_validator.json
```

3.执行启动命令，运行新节点。

```
nohup ./chain33 -f chain33.toml >> qbft.out&
```

4.执行命令，查询p2p节点信息。
添加新节点成功后，可以查询到5个节点的信息。

```
./chain33-cli net peer
```

### 变更节点角色

**1.设置qbft-manager账户**
查询是否已有[添加或者删除节点权限]的账户qbft-manager。
如果有，无序执行后续步骤；如果没有，在原有节点(因为新节点可能还没有同步到最大高度，所以最好在原节点上执行)上执行后续步骤。

```
./chain33-cli config query -k qbft-manager
# 输出样例
{
  "key": "qbft-manager"
}
```
在原有节点服务器的节点目录下，执行以下步骤，设置qbft-manager账户。

```
# 配置qbft-manager。-c：qbft-manager，-o：add(表示添加)，-v：qbft-manager的地址，与配置文件中的superManager地址相同。./chain33-cli config config_tx -c qbft-manager -o add -v 1CbEVT9RnM5oZhWMj4fxUrJX94VtRotzvs# 签名交易。 -k 链的supermanager的私钥。./chain33-cli  wallet sign -k 0xdb9415dfbd54ed84dccde80a0f9f2497c7b967116da92d8682900b324ea33d68 -d 上一步返回的结果./chain33-cli  wallet send -d 上一步返回的结果# 查询qbft-manager账户。./chain33-cli config query -k qbft-manager# 输出样例。{  "key": "qbft-manager",  "value": "[1CbEVT9RnM5oZhWMj4fxUrJX94VtRotzvs]"}
```

**2.验证节点变为共识节点**
将新节点的公钥通过交易发送到链上，由全网所有共识节点进行共识。

```
# 发送交易，添加共识节点。-p：新节点的公钥，-w：节点权重，不超过原有节点权重之和的1/3，-k：qbft-manager账户私钥，即superManager账户私钥。
./chain33-cli send qbft add -p F4E29F6C49D4BE13A9271C0E66D96413C64A0D6C -w 10 -k 3990969DF92A5914F7B71EEB9A4E58D6E255F32BF042FEA5318FC8B3D50EE6E8
# 查询当前共识节点数目，检查是否存在新增的节点。
./chain33-cli qbft nodes
```

**3.共识节点变为验证节点**
将要删除的共识节点的公钥通过交易发送到链上，由全网所有共识节点进行共识。

```
# 发送交易，删除共识节点。-p：删除节点的公钥，-w：节点权重，0表示删除，-k：qbft-manager账户私钥，即superManager账户私钥。
./chain33-cli send qbft add -p F4E29F6C49D4BE13A9271C0E66D96413C64A0D6C -w 0 -k 3990969DF92A5914F7B71EEB9A4E58D6E255F32BF042FEA5318FC8B3D50EE6E8
# 查询当前共识节点数目
./chain33-cli qbft nodes
```

