1. 准备要求

目前超级链节点主要运行在linux和mac上,windows不能运行超级链节点. C++合约在ARM上编译还有点问题,可以先在x86平台上编译成wasm,然后拷贝到ARM发布合约
go >= 1.12.x && <= 1.13.x
g++ >= 4.8.2 或者 clang++ >= 3.3
Docker
git

1
2
3
4
5
6
7
8
9
###设置golang的国内代理###
go env -w GOPROXY=https://goproxy.cn,direct
##这些环境变量有助于我们更方便的执行一些命令而不用指定命令的全路径.
export PATH=/data/xuperchain/xuperchain/output:$PATH
export XDEV_ROOT=/data/xuperchain/xuperchain/core/contractsdk/cpp

git clone https://github.com/xuperchain/xuperchain.git  xuperchain  
cd xuperchain && make

2. 启动超级链

1
2
3
4
5
cd output
## 首先创建链
./xchain-cli createChain
## 后台启动xuperchain节点
nohup ./xchain &

3. 创建合约账号

合约账号用来进行合约管理,比如合约的权限控制等,要部署合约必须创建合约账号,同时合约账号里面需要有充足的xuper来部署合约.
创建合约账号XC1111111111111111@xuper

1
2
3
./xchain-cli account new --account 1111111111111111 --fee 2000
##给合约账号转账
./xchain-cli transfer --to XC1111111111111111@xuper --amount 10000000000

4. 合约演示

4.1 C++合约

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
##xdev提供了一个默认的c++合约工程模板
./xdev init hello-cpp
##编译合约,第一次编译的时间会长一点,因为xdev需要下载编译器镜像,以及编译超级链的标准库.
cd hello-cpp
../xdev build -o hello.wasm

##编译结果是hello.wasm,部署这个合约
cd ..
./xchain-cli wasm deploy --account XC1111111111111111@xuper --cname hello  --fee 5200000 --runtime c ./hello-cpp/hello.wasm  -H 127.0.0.1:37101
##调用合约
./xchain-cli wasm invoke --method hello --fee 110000 hello  -H 127.0.0.1:37101

4.2.golang合约

1
2
3
4
5
6
7
8
##编译超级链的golang版本要和开发合约的golang版本环境保持一致
GOOS=js GOARCH=wasm go build -o counter.wasm
##合约部署,目前这一步长时间卡顿,未获取到正确的结果
./xchain-cli wasm deploy --account XC1111111111111111@xuper --cname counter  --fee 5800000 -a '{"creator":"creator"}'  --runtime go ./counter.wasm  -H 127.0.0.1:37101

##调用合约
./xchain-cli wasm invoke --method Increase -a '{"key":"stones"}' --fee 110000 counter  -H 127.0.0.1:37101
./xchain-cli wasm invoke --method Get  -a '{"key":"stones"}' --fee 110000 counter  -H 127.0.0.1:37101

4.3.java合约

需要在xchain.yaml 中开启native支持.依赖docker服务,需要把docker也启动了!!!

1
2
3
4
5
6
7
8
9
##参照例子: https://github.com/xuperchain/xuperchain/tree/master/core/contractsdk/java/example/counter
##部署java native合约
./xchain-cli native deploy --account XC1111111111111111@xuper --fee 5200000 --runtime java counter-0.1.0-jar-with-dependencies.jar --cname javacounter  -H 127.0.0.1:37101

##调用合约
##给key stones 设置数据,increase 和 get 是Counter.java中的方法名称
./xchain-cli native invoke --method increase -a '{"key":"stones"}' --fee 1000 javacounter  -H 127.0.0.1:37101
./xchain-cli native invoke --method get -a '{"key":"stones"}' --fee 1000 javacounter  -H 127.0.0.1:37101

4.4.Solidity合约

需要在xchain.yaml 中开启evm支持

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
##参照例子: https://github.com/xuperchain/xuperchain/tree/master/core/contractsdk/evm/example/counter

##编译合约,通过solc编译合约源码,合约二进制文件和abi文件分别存放在当前目录下,Counter.bin和Counter.abi
#--bin : 表示需要生成合约二进制文件
#--abi :表示需要生成合约abi文件,用于合约方法以及参数编解码
#-o :表示编译结果输出路径
solc --bin --abi Counter.sol -o .

##部署Solidity 合约
./xchain-cli evm deploy --account XC1111111111111111@xuper --cname counterevm --fee 5200000 Counter.bin --abi Counter.abi -H 127.0.0.1:37101


##调用合约
./xchain-cli evm invoke --method increase -a '{"key":"stones"}' counterevm --fee 22787517 --abi Counter.abi -H 127.0.0.1:37101
./xchain-cli evm query --method get -a '{"key":"stones"}' counterevm --abi Counter.abi --abi Counter.abi -H 127.0.0.1:37101 

5.XPOA共识搭建联盟链

XPOA比较适合联盟链/私链,XPOS/TDPOS比较适合公链.xuperchian v3.10 版本正式支持了XPOA共识算法.

5.1.p2p网络配置

以搭建3节点网络为例,拷贝xuperchain编译产出的output到node1~node3.每个节点需修改配置文件 conf/xchain.yaml 中p2p一节,使用p2pv1,p2pv1是为许可链设计的p2p网络插件.

 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
39
40
41
42
43
44
45
46
##日志配置
log:
  ##日志等级
  level: error

