Parity搭建以太坊私链

与前文使用Geth搭建Ethereum以太坊私链不同,这次搭建以太坊私链用的是Parity.

CentOS系统可能会出现GLIBCXX版本不匹配的问题,建议使用Ubuntu18.04。

步骤

1. Parity下载

Parity Github Releases下载Parity,本文使用的是 2.7.2版本。

下载完成后

chmod +x parity
# 查看版本号
parity --version

2. spec配置

创建chain spec demo-spec.json

{
    "name": "DemoPoA",
    "engine": {
        "authorityRound": {
            "params": {
                "stepDuration": "2",
                "validators" : {
                    "list": []                
                }
            }
        }
    },
    "params": {
    "gasLimitBoundDivisor": "0x400",
        "maximumExtraDataSize": "0x20",
        "minGasLimit": "0x1388",
        "networkID" : "0x2323"
    },
    "genesis": {
        "seal": {
            "authorityRound": {
                "step": "0x0",
                "signature": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
            }
        },
        "difficulty": "0x10000",
        "gasLimit": "0x12a05f200"
    },
    "accounts": {
        "0x0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
        "0x0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
        "0x0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
        "0x0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }
    } 

}

stepDuration 设定成5秒产生一个区块。
validators 设定Authority的地方,目前先空著,后面创建account之后再回来填入。

3. 节点配置

node0使用 node0.toml

[parity]
chain = "demo-spec.json"
base_path = "parity0"
[network]
port = 30300
[rpc]
port = 8540
apis = ["web3", "eth", "net", "personal", "parity", "parity_set", "traces", "rpc", "parity_accounts"]
[ui]
port = 8180
[websockets]
port = 8450

node1 使用如下配置文件 node1.toml:

[parity]
chain = "demo-spec.json"
base_path = "parity1"
[network]
port = 30301
[rpc]
port = 8541
apis = ["web3", "eth", "net", "personal", "parity", "parity_set", "traces", "rpc", "parity_accounts"]
[ui]
port = 8181
[websockets]
port = 8451
[ipc]
disable = true

4. 账户创建

我们将创建三个帐户:两个权限和一个用户帐户。这里只记录两种:
方法:使用RPC
使用启动节点0 parity --config node0.toml

RPC可以通过访问web3,parity.js或简单地使用curl。
这将创建第一个权限地址:

curl --data '{"jsonrpc":"2.0","method":"parity_newAccountFromPhrase","params":["node0", "node0"],"id":0}' -H "Content-Type: application/json" -X POST localhost:8540

返回的地址应该是0x00bd138abd70e2f00903268f3db08f2d25677c9e。
用户地址:

curl --data '{"jsonrpc":"2.0","method":"parity_newAccountFromPhrase","params":["user", "user"],"id":0}' -H "Content-Type: application/json" -X POST localhost:8540

返回的地址应该是0x004ec07d2329997267ec62b4166639513386f32e。
现在启动另一个节点 parity --config node1.toml
并创建第二个权限帐户:

curl --data '{"jsonrpc":"2.0","method":"parity_newAccountFromPhrase","params":["node1", "node1"],"id":0}' -H "Content-Type: application/json" -X POST localhost:8541

返回的地址应该是0x00aa39d30f0d20ff03a22ccfc30b7efbfca597c2。

现在可以将帐户添加到spec文件中。打开demo-spec.json备份并将我们刚创建的权限添加到"validators"数组中:

"validators" : {
"list": [
"0x00Bd138aBD70e2F00903268F3Db08f2D25677C9e",
"0x00Aa39d30F0D20FF03a22cCfc30B7EfbFca597C2"
]
}

然后将我们的用户帐户添加到spec的accounts中,以便我们有一些余额可以发送:

"0x004ec07d2329997267ec62b4166639513386f32e": { "balance": "10000000000000000000000" }

5. 运行授权节点

现在规范已经完成,我们可以启动将运行链的两个节点。要将节点作为权限运行,我们需要启用它来签署共识消息。
首先,让我们用节点密码创建一个文件node.pwds。每行将包含我们在创建权限帐户时使用的密码,要存储在名为node.pwds的文件中的内容为:

node0
node1

现在我们可以添加engine-signer到配置文件中。
node0.toml:

[parity]
chain = "demo-spec.json"
base_path = "parity0"
[network]
port = 30300
[rpc]
port = 8540
apis = ["web3", "eth", "net", "personal", "parity", "parity_set", "traces", "rpc", "parity_accounts"]
[ui]
port = 8180
[websockets]
port = 8450
[account]
password = ["node.pwds"]
[mining]
engine_signer = "0x00Bd138aBD70e2F00903268F3Db08f2D25677C9e"
reseal_on_txs = "none"

并且node1.toml:

[parity]
chain = "demo-spec.json"
base_path = "parity1"
[network]
port = 30301
[rpc]
port = 8541
apis = ["web3", "eth", "net", "personal", "parity", "parity_set", "traces", "rpc", "parity_accounts"]
[ui]
port = 8181
[websockets]
port = 8451
[ipc]
disable = true
[account]
password = ["node.pwds"]
[mining]
engine_signer = "0x00Aa39d30F0D20FF03a22cCfc30B7EfbFca597C2"
reseal_on_txs = "none"

重新启动两个节点

6. 添加节点

首选获取节点0信息

  1. 通过控制台输出
  2. RPC
    curl --data '{"jsonrpc":"2.0","method":"parity_enode","params":[],"id":0}' -H "Content-Type: application/json" -X POST localhost:8540