##指定使用 gm 加密,需要配合 ./data/config/xuper.json 中的 "crypto": "gm" . 也可以在执行xchain-cli命令时加上  --cryptotype gm
##生成的address是和cryptotype加密方式绑定的,如果更换了加密方式,就需要重新生成address
cryptotype: "gm"

##ixvm参数是选择ixvm合约虚拟机,使用ixvm虚拟机能加快合约部署. 也可以使用 ./xchain --vm ixvm 命令参数.如果使用golang的wasm,需要开启
#vm: "ixvm"


##RPC 服务暴露的端口
tcpServer:
  ##node2,node3可以分别设置为37102,37103
  port: :37101
  ##prometheus监控指标端口, 为空的话就不启动,node2,node3可以分别设置为37202,37203
  metricPort: :37201

p2p:
  ## p2pv1是为许可链设计的p2p网络插件,XPOA共识算法需要使用p2pv1
  module: p2pv1
  ## port是节点p2p网络监听的默认端口,如果在一台机器上部署注意端口配置不要冲突,
  ## node1配置的是47101,node2,node3可以分别设置为47102,47103
  port: 47101
  ## 不使用证书
  isUseCert: false
  #isTls: true
  ## 配置网络中所有节点的neturl, 格式ip:port, 也加上本节点的neturl
  ## 注意,如果节点分布在不同的机器之上,需要把网络地址中的本地ip改为机器的实际ip.
  staticNodes:
    xuper:
     - "192.168.0.10:47101"
     - "192.168.0.10:47102"
     - "192.168.0.10:47103"

##运行native合约的配置,配置文件可以是docker:false,但是服务器需要启动docker服务,不要问我是怎么知道的的!!!!
native:
  enable: true

##运行Solidity/evm合约的配置
evm:
  driver: "evm"
  enable: true

复制得到node2和node3节点,并依次修改三个端口37101,37201,47101

5.2.更新各节点的keys

由于节点目录下的keys都是默认的,node1保持不变,更新node2.node3的keys.更新前需手动删掉data/keys目录.更新keys命令如下:

 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
cd node1
rm -rf ./data/keys ./data/netkeys
##生成的address是和cryptotype加密方式绑定的,如果更换了加密方式,就需要重新生成address
./xchain-cli account newkeys
##重新生成本地节点的网络私钥
./xchain-cli netURL gen
##显示本地节点的p2p地址
#./xchain-cli netURL preview     
cd ../node2
rm -rf ./data/keys ./data/netkeys
##生成的address是和cryptotype加密方式绑定的,如果更换了加密方式,就需要重新生成address
./xchain-cli account newkeys
##重新生成本地节点的网络私钥
./xchain-cli netURL gen
##显示本地节点的p2p地址
#./xchain-cli netURL preview 
cd ../node3
rm -rf ./data/keys ./data/netkeys
##生成的address是和cryptotype加密方式绑定的,如果更换了加密方式,就需要重新生成address
./xchain-cli account newkeys
##重新生成本地节点的网络私钥
./xchain-cli netURL gen
##显示本地节点的p2p地址
#./xchain-cli netURL preview 

cd ..

5.3.使用gm和XPOA共识

1
2
3
4
5
6
7
8
## 进入数据目录
cd ./data/config
## 备份默认的配置
mv xuper.json xuper.json.bak
## 使用国密的配置
cp -rf xuper_gm.json xuper.json

## 把共识由默默人的 genesis_consensus 下的 "name": "tdpos" 修改为 "name": "xpoa"

修改 xuper.json,并复制到三个节点
注意: 拷贝配置内容到xuper.json时需去掉注释

 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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
{
    "version": "1",
    "predistribution": [
        {
            "address": "dpzuVdosQrF2kmzumhVeFQZa1aYcdgFpN",
            "quota": "100000000000000000000"
        }
    ],
    "maxblocksize": "128",
    "award": "1000000",
    "decimals": "8",
    "award_decay": {
        "height_gap": 31536000,
        "ratio": 1
    },
    "gas_price": {
        "cpu_rate": 1000,
        "mem_rate": 1000000,
        "disk_rate": 1,
        "xfee_rate": 1
    },
    "new_account_resource_amount": 1000,
    //需要 xchain.yaml 中配置 cryptotype: "gm",并生成gm类型的address
    "crypto": "gm",

    //联盟链或者私链,交易合约等不需要手续费.
    "nofee": true,

    "genesis_consensus": {
        //指定共识类型,默认是tdpos.xopa比较适合联盟链/私链,xpos/tdpos比较适合公链
        "name": "xpoa",
        "config": {
            // 声明共识的起始时间戳,建议设置为一个刚过去不久的时间戳,更新前10位
            "timestamp": "1614757973000000000",
            // 初始的提案者数量
            "proposer_num": "3",
            // 每个矿工连续出块的出块间隔
            "period": "3000",
            //每一轮内切换矿工的时间间隔,需要为period的整数倍
            "alternate_interval": "3000",
            // 切换轮时的出块价格,即下一轮第一个矿工出第一个块距离上一轮矿工出最后一个块的时间间隔,需要为period的整数倍
            "term_interval": "6000",
            // 每一轮内每个矿工轮值任期内连续出块的个数
            "block_num": "20",
            //为提名的候选人投票时,每一票单价,即一票等于多少Xuper
            "vote_unit_price": "1",
            // xpoa共识依赖的合约名称,无需修改.需要发布xpoa_validates合约
            "contract_name":"xpoa_validates",
            // xpoa共识查询候选人的合约方法,无需修改.需要发布xpoa_validates合约
            "method_name":"get_validates",
            // 指定第一轮初始矿工,矿工个数需要符合proposer_num指定的个数,所指定的初始矿工需要在网络中存在,不然系统轮到该节点出块时会没有节点出块
            // 通过查看 more ./data/keys/address 获取到address
            "init_proposer": [
                {
                    "address" : "dpzuVdosQrF2kmzumhVeFQZa1aYcdgFpN"
                    , "neturl" : "192.168.0.10:47101"
                },
                {
                    "address" : "VSML7NenZnGZgCEwtbQDKDSrPHhT5wsu6"
                    , "neturl" : "192.168.0.10:47102"
                },
                {
                    "address" : "bg3KLC3YCmvLWBCNAVHGHLfk3qeWEdoD3"
                    , "neturl" : "192.168.0.10:47103"
                }
            ],

           // "init_proposer": {
           //     "1": [
           //         "dpzuVdosQrF2kmzumhVeFQZa1aYcdgFpN"
           //     ]
           // },
           // "init_proposer_neturl": {
           //     "1": [
           //         "/ip4/192.168.0.10/tcp/47101/p2p/QmVxeNubpg1ZQjQT8W5yZC9fD7ZB1ViArwvyGUB53sqf8e"
           //     ]
           // },


            // 使用chained-bft
            "bft_config": {}
        }
    }
}

5.4.创建链并启动xchain

检查data/blockchain 目录下内容为空之后,创建链并启动所有节点.命令如下:

 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
cd ./node1
rm -rf ./data/blockchain/*
# 创建xuper链
./xchain-cli createChain
# 启动服务节点,使用ixvm虚拟机能加快合约部署
nohup ./xchain --vm ixvm &

cd ../node2
rm -rf ./data/blockchain/*
# 创建xuper链
./xchain-cli createChain
# 启动服务节点,使用ixvm虚拟机能加快合约部署
nohup ./xchain --vm ixvm &

cd ../node3
rm -rf ./data/blockchain/*
# 创建xuper链
./xchain-cli createChain
# 启动服务节点,使用ixvm虚拟机能加快合约部署
nohup ./xchain --vm ixvm &

##返回到主目录
cd ..

# check服务运行状况,修改-H后参数,可以查询每个节点状态
for ((i=1;i<=3;i++)); do
./xchain-cli status -H 127.0.0.1:3710$i |grep -i height
done

5.5.验证集合合约部署和调用

XPoA共识算法中,候选人的变更依赖"验证集合"合约,所以需要部署"验证集合"合约.通过调用合约中的add_validate方法新增候选人.del_validate方法删除候选人.update_validate方法更新候选人neturl.get_validates方法查询候选人列表.通过设置合约方法的ACL,可以限制哪些用户具有变更候选人的权限,设置方法参考设置合约方法的ACL.

5.5.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
27
28
cd node1
##创建合约账号
./xchain-cli account new --account 1111111111111111 --fee 1000 -H 127.0.0.1:37101
# 执行结果
# contract response:
#         {
#             "pm": {
#                 "rule": 1,
#                 "acceptValue": 1.0
#             },
#             "aksWeight": {
#                 "dpzuVdosQrF2kmzumhVeFQZa1aYcdgFpN": 1.0
#             }
#         }

# The gas you cousume is: 1000
# The fee you pay is: 1000
# Tx id: eb9924c85a16d72f5daf6e6feabb130ef9c8a3ce8f507db08dcb726111aef74f
# account name: XC1111111111111111@xuper

# 给合约账号转账
./xchain-cli transfer --to XC1111111111111111@xuper --amount 1000000000000000 -H 127.0.0.1:37101

##查询账号
./xchain-cli acl query --account XC1111111111111111@xuper -H 127.0.0.1:37101

# 执行结果
# ec6fa53446a8c6ab0d8d45f2bba80c7e5122341ce9b0c85779f80ce1a55f37b6

5.5.2.发布xpoa_validates合约

“验证集合"合约源码位于core/contractsdk/cpp/example/xpoa_validates,执行如下命令编译合约,编译结果为xpoa_validates.wasm
C++合约在ARM上编译还有点问题,可以先在x86平台上编译成wasm,然后拷贝到ARM发布合约

 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
# data/xuperchain/xuperchain是xuperchain源码所在目录,设定环境变量
export XDEV_ROOT=/data/xuperchain/xuperchain/core/contractsdk/cpp
# 编译合约
cd /data/xuperchain/xuperchain/core/contractsdk/cpp/example/xpoa_validates
xdev build

##部署合约,并设置node1.node2为初始候选人
#wasm deploy:部署wasm合约
#-account XC1111111111111111@xuper:此为部署wasm合约的账号
#-cname xpoa_validates :合约名称,需与xuper.json中配置的contract_name参数一致
#-arg :此为传入合约的参数,这里设置初始矿工,所指定的初始矿工需要在网络中存在,多个矿工用分号间隔,且address与netrul要 一一对应.
#./xpoa_validates.wasm :是编译合约产出的文件
./xchain-cli wasm deploy --account XC1111111111111111@xuper --cname xpoa_validates --arg '{"addresss":"dpzuVdosQrF2kmzumhVeFQZa1aYcdgFpN;VSML7NenZnGZgCEwtbQDKDSrPHhT5wsu6","neturls":"127.0.0.1:47101;127.0.0.1:47102"}' ./xpoa_validates.wasm --fee 300000 -H 127.0.0.1:37101


##查看候选人
#wasm invoke:调用合约
#-method get_validates:调用get_validates方法
./xchain-cli wasm invoke xpoa_validates --method get_validates --fee 1000 -H 127.0.0.1:37101


##增加候选人
#wasm invoke:调用合约
#-method add_validate:调用add_validate方法
#-args:传入的参数,填写待添加候选人的address和neturl
./xchain-cli wasm invoke xpoa_validates --method add_validate --args '{"address":"bg3KLC3YCmvLWBCNAVHGHLfk3qeWEdoD3","neturl":"127.0.0.1:47103"}' --fee 1000 -H 127.0.0.1:37101


##更新候选人
#候选人的netrul发生变化后,需要更新.以更新node3的neturl为例,比如更新为localhost:47103.修改后等待1分钟,调查看候选人命令,查看是否修改成功.
./xchain-cli wasm invoke xpoa_validates --method update_validate -a '{"address":"bg3KLC3YCmvLWBCNAVHGHLfk3qeWEdoD3","neturl":"localhost:47103"}' --fee 300 -H 127.0.0.1:37101

5.5.3.发布Java合约

参照:https://gitee.com/chunanyong/xuper-demo-java-counter
项目打包 xuper-demo-java-counter-0.1.0-jar-with-dependencies.jar

1
2
3
4
5
6
7
8
9
cd node1
## 部署合约
./xchain-cli native deploy --account XC1111111111111111@xuper  --runtime java xuper-demo-java-counter-0.1.0-jar-with-dependencies.jar --cname javacounter --fee 20000000 -H 127.0.0.1:37101

##调用合约
##给key stones 设置数据,increase 和 get 是Counter.java中的方法名称
./xchain-cli native invoke --method increase -a '{"key":"stones"}' javacounter --fee 100 -H 127.0.0.1:37101
./xchain-cli native invoke --method get -a '{"key":"stones"}' javacounter --fee 100 -H 127.0.0.1:37101

6.账户权限

6.1.个人账户(AK)

个人账号(AK)其实是一组公私钥对,个人帐号地址(address)是根据公钥经过一定规则导出的一个散列值.个人账号可以离线生成,不需要上链,只有在个人账号产生测试资源变动时(例如转入了一部分测试资源)才会在UTXO中产生记录
在data/keys下会有一个默认的个人账号(AK),包括address(你的地址).private.key(你的私钥).public.key(你的公钥),建议按照如下命令重新生成一个独有的个人账号.

1
2
3
4
5
#指定私钥目录: 在data/test_demo下生成address.private.key.public.key: 
./xchain-cli account newkeys --output data/test_demo
#覆盖默认目录: 覆盖data/keys下的文件,需要先删除data/keys目录,然后重新生成新的address.private.key.public.key
rm -r data/keys
./xchain-cli account newkeys

个人账号地址默认在data/keys/address文件中,可通过 more data/keys/address 查看自己的个人账号地址.

6.2.创建合约账号(Account)

如果把合约账号当作一家股份制公司,那么ACL便是公司股东投票的机制,ACL可以规定合约账号背后各"股东"账号的权重,只有当"股东(AK)“签名的权重之和大于设定阈值时操作才会有效地进行.

合约账户可以看作是AK的权重集合,主要是控制合约的发布,也可以控制对合约方法的调用限制.

合约账号可以用来部署智能合约,创建合约账号是一个上链操作,因此也需要消耗一定量的测试资源.合约账号可以设置为多个个人账号共同持有,只有一个交易中的背书签名满足一定合约账号的ACL要求,才能代表这个合约账号进行操作.关于合约账号和ACL权限相关的内容,可以参考

注意:
multisig最终合入签名时需要将签名顺序与 data/acl/addrs 里面的地址顺序保持一致,否则会签名校验失败.
ACL配置格式如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
    "pm": {
        "rule": 1,              # rule=1表示签名阈值策略,rule=2表示AKSet签名策略
        "acceptValue": 0.6      # acceptValue为签名需达到的阈值
    },
    "aksWeight": {              # aksWeight里规定了每个地址对应账号签名的权重
        "AK1": 0.3,
        "AK2": 0.3
    }
}

1.创建合约账号是一个系统合约,可以通过多重签名的方式发起系统合约调用.系统合约调用需要先创建一个合约调用描述文件,例如下面account.des是一个创建合约账号的描述文件. account.des文件内容:

1
2
3
4
5
6
7
8
9
{
    "module_name": "xkernel",
    "method_name": "NewAccount",
    "args" : {
        "account_name": "1111111111111111", ##说明:账号名称是16位数字组成的字符串
        ##acl 中的内容注意转义 
        "acl": "{\"pm\": {\"rule\": 1,\"acceptValue\": 1},\"aksWeight\": {\"dpzuVdosQrF2kmzumhVeFQZa1aYcdgFpN\": 1}}"
    }
}
1
2
3
4
5
6
##创建合约账户
./xchain-cli account new --desc account.des

##除了上述方法,我们还提供了一个比较简易的方式来创建合约账号,命令如下:
./xchain-cli account new --account 1111111111111111 # 16位数字组成的字符串
#上述命令也会创建一个名为 XC1111111111111111@xuper 的账号,由于我们没有制定ACL的具体内容,其ACL被赋值为默认状态,即背后有权限的账号只有当前节点上默认账号一个(地址默认位于 data/keys/address)

注意:创建合约账号的操作需要提供手续费,需要按照命令行运行结果给出的数值,添加一个不小于它的费用(使用 -fee 参数)

6.3.查询合约账号ACL

1
2
3
4
5
##查询账号
./xchain-cli acl query --account XC1111111111111111@xuper # account参数为合约账号名称

#查询账号余额,合约账号查询余额和普通账号类似,只是命令行的参数有些许变化
./xchain-cli account balance XC1111111111111111@xuper -H 127.0.0.1:37101

6.4.修改合约账户ACL

修改ACL的配置和创建账号的配置类似

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
    "module_name": "xkernel",
    "method_name": "SetAccountAcl",  ##这里的方法有了变更
    "args" : {
        "account_name": "XC1111111111111111@xuper", ##account_name在此处一定要写成XC.....@xuper的形式
        ##acl字段为要修改成的新ACL
        "acl": "{\"pm\": {\"rule\": 1,\"acceptValue\": 0.6},\"aksWeight\": {\"AK3\": 0.3,\"AK4\": 0.3}}"
    }
}

修改ACL的操作,需要符合当前ACL中设置的规则,即需要具有足够权重的账号签名.
需要新建文件添加需要签名的地址,默认acl文件路径是:./data/acl/addrs 示例:

1
2
XC9999999999999999@xuper/9LArZSMrrRorV7T6h5T32PVUrmdcYLbug 
XC9999999999999999@xuper/gLAdZSMtkforV7T6h5TA14VUrfdcYLbuy  
1
2
3
4
5
6
7
8
9
##先生成一个多重签名的交易 
./xchain-cli multisig gen --desc acl_new.json --from XC1111111111111111@xuper

##这样就会生成一个默认为`tx.out`的文件,之后使用原ACL中的账号对其进行签名
./xchain-cli multisig sign --keys data/account/AK1 --output AK1.sign
./xchain-cli multisig sign --keys data/account/AK2 --output AK2.sign

##最后把生成的`tx.out`发出去,至此便完成了ACL的修改
./xchain-cli multisig send --tx tx.out AK1.sign,AK2.sign AK1.sign,AK2.sign

7.平行链与群组(待完善)

8.合约和SDK

得益于XuperChain独创的XuperBridge的架构设计,支持native/docker,wasm,evm运行方式,特别是对

8.1.合约

目前超级链的智能合约可以使用solidity.c++.go以及 java语言来编写,solidity为EVM合约,c++和go 支持 wasm合约,go和java支持native合约.solidity合约应用最为广泛,完美兼容以太坊开源社区以及相关开发工具,c++合约合约性能会更好些,go合约在易用性上更好,java合约的开发者会更多些.

理论上所有可以编译成wasm格式的开发语言,都可以编写合约.

java合约例子: https://gitee.com/chunanyong/xuper-demo-java-counter
golang合约例子: https://github.com/xuperchain/contract-sdk-go

8.2.SDK

9.K8S部署

9.1.制作镜像

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
mkdir ./docker
cd ./docker

##增加xpoa_validates.wasm用于xpoa共识
cp -rf ./xpoa_validates.wasm ./xuperchain/

wget https://cms-store.bj.bcebos.com/xuperchain-linux-amd64-v3.10.tar.gz
tar -zxvf xuperchain-linux-amd64-v3.10.tar.gz
cd ./xuperchain
rm -rf ./data/keys
rm -rf ./data/netkeys


##修改默认的./data/config/xuper.json,用于判断是否是第一次初始化.
mv ./data/config/xuper.json ./data/config/xuper_default.json

###增加./conf/xchain_xpoa.yaml 和 ./data/config/xuper_xpoa.json ,文件在下面会列出


### 复制目录,作为xpoa协议的初始化配置目录.如果是外挂目录,就会使用xpoa协议,目前默认xpoa国密,后期可以设置环境变量
cp -rf ./conf ./conf_xpoa
cp -rf ./data ./data_xpoa

./conf/xchain_xpoa.yaml改动的配置

 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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# node config
# 日志配置
log:
  # 模块名称
  module: xchain
  # 日志目录
  filepath: logs
  # 日志文件名
  filename: xchain
  fmt: logfmt
  # 是否打印命令行工具端口
  console: true
  # 修改
  # 日志等级
  level: error

## 修改
##指定使用 gm 加密,需要配合 ./data/config/xuper.json 中的 "crypto": "gm" . 也可以在执行xchain-cli命令时加上  --cryptotype gm
##生成的address是和cryptotype加密方式绑定的,如果更换了加密方式,就需要重新生成address
cryptotype: "gm"


# 区块链节点配置
p2p:
  ## 修改
  # module is the name of p2p module plugin, value is [p2pv2/p2pv1], default is p2pv2
  module: p2pv1
  port: 47101
  ## 不使用证书
  isUseCert: false
  #isTls: true
  ## 修改
  ## 配置网络中所有节点的neturl, 格式ip:port, 也加上本节点的neturl
  ## 注意,如果节点分布在不同的机器之上,需要把网络地址中的本地ip改为机器的实际ip.
  staticNodes:
    xuper:
     - "xc_node_ip1:xc_node_port1"
     - "xc_node_ip2:xc_node_port2"
     - "xc_node_ip3:xc_node_port3"

##运行Solidity/evm合约的配置
evm:
  driver: "evm"
  ## 修改
  enable: true

kernel:
  # minNewChainAmount 设置创建平行链时最少要转多少钱到同链名address
  minNewChainAmount: "100"
  ## 修改
  newChainWhiteList:
   - xc_node_address1: true
   - xc_node_address2: true
   - xc_node_address3: true 

# 合约通用配置
contract:
  ## 修改
  enableUpgrade: true


./data/config/xuper_xpoa.json

 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
39
40
41
42
43
44
45
46
47
48
49
{
    "version": "1",
    "predistribution": [
        {
            "address": "xc_node_address1",
            "quota": "100000000000000000000"
        }
    ],
    "maxblocksize": "128",
    "award": "1000000",
    "decimals": "8",
    "award_decay": {
        "height_gap": 31536000,
        "ratio": 1
    },
    "gas_price": {
        "cpu_rate": 1000,
        "mem_rate": 1000000,
        "disk_rate": 1,
        "xfee_rate": 1
    },
    "new_account_resource_amount": 1000,
    "crypto": "gm",
    "genesis_consensus": {
        "name": "xpoa",
        "config": {
            "timestamp": "1559021720000000000",
            "period": "3000",
            "block_num": "10",
            "contract_name": "xpoa_validates",
            "method_name": "get_validates",
            "init_proposer": [
                {
                    "address": "xc_node_address1",
                    "neturl": "xc_node_ip1:xc_node_port1"
                },
                {
                    "address": "xc_node_address2",
                    "neturl": "xc_node_ip2:xc_node_port2"
                },
                {
                    "address": "xc_node_address3",
                    "neturl": "xc_node_ip3:xc_node_port3"
                }
            ],
            "bft_config": {}
        }
    }
}

创建 ./xuperchain/start.sh文件

 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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#!/bin/bash
set -e

###制作镜像前将start.sh复制到xuperchain目录下,删除 rm -rf ./data/keys和 rm -rf ./data/netkeys目录,初始化生成新的AK###
###执行权限 chmod 755 ./start.sh ###
###修改权限组:chown 1001:docker ./start.sh ###


### 不存在配置目录,可以认为data目录外挂出来了,使用xpoa共识协议,进行多节点的配置.目前默认xpoa国密,后期可以设置环境变量
if [ ! -d "./data/config" ]; then
  echo "xpoa初始化配置文件,不启动服务,请使用initxpoa.sh脚本生成配置"
  cp -rf ./conf_xpoa/* ./conf/
  cp -rf ./data_xpoa/* ./data/


  ### 使用xpoa的配置文件,配置gm算法
  rm -rf ./conf/xchain.yaml
  cp -rf ./conf/xchain_xpoa.yaml ./conf/xchain.yaml

   ### 生成的address是和cryptotype加密方式绑定的,如果更换了加密方式,就需要重新生成address
  ./xchain-cli account newkeys
  ### 重新生成本地节点的网络私钥
  ./xchain-cli netURL gen

  ###循环120秒,检查是否存在 ./data/config/xuper.json,给外部命令时间生成xpoa的 xuper.json
  for i in {1..120}
  do
    ### 如果./data/config/xuper.json文件不存在,等待一秒循环
    if [ ! -e "./data/config/xuper.json" ];then
      sleep 1
    else
      ### 生成xuper主链
      ./xchain-cli createChain
      break
    fi
  done

fi

### 没有xuper.json文件,使用默认的配置文件启动
if [ ! -e "./data/config/xuper.json" ]; then
  
  cp -rf ./data/config/xuper_default.json  ./data/config/xuper.json

  ###获取新生成的address
  adress=`cat ./data/keys/address`
  netURL=`./xchain-cli netURL preview`

  defaultAddress="dpzuVdosQrF2kmzumhVeFQZa1aYcdgFpN"
  defaultNetURL="/ip4/127.0.0.1/tcp/47101/p2p/QmVxeNubpg1ZQjQT8W5yZC9fD7ZB1ViArwvyGUB53sqf8e"
  ###替换默认值
  sed -i "s#${defaultAddress}#${adress}#g" ./data/config/xuper.json ./conf/xchain.yaml
  sed -i "s#${defaultNetURL}#${netURL}#g" ./data/config/xuper.json ./conf/xchain.yaml

  ### 生成的address是和cryptotype加密方式绑定的,如果更换了加密方式,就需要重新生成address
  ./xchain-cli account newkeys
  ### 重新生成本地节点的网络私钥
  ./xchain-cli netURL gen

  ### 生成xuper主链
  ./xchain-cli createChain
fi

### 启动服务节点,使用 --vm ixvm 能加快合约部署,但是执行合约不会比较慢.容器部署,不要使用nohup运行####
./xchain

创建./xuperchain/initxpoa.sh文件

 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
39
#!/bin/bash
set -e

###初始化3个节点,每个节点 nodeip,nodeport,address 三个参数.可以使用数组优化

xc_node_ip1=$1
xc_node_port1=$2
xc_node_address1=$3

xc_node_ip2=$4
xc_node_port2=$5
xc_node_address2=$6

xc_node_ip3=$7
xc_node_port3=$8
xc_node_address3=$9


### 生成新的配置 ###
sed -i "s/xc_node_ip1/${xc_node_ip1}/g" ./conf/xchain_xpoa.yaml ./data/config/xuper_xpoa.json
sed -i "s/xc_node_port1/${xc_node_port1}/g" ./conf/xchain_xpoa.yaml ./data/config/xuper_xpoa.json
sed -i "s/xc_node_address1/${xc_node_address1}/g" ./conf/xchain_xpoa.yaml ./data/config/xuper_xpoa.json

sed -i "s/xc_node_ip2/${xc_node_ip2}/g" ./conf/xchain_xpoa.yaml ./data/config/xuper_xpoa.json
sed -i "s/xc_node_port2/${xc_node_port2}/g" ./conf/xchain_xpoa.yaml ./data/config/xuper_xpoa.json
sed -i "s/xc_node_address2/${xc_node_address2}/g" ./conf/xchain_xpoa.yaml ./data/config/xuper_xpoa.json

sed -i "s/xc_node_ip3/${xc_node_ip3}/g" ./conf/xchain_xpoa.yaml ./data/config/xuper_xpoa.json
sed -i "s/xc_node_port3/${xc_node_port3}/g" ./conf/xchain_xpoa.yaml ./data/config/xuper_xpoa.json
sed -i "s/xc_node_address3/${xc_node_address3}/g" ./conf/xchain_xpoa.yaml ./data/config/xuper_xpoa.json

#adress=`cat ./data/keys/address`
#sed -i "s/xc_curr_address/${adress}/g" ./data/config/xuper_xpoa.json

###删除原有文件 ###
rm -rf ./conf/xchain.yaml ./data/config/xuper.json

mv ./conf/xchain_xpoa.yaml ./conf/xchain.yaml
mv ./data/config/xuper_xpoa.json ./data/config/xuper.json

创建 ./docker/DockerFile 文件,和 ./docker/xuperchain 目录平行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
FROM centos:8
##如果是wasm合约,需要依赖gcc环境,g++ >= 4.8.2 或者 clang++ >= 3.3,所以使用centos8方便dnf安装gcc
RUN dnf install -y wget \
    && rm -rf /etc/yum.repos.d/CentOS-Base.repo \
    && wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-8.repo \
    && dnf makecache \
    && dnf update -y \
    && dnf install -y gcc gcc-c++ \
    && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
    && echo -e "* soft nofile 1024000\n* hard nofile 1024000" >> /etc/security/limits.conf \
    && echo -e  " fs.file-max = 1024000 \n vm.swappiness = 0 \n kernel.sysrq = 1 \n net.ipv4.neigh.default.gc_stale_time = 120 \n net.ipv4.conf.all.rp_filter = 0 \n net.ipv4.conf.default.rp_filter = 0 \n net.ipv4.conf.default.arp_announce = 2 \n net.ipv4.conf.lo.arp_announce = 2 \n net.ipv4.conf.all.arp_announce = 2 \n net.ipv4.tcp_max_tw_buckets = 5000 \n net.ipv4.tcp_syncookies = 1 \n net.ipv4.tcp_max_syn_backlog = 1024 \n net.ipv4.tcp_synack_retries = 2" >> /etc/sysctl.conf

COPY . /
WORKDIR /xuperchain
EXPOSE 37101 47101 37200
ENTRYPOINT ["/bin/bash","./start.sh"]

制作镜像:

1
2
3
4
5
6
7
#进入到docker目录
cd ../

chmod 755 ./xuperchain/start.sh
chmod 755 ./xuperchain/initxpoa.sh
chown -R 1001:docker ./xuperchain
docker build -t xuperchain:3.10 -f DockerFile .

9.2.K8S部署3节点国密XPOA

编写xuperchain_init.yaml模板文件

  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
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
###可以使用 kubectl api-versions 查询支持的version###
apiVersion: v1 
### service 类型####
kind: Service
###元数据###
metadata:
  ###service的名称####
  name: xc_app_name
###具体描述###
spec:
  ###标签选择器###
  selector:
    ###标签是 nginx 的pod###
    app: xc_app_name
  ###节点类型是 NodePort ###
  type: NodePort

  ###端口列表###
  ports:
  ###http的端口说明,istio需要端口的命名为协议名(http)或者协议名加后缀(http-a)###
  - name: tcp-server
    protocol: TCP
    port: 37101
  - name: tcp-p2p
    protocol: TCP
    port: 47101   
  - name: tcp-metric
    protocol: TCP
    port: 37200
--- 
###声明 Deployment####
apiVersion: apps/v1
kind: Deployment
metadata:
  name: xc_app_name
spec:
  replicas: 1
  ###更新:容器准备完成之后,延迟60s,配合strategy.maxUnavailable: 0时,可以忽略###
  #minReadySeconds: 60
  strategy:
    ###升级方式,默认的方式###
    type: RollingUpdate 
    ###严格控制,每次升级一个pod,不可用状态为0个pod###
    rollingUpdate:
      ###滚动升级时会先启动1个pod###
      maxSurge: 1
      ###滚动升级时允许的最大不可用的pod个数###      
      maxUnavailable: 0

  selector: 
    matchLabels: 
      app: xc_app_name
  template:
    metadata:
      name: xc_app_name
      #hostname: xc_app_name
      ####禁用istio自动注入sidecar###
      annotations:
        sidecar.istio.io/inject: "false"
           
      ###istio需要app和version两个标签###
      labels: 
        app: xc_app_name
        chainname: xc_chainname
        chaintype: xuperchain
        version: v1
    spec:
      containers:
      - name: xuperchain
        image: xuperchain:3.10
        ###
        #imagePullPolicy: Always
        imagePullPolicy: IfNotPresent
        #command: ["sleep","3600"]

        env:
        ###设置时区###
        - name: TZ
          value: Asia/Shanghai 
        #- name: LC_ALL
        #  value: zh_CN.UTF-8
        - name: LANG
          value: zh_CN.UTF-8                      
                
        ports:
        - containerPort: 37101
        - containerPort: 47101
        - containerPort: 37200


        volumeMounts:
        ###挂载点###
        - mountPath: /xuperchain/data
          ###对应下面卷 volumes.name ###
          name: xuperchain-data
          ###指定子目录,会在自动创建子文件夹####
          subPath: xuperchain/xc_chainname/xc_app_name/data

        - mountPath: /xuperchain/conf
          ###对应下面卷 volumes.name ###
          name: xuperchain-data
           ###指定子目录,会在自动创建子文件夹####
          subPath: xuperchain/xc_chainname/xc_app_name/conf

      ###使用local PV,绑定node节点,不再飘移###
      volumes:
      ###卷名称,对应上面的挂载名称###
      - name: xuperchain-data
        persistentVolumeClaim:
          ###对应k8s-pvc.yaml的metadata.name###
          claimName: k8s-pvc

      ### 设置反亲和性,尽可能的不要部署到同一个node节点###
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:  # 软策略
          - weight: 50
            podAffinityTerm:
              topologyKey: kubernetes.io/hostname
              labelSelector:
                matchExpressions:
                - key: chaintype
                  operator: In
                  values:
                  - xuperchain

编写xuperchain-k8s.sh部署脚本,和xuperchain_init.yaml放到同一目录

 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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#!/bin/bash

##需要传入chainName,默认xuper,初始化3个节点.先简单可用,后期再优化
cname=$1
chainName=${cname:-"xuper"}

rm -rf ./${chainName}
mkdir ./${chainName}

###生成service,deployment和pod
for i in {1..3}
do
  cp -rf xuperchain_init.yaml ./${chainName}/${chainName}-$i.yaml
  sed -i "s#xc_chainname#${chainName}#g" ./${chainName}/${chainName}-$i.yaml
  sed -i "s#xc_app_name#${chainName}-$i#g" ./${chainName}/${chainName}-$i.yaml
  #kubectl delete -f ./${chainName}/${chainName}-$i.yaml
  kubectl apply -f ./${chainName}/${chainName}-$i.yaml
done

###等待40秒
sleep 40

###获取Pod所在的NodeIP
xc_node_ip1=`kubectl get pod -l app=${chainName}-1 -o jsonpath="{.items[*].status.hostIP}"`
xc_node_ip2=`kubectl get pod -l app=${chainName}-2 -o jsonpath="{.items[*].status.hostIP}"`
xc_node_ip3=`kubectl get pod -l app=${chainName}-3 -o jsonpath="{.items[*].status.hostIP}"`

###获取service的Port
xc_node_port1=`kubectl get svc ${chainName}-1 -o jsonpath={".spec.ports[?(@.port==47101)].nodePort"}`
xc_node_port2=`kubectl get svc ${chainName}-2 -o jsonpath={".spec.ports[?(@.port==47101)].nodePort"}`
xc_node_port3=`kubectl get svc ${chainName}-3 -o jsonpath={".spec.ports[?(@.port==47101)].nodePort"}`

###获取podName
podName1=`kubectl get pod -l app=${chainName}-1 -o jsonpath="{.items[*].metadata.name}"`
podName2=`kubectl get pod -l app=${chainName}-2 -o jsonpath="{.items[*].metadata.name}"`
podName3=`kubectl get pod -l app=${chainName}-3 -o jsonpath="{.items[*].metadata.name}"`


###获取节点的 address
xc_node_address1=`kubectl exec -it ${podName1} -- cat ./data/keys/address`
xc_node_address2=`kubectl exec -it ${podName2} -- cat ./data/keys/address`
xc_node_address3=`kubectl exec -it ${podName3} -- cat ./data/keys/address`

###初始化节点配置
kubectl exec -it ${podName1} -- ./initxpoa.sh ${xc_node_ip1} ${xc_node_port1} ${xc_node_address1} ${xc_node_ip2} ${xc_node_port2} ${xc_node_address2} ${xc_node_ip3} ${xc_node_port3} ${xc_node_address3}
kubectl exec -it ${podName2} -- ./initxpoa.sh ${xc_node_ip1} ${xc_node_port1} ${xc_node_address1} ${xc_node_ip2} ${xc_node_port2} ${xc_node_address2} ${xc_node_ip3} ${xc_node_port3} ${xc_node_address3}
kubectl exec -it ${podName3} -- ./initxpoa.sh ${xc_node_ip1} ${xc_node_port1} ${xc_node_address1} ${xc_node_ip2} ${xc_node_port2} ${xc_node_address2} ${xc_node_ip3} ${xc_node_port3} ${xc_node_address3}

sleep 10

### 确认启动

### 创建合约账户

### 发布 xpoa_validates.wasm 合约

10.踩坑

golang踩坑:

  • 编译超级链的golang版本要和开发合约的golang版本环境保持一致
  • golang版本不要太新,1.13封顶了
  • go编译的wasm文件发布有问题
  • go的SDK支持国密,其他的SDK还在支持中
  • go支持native和wasm两种合约模式,native需要把docker启动,虽然配置的是docker:false
  • Select utxo error, details:NOT_ENOUGH_UTXO_ERROR 没有足够的钱了,需要给账户转账.
  • /lib64/libstdc++.so.6.0.21 not found https://www.cnblogs.com/HandyLi/p/13957043.html
  • /lib64/libstdc++.so.6: version `GLIBCXX_3.4.21' not found https://blog.csdn.net/qq_39295044/article/details/86685789