将“结果”添加到节点1(enode://RESULT在命令中替换):

curl --data '{"jsonrpc":"2.0","method":"parity_addReservedPeer","params":["enode://RESULT"],"id":0}' -H "Content-Type: application/json" -X POST localhost:8541

7. 静态节点

Parity也可实现类似Geth的静态节点。

把节点添加到一个文件,比如 myPrivateNetwork.txt, 一行一条:

enode://0000..0001@192.168.0.101:36541
enode://0000..0002@192.168.0.102:36542
enode://0000..0003@192.168.0.103:36543
enode://0000..0004@192.168.0.104:36544
enode://0000..0005@192.168.0.105:36545

接下来,运行Parity带上以下参数 --reserved-peers myPrivateNetwork.txt --reserved-only. 或者把它添加到toml文件

[parity]
chain = "myGenesis.json"

[network]
id = 13337
reserved_only = true
reserved_peers = "./myPrivateNetwork.txt"

7. 交易测试

curl --data '{"jsonrpc":"2.0","method":"personal_sendTransaction","params":[{"from":"0x004ec07d2329997267Ec62b4166639513386F32E","to":"0x00Bd138aBD70e2F00903268F3Db08f2D25677C9e","value":"0xde0b6b3a7640000"}, "user"],"id":0}' -H "Content-Type: application/json" -X POST localhost:8540

过几秒就会被挖出。

8. 一般性节点

现在可以创建更多帐户,发送价值,编写合同并进行部署。用于开发和使用以太坊网络的所有工具也可用于此网络。
要在多台计算机上部署Parity,您可能会发现docker构建很有用。
要添加非授权节点,可以使用以下更简单的配置:

[parity]
chain = "demo-spec.json"
base_path = "/tmp/parity2"
[network]
port = 30302
[rpc]
port = 8542
apis = ["web3", "eth", "net", "personal", "parity", "parity_set", "traces", "rpc", "parity_accounts"]
[ui]
port = 8182
[websockets]
port = 8452
[ipc]
disable = true

然后,帐户和连接节点可以与权限节点相同。为了确保接受事务,权限也可以在usd_per_tx = "0" 字段下运行[mining]。任何提交交易的节点都可以免费提供。

9. 动态管理验证节点

使用合约动态管理验证节点,合约代码

pragma solidity ^0.4.21;
contract ValidatorSet {
    event InitiateChange(bytes32 indexed _parent_hash, address[] _new_set);
    function getValidators() public constant returns (address[] _validators);
    function finalizeChange() public;
}
contract MajorityList is ValidatorSet {
    event ChangeFinalized(address[] current_set);
    struct ValidatorStatus {
        bool isValidator;
        uint index;
    }
    address SYSTEM_ADDRESS = 0x0000000000000000000000000000000000000000;
    address[] public validatorsList;
    address[] pendingList;
    bool finalized;
    mapping(address => ValidatorStatus) validatorsStatus;
    bool private initialized;
    function MajorityList() public {
        pendingList = [0x00bd138abd70e2f00903268f3db08f2d25677c9e];
        initializeValidators();
    }
    modifier uninitialized() {
        if (initialized) { revert();}
        _;
    }
    modifier when_finalized() {
        if (!finalized) { revert();}
        _;
    }
    modifier only_system_and_not_finalized() {
        if (msg.sender != SYSTEM_ADDRESS || finalized) { revert(); }
        _;
    }
    modifier is_validator(address someone) {
        if (validatorsStatus[someone].isValidator) { _; }
    }
    modifier is_not_validator(address someone) {
        if (!validatorsStatus[someone].isValidator) { _; }
    }
    function initializeValidators() public uninitialized {
        for (uint j = 0; j < pendingList.length; j++) {
            address validator = pendingList[j];
            validatorsStatus[validator] = ValidatorStatus({
                isValidator: true,
                index: j
            });
        }
        initialized = true;
        validatorsList = pendingList;
        finalized = false;
    }
    function initiateChange() private when_finalized {
        finalized = false;
        emit InitiateChange(block.blockhash(block.number - 1), pendingList);
    }
    function finalizeChange() public only_system_and_not_finalized {
        validatorsList = pendingList;
        finalized = true;
        emit ChangeFinalized(validatorsList);
    }
    function addValidator(address validator) public is_not_validator(validator){
        validatorsStatus[validator].index = pendingList.length;
        pendingList.push(validator);
        validatorsStatus[validator].isValidator = true;
        initiateChange();
    }
    function removeValidator(address validator) public is_validator(validator){
        uint removedIndex = validatorsStatus[validator].index;
        uint lastIndex = pendingList.length-1;
        address lastValidator = pendingList[lastIndex];

        pendingList[removedIndex] = lastValidator;
        validatorsStatus[lastValidator].index = removedIndex;

        delete pendingList[lastIndex];
        pendingList.length--;

        validatorsStatus[validator].index = 0;
        validatorsStatus[validator].isValidator = false;
        initiateChange();

    }
    function getValidators() public constant returns (address[]) {
        return validatorsList;
    }
}

通过Remix编译成功后,查看编译详情,Bytecode中的object即是合约的二进制代码,实际使用时要加上 0x

spec修改如下:

"validators": {
          "safeContract":"0x0000000000000000000000000000000000000005"
        }

accounts处添加

"0x0000000000000000000000000000000000000005":{
      "balance": "1",
      "constructor":"0x..."
    }

constructor处添加的是合约的二进制代码。

注意点

  1. 子节点必须使用和主节点相同的 spec.json,不然连不上主节点!
  2. validators可以动态配置也可静态配置。静态配置最为简单,动态配置还存在一些问题。

Leave a Comment

豫ICP备19001387号-1