Merge pull request #373 from meshplus/dev-release-1.6

merge new commits to release 1.6 except DID feature
This commit is contained in:
Aiden X 2021-04-19 09:41:52 +08:00 committed by GitHub
commit 8b2b9fe426
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
76 changed files with 6698 additions and 518 deletions

32
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,32 @@
name: Release
on:
push:
tags:
- 'v*.*.*'
jobs:
release:
name: Build Release on Linux and Macos
runs-on: ${{matrix.os}}
strategy:
matrix:
os: [macos-latest, ubuntu-latest]
steps:
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.13
- name: Check out code
uses: actions/checkout@v2
- name: Build Binary
run: make release-binary
- name: Release Binary
uses: softprops/action-gh-release@v1
with:
files: dist/**.tar.gz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

4
.gitignore vendored
View File

@ -27,4 +27,6 @@ testdata/storage
imports/imports.go
goent.mod
goent.sum
goent.sum
dist
build

29
.goreleaser.yml Normal file
View File

@ -0,0 +1,29 @@
env:
- GO111MODULE=on
#- GOPROXY=https://goproxy.cn,direct
before:
hooks:
- make release-binary
builds:
- id: bitxhub
binary: bitxhub
main: ./cmd/bitxhub
skip: true
release:
draft: true
prerelease: auto
name_template: Release {{.Tag}}
disable: false
archives:
- id: bitxhub-archive
format: binary
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'

View File

@ -2,7 +2,7 @@
SHELL := /bin/bash
CURRENT_PATH = $(shell pwd)
APP_NAME = bitxhub
APP_VERSION = 1.5.0
APP_VERSION = 1.6.0
export GODEBUG=x509ignoreCN=0
# build with verison infos
@ -81,6 +81,10 @@ buildent:
@mv ./bitxhub bin
@printf "${GREEN}Build bitxhub ent successfully!${NC}\n"
## make release: Build release before push
release-binary:
@cd scripts && bash release_binary.sh '${APP_VERSION}'
mod:
sed "s?)?$(MODS)\n)?" go.mod

View File

@ -76,15 +76,45 @@ func governanceCMD() cli.Command {
},
cli.Command{
Name: "chain",
Usage: "query chain status by chain id",
Flags: []cli.Flag{
cli.StringFlag{
Name: "id",
Usage: "chain id",
Required: true,
Usage: "appchain manage command",
Subcommands: cli.Commands{
cli.Command{
Name: "status",
Usage: "query chain status by chain id",
Flags: []cli.Flag{
cli.StringFlag{
Name: "id",
Usage: "chain id",
Required: true,
},
},
Action: getChainStatusById,
},
cli.Command{
Name: "freeze",
Usage: "freeze appchain by chain id",
Flags: []cli.Flag{
cli.StringFlag{
Name: "id",
Usage: "chain id",
Required: true,
},
},
Action: freezeAppchain,
},
cli.Command{
Name: "activate",
Usage: "activate chain by chain id",
Flags: []cli.Flag{
cli.StringFlag{
Name: "id",
Usage: "chain id",
Required: true,
},
},
Action: activateAppchain,
},
},
Action: getChainStatusById,
},
},
}
@ -99,47 +129,10 @@ func vote(ctx *cli.Context) error {
return fmt.Errorf("the info parameter can only have a value of \"approve\" or \"reject\"")
}
repoRoot, err := repo.PathRootWithDefault(ctx.GlobalString("repo"))
receipt, err := invokeBVMContract(ctx, constant.GovernanceContractAddr.String(), "Vote", pb.String(id), pb.String(info), pb.String(reason))
if err != nil {
return err
}
keyPath := repo.GetKeyPath(repoRoot)
resp, err := sendTx(ctx, constant.GovernanceContractAddr.String(), 0, uint64(pb.TransactionData_INVOKE), keyPath, uint64(pb.TransactionData_BVM), "Vote",
pb.String(id), pb.String(info), pb.String(reason))
if err != nil {
return fmt.Errorf("send transaction error: %s", err.Error())
}
hash := gjson.Get(string(resp), "tx_hash").String()
var data []byte
if err = retry.Retry(func(attempt uint) error {
data, err = getTxReceipt(ctx, hash)
if err != nil {
fmt.Println("get transaction receipt error: " + err.Error() + "... retry later")
return err
} else {
m := make(map[string]interface{})
if err := json.Unmarshal(data, &m); err != nil {
fmt.Println("get transaction receipt error: " + err.Error() + "... retry later")
return err
}
if errInfo, ok := m["error"]; ok {
fmt.Println("get transaction receipt error: " + errInfo.(string) + "... retry later")
return fmt.Errorf(errInfo.(string))
}
return nil
}
}, strategy.Wait(500*time.Millisecond),
); err != nil {
fmt.Println("get transaction receipt error: " + err.Error())
}
m := &runtime.JSONPb{OrigName: true, EmitDefaults: true, EnumsAsInts: true}
receipt := &pb.Receipt{}
if err = m.Unmarshal(data, receipt); err != nil {
return fmt.Errorf("jsonpb unmarshal receipt error: %w", err)
}
if receipt.IsSuccess() {
color.Green("vote successfully!\n")
@ -155,7 +148,7 @@ func getProposals(ctx *cli.Context) error {
status := ctx.String("status")
from := ctx.String("from")
if err := checkArgs(id, typ, status, from); err != nil {
if err := checkProposalArgs(id, typ, status, from); err != nil {
return err
}
@ -206,7 +199,7 @@ func getProposals(ctx *cli.Context) error {
return nil
}
func checkArgs(id, typ, status, from string) error {
func checkProposalArgs(id, typ, status, from string) error {
if id == "" &&
typ == "" &&
status == "" &&
@ -246,40 +239,9 @@ func getdDuplicateProposals(ps1, ps2 []contracts.Proposal) []contracts.Proposal
}
func getProposalsByConditions(ctx *cli.Context, keyPath string, menthod string, arg string) ([]contracts.Proposal, error) {
resp, err := sendTx(ctx, constant.GovernanceContractAddr.String(), 0, uint64(pb.TransactionData_INVOKE), keyPath, uint64(pb.TransactionData_BVM), menthod,
pb.String(arg))
receipt, err := invokeBVMContract(ctx, constant.GovernanceContractAddr.String(), menthod, pb.String(arg))
if err != nil {
return nil, fmt.Errorf("send transaction error: %w", err)
}
hash := gjson.Get(string(resp), "tx_hash").String()
var data []byte
if err = retry.Retry(func(attempt uint) error {
data, err = getTxReceipt(ctx, hash)
if err != nil {
fmt.Println("get transaction receipt error: " + err.Error() + "... retry later")
return err
} else {
m := make(map[string]interface{})
if err := json.Unmarshal(data, &m); err != nil {
fmt.Println("get transaction receipt error: " + err.Error() + "... retry later")
return err
}
if errInfo, ok := m["error"]; ok {
fmt.Println("get transaction receipt error: " + errInfo.(string) + "... retry later")
return fmt.Errorf(errInfo.(string))
}
return nil
}
}, strategy.Wait(500*time.Millisecond),
); err != nil {
fmt.Println("get transaction receipt error: " + err.Error())
}
m := &runtime.JSONPb{OrigName: true, EmitDefaults: true, EnumsAsInts: true}
receipt := &pb.Receipt{}
if err = m.Unmarshal(data, receipt); err != nil {
return nil, fmt.Errorf("jsonpb unmarshal receipt error: %w", err)
return nil, err
}
if receipt.IsSuccess() {
@ -356,22 +318,72 @@ func addRow(t *tabby.Tabby, rawLine []string, header bool) {
func getChainStatusById(ctx *cli.Context) error {
id := ctx.String("id")
repoRoot, err := repo.PathRootWithDefault(ctx.GlobalString("repo"))
receipt, err := invokeBVMContract(ctx, constant.AppchainMgrContractAddr.String(), "GetAppchain", pb.String(id))
if err != nil {
return err
}
if receipt.IsSuccess() {
chain := &appchainMgr.Appchain{}
if err := json.Unmarshal(receipt.Ret, chain); err != nil {
return fmt.Errorf("unmarshal receipt error: %w", err)
}
color.Green("appchain %s is %s", chain.ID, string(chain.Status))
} else {
color.Red("get chain status error: %s\n", string(receipt.Ret))
}
return nil
}
func freezeAppchain(ctx *cli.Context) error {
id := ctx.String("id")
receipt, err := invokeBVMContract(ctx, constant.AppchainMgrContractAddr.String(), "FreezeAppchain", pb.String(id))
if err != nil {
return err
}
if receipt.IsSuccess() {
color.Green("proposal id is %s", string(receipt.Ret))
} else {
color.Red("freeze appchain error: %s\n", string(receipt.Ret))
}
return nil
}
func activateAppchain(ctx *cli.Context) error {
id := ctx.String("id")
receipt, err := invokeBVMContract(ctx, constant.AppchainMgrContractAddr.String(), "ActivateAppchain", pb.String(id))
if err != nil {
return err
}
if receipt.IsSuccess() {
color.Green("proposal id is %s", string(receipt.Ret))
} else {
color.Red("activate appchain error: %s\n", string(receipt.Ret))
}
return nil
}
func invokeBVMContract(ctx *cli.Context, contractAddr string, method string, args ...*pb.Arg) (*pb.Receipt, error) {
repoRoot, err := repo.PathRootWithDefault(ctx.GlobalString("repo"))
if err != nil {
return nil, err
}
keyPath := repo.GetKeyPath(repoRoot)
resp, err := sendTx(ctx, constant.AppchainMgrContractAddr.String(), 0, uint64(pb.TransactionData_INVOKE), keyPath, uint64(pb.TransactionData_BVM), "GetAppchain",
pb.String(id))
resp, err := sendTx(ctx, contractAddr, 0, uint64(pb.TransactionData_INVOKE), keyPath, uint64(pb.TransactionData_BVM), method, args...)
if err != nil {
return fmt.Errorf("send transaction error: %s", err.Error())
return nil, fmt.Errorf("send transaction error: %s", err.Error())
}
hash := gjson.Get(string(resp), "tx_hash").String()
var data []byte
if err = retry.Retry(func(attempt uint) error {
time.Sleep(1000 * time.Millisecond)
data, err = getTxReceipt(ctx, hash)
if err != nil {
fmt.Println("get transaction receipt error: " + err.Error() + "... retry later")
@ -396,17 +408,8 @@ func getChainStatusById(ctx *cli.Context) error {
m := &runtime.JSONPb{OrigName: true, EmitDefaults: true, EnumsAsInts: true}
receipt := &pb.Receipt{}
if err = m.Unmarshal(data, receipt); err != nil {
return fmt.Errorf("jsonpb unmarshal receipt error: %w", err)
return nil, fmt.Errorf("jsonpb unmarshal receipt error: %w", err)
}
if receipt.IsSuccess() {
chain := &appchainMgr.Appchain{}
if err := json.Unmarshal(receipt.Ret, chain); err != nil {
return fmt.Errorf("unmarshal receipt error: %w", err)
}
color.Green("appchain %s is %s", chain.ID, string(chain.Status))
} else {
color.Red("get chain status error: %s\n", string(receipt.Ret))
}
return nil
return receipt, nil
}

89
dist/config.yaml vendored Normal file
View File

@ -0,0 +1,89 @@
project_name: bitxhub
env:
- GO111MODULE=on
release:
github:
owner: meshplus
name: bitxhub
draft: true
prerelease: auto
name_template: Release {{.Tag}}
milestones:
- repo:
owner: meshplus
name: bitxhub
name_template: '{{ .Tag }}'
scoop:
name: bitxhub
commit_author:
name: goreleaserbot
email: goreleaser@carlosbecker.com
commit_msg_template: Scoop update for {{ .ProjectName }} version {{ .Tag }}
builds:
- id: bitxhub
goos:
- linux
- darwin
goarch:
- amd64
- arm64
- "386"
goarm:
- "6"
targets:
- linux_amd64
- linux_arm64
- linux_386
- darwin_amd64
dir: .
main: ./cmd/bitxhub
ldflags:
- -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}
-X main.builtBy=goreleaser
binary: bitxhub
lang: go
skip: true
gobinary: go
archives:
- id: bitxhub-archive
builds:
- bitxhub
name_template: '{{ .Binary }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{
.Arm }}{{ end }}{{ if .Mips }}_{{ .Mips }}{{ end }}'
format: binary
files:
- licence*
- LICENCE*
- license*
- LICENSE*
- readme*
- README*
- changelog*
- CHANGELOG*
allow_different_binary_count: false
snapshot:
name_template: '{{ .Tag }}-SNAPSHOT-{{ .ShortCommit }}'
checksum:
name_template: '{{ .ProjectName }}_{{ .Version }}_checksums.txt'
algorithm: sha256
changelog:
filters:
exclude:
- '^docs:'
- '^test:'
sort: asc
dist: dist
env_files:
github_token: ~/.config/goreleaser/github_token
gitlab_token: ~/.config/goreleaser/gitlab_token
gitea_token: ~/.config/goreleaser/gitea_token
before:
hooks:
- make release-binary
source:
name_template: '{{ .ProjectName }}-{{ .Version }}'
format: tar.gz
github_urls:
download: https://github.com
gitlab_urls:
download: https://gitlab.com

BIN
docs/docs/assets/burn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 344 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

BIN
docs/docs/assets/mint.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 KiB

BIN
docs/docs/assets/oracle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

View File

@ -0,0 +1,345 @@
# 跨链合约
按照跨链合约的设计我们需要在有跨链需求的应用链上部署两种合约。一个合约负责对接跨链网关Pier为跨链管理合约Broker一个合约负责具体的业务场景为业务合约。业务合约需要跨链时要统一将跨链请求提交到Broker合约上Broker统一和Pier进行交互。一个Broker合约可以负责对接多个业务合约。
跨链接入方无需对broker合约进行修改直接部署使用即可同时为了简化业务合约的编写我们设计了业务合约的相应接口。
## Broker 合约接口
```go
type Broker interface {
// 提供给业务合约注册。注册且审核通过的业务合约才能调用Broker合约的跨链接口
register(string id) Response
// 提供给管理员审核已经注册的业务合约
audit(string id, bool status) Response
// getInnerMeta 是获取跨链请求相关的Meta信息的接口。以Broker所在的区块链为目的链的一系列跨链请求的序号信息。如果Broker在A链则可能有多条链和A进行跨链如B->A:3; C->A:5。返回的map中key值为来源链IDvalue对应该来源链已发送的最新的跨链请求的序号如{B:3, C:5}。
getInnerMeta() Response
// getOuterMeta 是获取跨链请求相关的Meta信息的接口。以Broker所在的区块链为来源链的一系列跨链请求的序号信息。如果以Broker在A链则A可能和多条链进行跨链如A->B:3; A->C:5。返回的map中key值为目的链IDvalue对应已发送到该目的链的最新跨链请求的序号如{B:3, C:5}。
getOuterMeta() Response
// getCallbackMeta 是获取跨链请求相关的Meta信息的接口。以Broker所在的区块链为来源链的一系列跨链请求的序号信息。如果Broker在A链则A可能和多条链进行跨链如A->B:3; A->C:5同时由于跨链请求中支持回调操作即A->B->A为一次完整的跨链操作我们需要记录回调请求的序号信息如A->B->:2; A->C—>A:4。返回的map中key值为目的链IDvalue对应到该目的链最新的带回调跨链请求的序号如{B:2, C:4}。(注意 callbackMeta序号可能和outMeta是不一致的这是由于由A发出的跨链请求部分是没有回调的
getCallbackMeta() Response
// getInMessage 查询历史跨链请求。查询键值中srcChainID指定来源链idx指定序号查询结果为以Broker所在的区块链作为目的链的跨链请求。
getInMessage(string srcChainID, uint64 idx) Response
// getOutMessage 查询历史跨链请求。查询键值中dstChainID指定目的链idx指定序号查询结果为以Broker所在的区块链作为来源链的跨链请求。
getOutMessage(string dstChainID, uint64 idx) Response
// 提供给跨链网关调用的接口,跨链网关收到跨链请求时会调用该接口。
invokeInterchain(address srcChainID, uint64 index, address destAddr, bool req, bytes calldata bizCallData)
// 提供给跨链网关调用的接口,当跨链网关收到无效当跨链请求时会调用该接口。
invokeIndexUpdateWithError(address srcChainID, uint64 index, bool req, string memory err)
// 提供给业务合约发起通用的跨链交易的接口。
emitInterchainEvent(address destChainID, string memory destAddr, string memory funcs, string memory args, string memory argscb, string memory argsrb)
// 提供给合约部署初始化使用
initialize() Response
}
```
### 重要接口说明
- `__emitInterchainEvent__`
该接口是业务合约发起通用的跨链调用的接口。接受的参数有目的链ID目的链业务合约地址或ID调用的函数名、回调函数名、回滚函数名调用函数的参数回调函数参数回滚函数参数。
Broker会记录跨链交易相应的元信息并对跨链交易进行编号保证跨链交易有序进行。并且抛出跨链事件以通知跨链网关跨链交易的产生。
- `invokeInterchain`
该接口是跨链网关对业务合约进行跨链调用或回调/回滚的接口。跨链网关对要调用的目的合约的方法和参数进行封装,通过该接口实现对不同目的合约的灵活调用,并返回目的合约的调用函数的返回值。
接受参数有来源链ID交易序号目的业务合约ID是否是跨链请求业务合约调用方法和参数的封装数据。
## 业务合约接口
业务合约现阶段分为资产类和数据交换类的业务合约,由于资产类的有操作原子性和安全性的考虑,需要的接口实现上比数据交换类的业务合约更复杂。
### Transfer 合约
```go
type Transfer interface {
// 发起一笔跨链交易的接口
transfer(string dstChainID, string destAddr, string sender, string receiver, string amount) Response
// 提供给Broker合约收到跨链充值所调用的接口
interchainCharge(string sender, string receiver, uint64 val) Response
// 跨链交易失败之后提供给Broker合约进行回滚的接口
interchainRollback(string sender, uint64 val) Response
// 冻结账户操作
frozenAccount(string account) Response
// 冻结账户操作
unfrozenAccount(string account) Response
}
```
### DataSwapper合约
```go
public interface DataSwapper {
// 发起一个跨链获取数据交易的接口
get(string dstChainID, string dstAddr, string key) Response
// 提供给Broker合约调用当Broker收到跨链获取数据的请求时取数据的接口
interchainGet(string key) Response
// 跨链获取到的数据回写的接口
interchainSet(string key, string value) Response
}
```
## 具体实现
对于想要接入到我们的跨链平台中的Fabric区块链我们已经有提供跨链管理合约Broker和相应的Plugin你只需要对你的业务合约进行一定的改造便可拥有跨链功能。
**如果是其他应用链你可以根据我们的设计思路自行开发跨链管理合约以及相应的Plugin。**
现在我们已经有Solidity版本和chaincode版本编写的跨链合约样例实现具体说明如下
- [__Solidity 跨链合约实现__](https://github.com/meshplus/pier-client-ethereum/tree/master/example)
- [chaincode 跨链合约实现](https://github.com/meshplus/pier-client-fabric/tree/master/example)
如果你需要新的语言编写合约,你可以按照我们的设计思路和参考实现进行进一步的开发。
现在我们支持Hyperchain EVM合约、以太坊私链Solidity合约、BCOS上的EVM合约以及Fabric上chaincode合约编写跨链合约。
## Hyperchain、以太坊、BCOS上的EVM合约
本节主要说明在支持EVM合约的应用链上如何使用我们提供的跨链管理合约Broker在你已有的Solidity业务合约的基础上添加接口以获得跨链能力。
当然不同的区块链可能在以太坊的EVM上做了一些二次开发和新增功能请根据具体区块链的文档相应修改代码。
### 业务合约Demo
假设你已经有了一个简单的KV存储的业务合约代码如下
```javascript
pragma solidity >=0.5.7;
contract DataSwapper {
mapping(string => string) dataM; // map for accounts
// 数据交换类的业务合约
function getData(string memory key) public returns(string memory) {
return dataM[key];
}
function setData(string memory key, string memory value) public {
dataM[key] = value;
}
}
```
现在你想在这个合约的基础上增加一个跨链获取数据的功能,如果使用我们的跨链管理合约提供的接口,很简单的增加几个接口就可以了。
### 发起跨链数据交换的接口
```javascript
contract DataSwapper {
address BrokerAddr = 0x2346f3BA3F0B6676aa711595daB8A27d0317DB57;
Broker broker = Broker(BrokerAddr);
...
function get(address destChainID, string memory destAddr, string memory key) public {
broker.emitInterchainEvent(destChainID, destAddr, "interchainGet,interchainSet,", key, key, "");
}
}
contract Broker {
function emitInterchainEvent(address destChainID, string memory destAddr, string memory funcs, string memory args, string memory argscb, string memory argsrb);
}
```
其中Broker的地址和该业务合约需要使用到的接口需要在业务合约中声明然后直接调用该接口发起跨链交易。
### 跨链获取的接口
```javascript
modifier onlyBroker {
require(msg.sender == BrokerAddr, "Invoker are not the Broker");
_;
}
function interchainGet(string memory key) public onlyBroker returns(bool, string memory) {
return (true, dataM[key]);
}
```
我们规定跨链调用的接口的第一个返回值类型必须是bool类型它用来表示跨链调用是否成功。
其中onlyBroker是进行跨链权限控制的修饰器。该接口和下面的跨链回写接口均是提供给Broker合约进行调用也是其他应用链发来的跨链交易执行时需要调用的接口。
### 跨链回写的接口
```javascript
function interchainSet(string memory key, string memory value) public onlyBroker {
setData(key, value);
}
```
## Fabric
本节主要说明在Fabric应用链上如何使用我们提供的跨链管理合约Broker在你已有业务合约的基础上添加接口以跨链能力。
### 业务合约Demo
假设你已经有了一个简单的KV存储的业务合约代码如下
```go
type KVStore struct{}
func (s *KVStore) Init(stub shim.ChaincodeStubInterface) peer.Response {
return shim.Success(nil)
}
func (s *KVStore) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
function, args := stub.GetFunctionAndParameters()
fmt.Printf("invoke: %s\n", function)
switch function {
case "get":
return s.get(stub, args)
case "set":
return s.set(stub, args)
default:
return shim.Error("invalid function: " + function + ", args: " + strings.Join(args, ","))
}
}
func (s *KVStore) get(stub shim.ChaincodeStubInterface, args []string) peer.Response {
// args[0]: key
value, err := stub.GetState(args[0])
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(value)
}
// get is business function which will invoke the to,tid,id
func (s *KVStore) set(stub shim.ChaincodeStubInterface, args []string) peer.Response {
if len(args) != 2 {
return shim.Error("incorrect number of arguments")
}
err := stub.PutState(args[0], []byte(args[1]))
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(nil)
}
func main() {
err := shim.Start(new(KVStore))
if err != nil {
fmt.Printf("Error starting chaincode: %s", err)
}
}
```
现在你想在这个合约的基础上增加一个跨链获取数据的功能,如果使用我们的跨链管理合约提供的接口,很简单的增加几个接口就可以了。
### 发起跨链数据交换的接口
为了方便用户使用,我们在原来获取数据的接口基础增加这个功能:
```go
const (
channelID = "mychannel"
brokerContractName = "broker"
interchainInvokeFunc = "InterchainDataSwapInvoke"
)
func (s *KVStore) get(stub shim.ChaincodeStubInterface, args []string) peer.Response {
switch len(args) {
case 1:
// args[0]: key
value, err := stub.GetState(args[0])
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(value)
case 3:
// args[0]: destination appchain id
// args[1]: destination contract address
// args[2]: key
b := util.ToChaincodeArgs(emitInterchainEventFunc, args[0], args[1], "interchainGet", args[2], "interchainSet", args[2], "", "")
response := stub.InvokeChaincode(brokerContractName, b, channelID)
if response.Status != shim.OK {
return shim.Error(fmt.Errorf("invoke broker chaincode %s error: %s", brokerContractName, response.Message).Error())
}
return shim.Success(nil)
default:
return shim.Error("incorrect number of arguments")
}
}
```
由于我们的跨链管理合约一旦部署之后chaincode name和所在的channel和跨链接口都是不变的所以在业务变量中直接使用常量指定Broker合约的相关信息。
```go
b := util.ToChaincodeArgs(emitInterchainEventFunc, args[0], args[1], "interchainGet", args[2], "interchainSet", args[2], "", "")
response := stub.InvokeChaincode(brokerContractName, b, channelID)
```
这两行代码调用了我们的跨链管理合约只需要提供参数目的链ID目的链上业务合约的地址跨链调用函数跨链调用函数参数回调函数回调函数参数最后两个参数为回滚函数和回滚函数参数因为该场景下即使目的链执行跨链交易失败来源链也无需回滚因此无需提供回滚信息。
### 跨链获取的接口
```go
func (s *KVStore) interchainGet(stub shim.ChaincodeStubInterface, args []string) peer.Response {
if len(args) != 2 {
return shim.Error("incorrect number of arguments")
}
value, err := stub.GetState(args[0])
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(value)
}
```
`interchainGet` 接受参数 `key`,在本合约中查询该`Key`值对应的`value`,并返回。该接口提供给`Broker`合约进行跨链获取数据的调用。
### 跨链回写的接口
```go
func (s *KVStore) interchainSet(stub shim.ChaincodeStubInterface, args []string) peer.Response {
if len(args) != 2 {
return shim.Error("incorrect number of arguments")
}
err := stub.PutState(args[0], []byte(args[1]))
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(nil)
}
```
`interchainSet` 接受参数 `key`,在本合约中设置`Key`值对应的`value`。该接口提供给`Broker`合约回写跨链获取数据的时候进行调用。
## 总结
经过上面的改造,你的业务合约已经具备跨链获取数据的功能了,完整的代码可以参考[__这里__](https://github.com/meshplus/pier-client-fabric/tree/master/example)

View File

@ -0,0 +1,388 @@
# 应用链插件编写
在本教程中,你将构建一个完整功能的 Plugin。过程中能学习基本的概念和具体使用细节。该示例将展示如何快速、轻松地**接入自己的区块链到跨链平台中来**。
如果你需要接入自己的开发的区块链到BitXHub跨链平台来的话可以根据你们自己的区块链来定制开发Plugin通过我们跨链网关加入到跨链平台来。
## 开发要求
- 安装 [__go1.13+__](https://golang.org/doc/install)
- 设置好$GOPATH等环境
## 教程章节
1. 重要概念
2. Plugin接口
3. 程序目标
4. 开始编写程序
5. 编译你的Plugin
## 重要概念
在解释具体的接口之前,先明确几个概念:
**跨链请求:**如果有两条区块链A和BA链需要向B链发起任何操作需要按照IBTP规则向中继链发出一个请求包我们称之为跨链请求A->B。
**IBTP包**满足IBTP的一个package跨链请求都需要通过IBTP包进行。
**来源链:**在跨链请求A->B中A即为来源链。
**目的链:**在跨链请求A->B中B即为目的链。
## Plugin接口
为了更加便捷的开发Plugin接入到Pier中来我们规定了下面一些必要的接口。
```go
type Client interface {
// 启动Plugin服务的接口
Start() error
// 停止Plugin服务的接口
Stop() error
// Plugin负责将区块链上产生的跨链事件转化为标准的IBTP格式Pier通过GetIBTP接口获取跨链请求再进行处理
GetIBTP() chan *pb.IBTP
// Plugin 负责执行其他链过来的跨链请求Pier调用SubmitIBTP提交收到的跨链请求。[][]byte 为执行跨链请求的结果。
SubmitIBTP(*pb.IBTP) ([][]byte, error)
// GetOutMessage 负责在跨链合约中查询历史跨链请求。查询键值中to指定目的链idx指定序号查询结果为以Plugin负责的区块链作为来源链的跨链请求。
GetOutMessage(to string, idx uint64) (*pb.IBTP, error)
// GetInMessage 负责在跨链合约中查询历史跨链请求。查询键值中from指定来源链idx指定序号查询结果为以Plugin负责的区块链作为目的链的跨链请求。
GetInMessage(from string, idx uint64) ([][]byte, error)
// GetInMeta 是获取跨链请求相关的Meta信息的接口。以Plugin负责的区块链为目的链的一系列跨链请求的序号信息。如果Plugin负责A链则可能有多条链和A进行跨链如B->A:3; C->A:5。返回的map中key值为来源链IDvalue对应该来源链已发送的最新的跨链请求的序号如{B:3, C:5}。
GetInMeta() (map[string]uint64, error)
// GetOutMeta 是获取跨链请求相关的Meta信息的接口。以Plugin负责的区块链为来源链的一系列跨链请求的序号信息。如果Plugin负责A链则A可能和多条链进行跨链如A->B:3; A->C:5。返回的map中key值为目的链IDvalue对应已发送到该目的链的最新跨链请求的序号如{B:3, C:5}。
GetOutMeta() (map[string]uint64, error)
// GetCallbackMeta 是获取跨链请求相关的Meta信息的接口。以Plugin负责的区块链为来源链的一系列跨链请求的序号信息。如果Plugin负责A链则A可能和多条链进行跨链如A->B:3; A->C:5同时由于跨链请求中支持回调操作即A->B->A为一次完整的跨链操作我们需要记录回调请求的序号信息如A->B->:2; A->C—>A:4。返回的map中key值为目的链IDvalue对应到该目的链最新的带回调跨链请求的序号如{B:2, C:4}。(注意 CallbackMeta序号可能和outMeta是不一致的这是由于由A发出的跨链请求部分是没有回调的
GetCallbackMeta() (map[string]uint64, error)
// CommitCallback 执行完IBTP包之后进行一些回调操作。
CommitCallback(ibtp *pb.IBTP) error
// Name 描述Plugin负责的区块链的自定义名称一般和业务相关如司法链等。
Name() string
// Type 描述Plugin负责的区块链类型比如Fabric
Type() string
}
```
## 程序目的
本教程以开发一个简单的连接Fabric区块链网络的Plugin为例最终的程序能够实现从负责的区块链获取`Hello World`信息并返回到跨链平台中。
## 开始编写你的程序
首先选择你的工程目录按照正常的GO程序的流程建立项目
```shell
$ go version // 确认你安装的GO版本
$ mkdir ${YOUR_PROJECT}
$ cd ${YOUR_PROJECT}
$ go mod init exmple/fabric-plugin
```
### Client对象
首先创建一个`client.go`文件这个文件是Plugin的核心和入口。
在该文件中应该定义你的Plugin如何获取client 实例以及如何启动和停止Plugin服务。
现在我们需要创建一个自定义的Client 结构,跨链网关最终拿到的应该是这个结构的一个实例,先来看看这个结构中都需要什么。
首先来看看`Client自定义`具体结构
```go
type Client struct {
meta *ContractMeta
consumer *Consumer
eventC chan *pb.IBTP
pierId string
name string
}
type ContractMeta struct {
EventFilter string `json:"event_filter"`
Username string `json:"username"`
CCID string `json:"ccid"`
ChannelID string `json:"channel_id"`
ORG string `json:"org"`
}
```
- metaPlugin直接和跨链合约交互需要保存你的合约的一些基础信息。由于我们需要连接一个Fabric网络这些Meta信息包括 **Fabric中跨链事件的名称、Fabric中的用户名称、Chaincode合约的名称、你的组织名称Org以及组织所在的channel。**
- consumer可以理解为Fabric上跨链事件的“监听器”这个监听器也是一个自定义的结构具体的结构在后面会详细介绍。
- eventC为跨链网关提供读取监听到的跨链事件的通道。
- name自定的区块链的名称。
- pierId跨链网关注册在跨链平台中后产生的唯一ID作为应用链的标识。
然后应该提供一个Client的实例化的接口类似于构造函数具体代码如下
```go
func NewClient(configPath, pierId string, extra []byte) (client.Client, error) {
eventC := make(chan *pb.IBTP)
// read config from files
fabricConfig, err := UnmarshalConfig(configPath)
if err != nil {
return nil, fmt.Errorf("unmarshal config for plugin :%w", err)
}
// some basic configs about your chaincode
c := &ContractMeta{
EventFilter: fabricConfig.EventFilter,
Username: fabricConfig.Username,
CCID: fabricConfig.CCID,
ChannelID: fabricConfig.ChannelId,
ORG: fabricConfig.Org,
}
// handler for listening on inter-chain events posted on your Fabric
mgh, err := newFabricHandler(c.EventFilter, eventC, pierId)
if err != nil {
return nil, err
}
csm, err := NewConsumer(configPath, c, mgh)
if err != nil {
return nil, err
}
return &Client{
consumer: csm,
eventC: eventC,
meta: c,
pierId: pierId,
name: fabricConfig.Name,
}, nil
}
```
### consumer
consumer 负责监听区块链上的由跨链合约抛出的跨链事件以及和调用chaincode。
我们新建 `./consumer.go` 文件
```go
type Consumer struct {
eventClient *event.Client
meta *ContractMeta
msgH MessageHandler
channelProvider context.ChannelProvider
ChannelClient *channel.Client
registration fab.Registration
ctx chan bool
}
```
- eventClientfabric gosdk提供的事件Client
- meta Fabric相关的参数信息
- msgH事件handler在监听到指定事件之后负责处理的函数
- channelProviderfabric gosdk提供的和chaincode交互
- ChannelClientfabric gosdk 提供的和调用chaincode的对象
- registerationfabric gosdk 提供的订阅特定事件的对象
- ctx用来结束consumer的 gorountine
### Event
由于在Fabric上抛出的事件内容是可以自定义的而跨链请求要在跨链平台上传递的话需要使用IBTP包所以我们需要一定的代码来执行这种转换。
我们新建 `./event.go` 文件
```go
type Event struct {
Index uint64 `json:"index"`
DstChainID string `json:"dst_chain_id"`
SrcContractID string `json:"src_contract_id"`
DstContractID string `json:"dst_contract_id"`
Func string `json:"func"`
Args string `json:"args"`
Callback string `json:"callback"`
Proof []byte `json:"proof"`
Extra []byte `json:"extra"`
}
```
Event结构也是自定义的需要和在你的跨链合约中抛出的事件结构一致。一个跨链交易事件一般来说需要指定目标应用的ID `DstChainID`目标应用链上智能合约的地址或者IDFabric上的chaincode没有合约地址`DstContractID`,这次跨链交易的发起者的合约地址`SrcContractID`,跨链调用的函数名 `Func`,该函数的参数 `Args`,是否有跨链调用之后要执行的回调函数 `Callback`,为了该应用链上对于该事件的证明 `Proof`,用户课自定义的部分 `Extra`
### 读取配置
Plugin的配置文件路径是通过NewClient的方法动态传入的这意味着你可以方便的修改关于你的区块链的参数信息。我们新建文件 `./config.go` 文件,负责配置读取的所有操作。
这里使用的是 `github.com/spf13/viper`库和TOML文件作为配置当然你也可以使用任何你熟悉的工具来读取配置。
```go
package main
import (
"path/filepath"
"strings"
"github.com/spf13/viper"
)
const (
ConfigName = "fabric.toml"
)
type Fabric struct {
Addr string `toml:"addr" json:"addr"`
Name string `toml:"name" json:"name"`
EventFilter string `mapstructure:"event_filter" toml:"event_filter" json:"event_filter"`
Username string `toml:"username" json:"username"`
CCID string `toml:"ccid" json:"ccid"`
ChannelId string `mapstructure:"channel_id" toml:"channel_id" json:"channel_id"`
Org string `toml:"org" json:"org"`
}
func DefaultConfig() *Fabric {
return &Fabric{
Addr: "localhost:10053",
Name: "fabric",
EventFilter: "CrosschainEventName",
Username: "Admin",
CCID: "Broker",
ChannelId: "mychannel",
Org: "org2",
}
}
func UnmarshalConfig(configPath string) (*Fabric, error) {
viper.SetConfigFile(filepath.Join(configPath, ConfigName))
viper.SetConfigType("toml")
viper.AutomaticEnv()
viper.SetEnvPrefix("FABRIC")
replacer := strings.NewReplacer(".", "_")
viper.SetEnvKeyReplacer(replacer)
if err := viper.ReadInConfig(); err != nil {
return nil, err
}
config := DefaultConfig()
if err := viper.Unmarshal(config); err != nil {
return nil, err
}
return config, nil
}
```
### SubmitIBTP
该接口主要负责将其他链发送过来的IBTP包解析并构造成本链的交易并发送到本链的跨链合约中。
如果来源链要求将本链调用合约的结果返回的话还需要构造相应的IBTP回执发回来源链。
```go
func (c *Client) SubmitIBTP(ibtp *pb.IBTP) (*model.PluginResponse, error) {
ret := &model.PluginResponse{}
pd := &pb.Payload{}
if err := pd.Unmarshal(ibtp.Payload); err != nil {
return ret, fmt.Errorf("ibtp payload unmarshal: %w", err)
}
content := &pb.Content{}
if err := content.Unmarshal(pd.Content); err != nil {
return ret, fmt.Errorf("ibtp content unmarshal: %w", err)
}
args := util.ToChaincodeArgs(ibtp.From, strconv.FormatUint(ibtp.Index, 10), content.DstContractId)
args = append(args, content.Args...)
request := channel.Request{
ChaincodeID: c.meta.CCID,
Fcn: content.Func,
Args: args,
}
// retry executing
var res channel.Response
var proof []byte
var err error
if err := retry.Retry(func(attempt uint) error {
res, err = c.consumer.ChannelClient.Execute(request)
if err != nil {
if strings.Contains(err.Error(), "Chaincode status Code: (500)") {
res.ChaincodeStatus = shim.ERROR
return nil
}
return fmt.Errorf("execute request: %w", err)
}
return nil
}, strategy.Wait(2*time.Second)); err != nil {
logger.Panicf("Can't send rollback ibtp back to bitxhub: %s", err.Error())
}
response := &Response{}
if err := json.Unmarshal(res.Payload, response); err != nil {
return nil, err
}
// if there is callback function, parse returned value
result := util.ToChaincodeArgs(strings.Split(string(response.Data), ",")...)
newArgs := make([][]byte, 0)
ret.Status = response.OK
ret.Message = response.Message
// If no callback function to invoke, then simply return
if content.Callback == "" {
return ret, nil
}
proof, err = c.getProof(res.TransactionID)
if err != nil {
return ret, err
}
switch content.Func {
case "interchainGet":
newArgs = append(newArgs, content.Args[0])
newArgs = append(newArgs, result...)
case "interchainCharge":
newArgs = append(newArgs, []byte(strconv.FormatBool(response.OK)), content.Args[0])
newArgs = append(newArgs, content.Args[2:]...)
}
ret.Result, err = c.generateCallback(ibtp, newArgs, proof)
if err != nil {
return nil, err
}
return ret, nil
}
```
## 编译你的Plugin
我们采用GO语言提供的插件模式实现Pier对于你编写的Plugin的动态加载。
MacOS和Linux平台
运行下面的命令,能够得到 `your_plugin.so`文件。
```shell
$ cd ${YOUR_PROJECT_PATH}
$ go build --buildmode=plugin -o your_plugin.so ./*.go
```
将你编写的动态链接文件放到Pier配置文件夹下配合我们提供的Pier就能接入到跨链平台来。

View File

@ -0,0 +1,167 @@
# 快速开始
我们提供了Goduck运维小工具来快速体验跨链流程。
## 1 环境准备
Goduck快速开始依赖于Docker和Docker-Compose需要提前准备好docker环境。
## 2 下载 Goduck
下载Goduck可执行二进制文件
```shell
curl https://raw.githubusercontent.com/meshplus/goduck/release-0.1/scripts/goduck.sh -L -o - | bash
```
**!!!注意:** 执行上述命令的路径下应该没有goduck同名目录
## 3 初始化
初始化goduck配置文件命令如下
```shell
goduck init
```
## 4 启动跨链网络
在本地启动一个solo版本的BitXHub节点、两条以太坊私有链以及相应的两个跨链网关启动命令如下
```shell
goduck playground start
```
该命令执行的具体操作包括以下步骤:
### 获取相关镜像
镜像拉取成功后将会打印日志如下:
```shell
Creating network "quick_start_default" with the default driver
Creating ethereum-1 ... done
Creating bitxhub_solo ... done
Creating ethereum-2 ... done
Creating pier-ethereum-2 ... done
Creating pier-ethereum-1 ... done
Attaching to bitxhub_solo, ethereum-2, ethereum-1, pier-ethereum-1, pier-ethereum-2
```
### 启动两条以太坊私有链
以太坊私有链ethereum-1和ethereum-2启动成功后最终会打印出日志如下
```shell
ethereum-2 | INFO [04-02|02:41:57.348] Sealing paused, waiting for transactions
ethereum-1 | INFO [04-02|02:41:57.349] Sealing paused, waiting for transactions
```
### 启动中继链
启动一条solo模式的中继链启动成功后将会打印出BitXHub的logo如下
```shell
bitxhub_solo | =======================================================
bitxhub_solo | ____ _ __ _ __ __ __ __
bitxhub_solo | / __ ) (_) / /_ | |/ / / / / / __ __ / /_
bitxhub_solo | / __ | / / / __/ | / / /_/ / / / / / / __ \
bitxhub_solo | / /_/ / / / / /_ / | / __ / / /_/ / / /_/ /
bitxhub_solo | /_____/ /_/ \__/ /_/|_| /_/ /_/ \__,_/ /_.___/
bitxhub_solo |
bitxhub_solo | =======================================================
```
### 注册应用链
创建两个与应用链对应的跨链网关通过跨链网关向中继链注册应用链信息注册成功后会打印出应用链id如下
```shell
pier-ethereum-1 | appchain register successfully, id is 0xb132702a7500507411f3bd61ab33d9d350d41a37
pier-ethereum-2 | appchain register successfully, id is 0x9f5cf4b97965ababe19fcf3f1f12bb794a7dc279
```
### 部署验证规则
跨链网关向中继链部署应用链的验证规则,部署成功后将打印日志如下:
```shell
pier-ethereum-1 | Deploy rule to bitxhub successfully
pier-ethereum-2 | Deploy rule to bitxhub successfully
```
### 启动跨链网关
跨链网关完成注册应用链、部署验证规则两步后即可启动,启动成功后跨链网关会打印出日志如下:
```shell
pier-ethereum-1 | time="02:42:02.287" level=info msg="Exchanger started" module=exchanger
pier-ethereum-2 | time="02:42:02.349" level=info msg="Exchanger started" module=exchanger
```
中继链会打印出日志如下:
```
bitxhub_solo | time="02:42:02.291" level=info msg="Add pier" id=0xb132702a7500507411f3bd61ab33d9d350d41a37 module=router
bitxhub_solo | time="02:42:02.353" level=info msg="Add pier" id=0x9f5cf4b97965ababe19fcf3f1f12bb794a7dc279 module=router
```
**!!!注意:** 如遇网络问题,运行下面命令:
```shell
goduck playground clean
```
## 5 跨链交易
分别在两条以太坊应用链上发起跨链交易,执行命令如下:
```shell
goduck playground transfer
```
该命令会调用以太坊上的合约(以太坊镜像上已经部署好合约)发起两笔跨链交易:
- 从ethereum-1的Alice账户转账1到ethereum-2的Alice账户
- 从ethereum-2的Alice账户转账1到ethereum-1的Alice账户
信息打印出如下:
```shell
// 查询账户余额
1. Query original accounts in appchains
// 以太坊1的Alice账户余额为10000
Query Alice account in ethereum-1 appchain
======= invoke function getBalance =======
call result: 10000
// 以太坊2的Alice账户余额为10000
Query Alice account in ethereum-2 appchain
======= invoke function getBalance =======
call result: 10000
// 从以太坊1的Alice账户转账1到以太坊2的Alice账户
2. Send 1 coin from Alice in ethereum-1 to Alice in ethereum-2
======= invoke function transfer =======
=============== Transaction hash is ==============
0xff42eb87410f7ed4c8bf394716b7f202d4f307191e5ac2ef3cfb77fabd8211a0
// 查询账户余额
3. Query accounts after the first-round invocation
// 以太坊1的Alice账户余额为9999
Query Alice account in ethereum-1 appchain
======= invoke function getBalance =======
call result: 9999
// 以太坊2的Alice账户余额为10001
Query Alice account in ethereum-2 appchain
======= invoke function getBalance =======
call result: 10001
// 从以太坊2的Alice账户转账1到以太坊1的Alice账户
4. Send 1 coin from Alice in ethereum-2 to Alice in ethereum-1
======= invoke function transfer =======
=============== Transaction hash is ==============
0x0c8042a3539cd49ce5d0570afbdb625697aadf71a040203f6bc03e3a43fb71b5
// 查询账户余额
5. Query accounts after the second-round invocation
// 以太坊1的Alice账户余额为10000
Query Alice account in ethereum-1 appchain
======= invoke function getBalance =======
call result: 10000
// 以太坊2的Alice账户余额为10000
Query Alice account in ethereum-2 appchain
======= invoke function getBalance =======
call result: 10000
```

View File

@ -0,0 +1,57 @@
# 环境准备
环境准备是部署和使用BitXHub跨链平台的第一步主要是说明BitXHub及相关组件运行的硬件配置和软件依赖您需要在部署BitXHub平台之前确认机器满足下述的要求。
## 硬件
配置| 推荐配置 | 最低配置
---|---|---
CPU | 2.4GHz *8核或以上 |1.5GHz *4核
内存 | 16GB或以上 | 8GB
存储 | 500G或以上需要支持扩容 |100G
带宽 | 10Mb |2Mb
## 操作系统支持
目前BitXHub支持的操作系统以及对应版本号如下
操作系统| 发行版本 | 系统架构
---|---|---
RHEL | 6或更新 |amd64386
CentOS | 6或更新| amd64386
SUSE |11SP3或更新|amd64386
Ubuntu |14.04或更新|amd64386
MacOS |10.8或更新|amd64386
**说明为了更好的部署安装体验我们建议您选用CentOS 8.2、Ubuntu 16.04和MacOS 10.15来进行部署安装。**
## 软件依赖
#### Go环境
BitXHub作为golang项目需要安装和配置Go环境您可以在这里下载适用于您的平台的最新版本Go二进制文件[下载](https://golang.org/dl/) -(请下载 1.13.x 或更新的稳定版本也可以下载Go源代码并从源代码进行安装这里不再赘述。
下载完成后您需要安装Go可以参考官方文档[安装Go](https://golang.org/doc/install#install) ,推荐使用默认配置安装即可,
- 对于Mac OS X 和 Linux操作系统默认情况下Go会被安装到/usr/local/go/并且将环境变量GOROOT设置为该路径/usr/local/go.
```shell
export GOROOT=/usr/local/go
```
- 同时由于我们可能将在Go中进行一系列编译操作还需要设置GOPATH等您可以将以下内容添加到您的~/.bashrc文件中
```shell
export GOROOT=/usr/local/go
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin:$GOROOT/bin
```
**说明:以上配置均是参考,您可以根据自己的实际情况进行安装配置。**
#### Docker
如果您想使用容器来部署bitxhub平台则需要提前安装好Docker推荐安装18.03或更新的稳定版本,具体的安装方法可以参考官方文档:[安装Docker](https://docs.docker.com/engine/install/)
恭喜您环境确认和准备完成接下来可以愉快地开始部署BitXHub平台啦

View File

@ -1,8 +1,8 @@
.md-header-nav__button.md-logo {
.md-header .md-logo {
padding: 0;
}
.md-header-nav__button.md-logo img {
.md-header .md-logo img {
width: auto;
height: 2rem;
}

View File

@ -0,0 +1,154 @@
# Premo使用文档
Premo是BitXHub跨链系统测试框架目前支持系统集成测试、接口测试和压力测试
## 安装
#### 获取源码
```shell
git clone git@github.com:meshplus/premo.git
```
#### 编译
进入premo工程目录:
```shell
cd premo
make install
```
## 初始化
```shell
premo init
```
## 基本使用
```text
premo [global options] command [command options] [arguments...]
```
#### COMMANDS:
- `init` init config home for premo
- `version` Premo version
- `test` test bitxhub function
- `pier` Start or stop the pier
- `bitxhub` Start or stop the bitxhub cluster
- `appchain` Bring up the appchain network
- `interchain` Start or Stop the interchain system
- `status` List the status of instantiated components
- `help, h` Shows a list of commands or help for one command
#### GLOBAL OPTIONS:
- `--repo value` Premo storage repo path
- `--help, -h` show help (default: false)
## 集成测试
进入premo的工程目录
```shell
cd premo
make bitxhub-tester
```
注意集成测试默认前置条件是本机已启动bitxhub四节点集群可在bitxhub工程目录下通过`make cluster`命令启动)
## 接口测试
进入premo的工程目录
```shell
cd premo
make http-tester
```
注意集成测试默认前置条件是本机已启动bitxhub四节点集群可在bitxhub工程目录下通过`make cluster`命令启动)
## 压力测试
test命令用于压测bitxhub的TPS性能。使用下面的命令获取使用帮助信息
```text
premo test --help
```
帮助信息如下:
```text
NAME:
premo test - test bitxhub function
USAGE:
premo test [command options] [arguments...]
OPTIONS:
--concurrent value, -c value concurrent number (default: 100)
--tps value, -t value all tx number (default: 500)
--duration value, -d value test duration (default: 60)
--key_path value, -k value Specific key path
--remote_bitxhub_addr value, -r value Specific remote bitxhub address (default: "localhost:60011")
--type value Specific tx type: interchain, data, transfer (default: "transfer")
--help, -h show help (default: false)
```
`--concurrent`或者`-c`指定并发量;
`--tps`或者`-t`指定每秒交易数量;
`--duration`或者`-d`指定压测时间;
`--key_path`或者`-k`指定私钥路径;
`--remote_bitxhub_addr`或者`-r`指定bitxhub的地址
`--type`指定交易类型,其中`transfer`是普通转账交易,`data`是调用BVM交易`interchain`是跨链交易;
压测完成后会打印压测的实际情况:
```text
$ premo test -c 50 -t 3000 -d 1000
INFO[0000] Premo configuration concurrent=50 duration=1000 tps=3000 type=transfer
INFO[0000] generate all bees number=50
2020-08-10 13:51:11 [INFO] [$(GOPATH)/src/meshplus/premo/internal/bitxhub/bitxhub.go:92] starting broker
INFO[0000] start all bees number=50
INFO[0001] current tps is 834.000000
INFO[0002] current tps is 1346.000000
INFO[0003] current tps is 2469.000000
INFO[0004] current tps is 1732.000000
INFO[0005] current tps is 2221.000000
INFO[0006] current tps is 2068.000000
INFO[0007] current tps is 1145.000000
INFO[0008] current tps is 1626.000000
INFO[0009] current tps is 2425.000000
INFO[0010] current tps is 1703.000000
INFO[0011] current tps is 1772.000000
INFO[0012] current tps is 1823.000000
INFO[0013] current tps is 1213.000000
INFO[0014] current tps is 1974.000000
INFO[0015] current tps is 1965.000000
INFO[0016] current tps is 2001.000000
INFO[0017] current tps is 975.000000
INFO[0018] current tps is 1505.000000
INFO[0019] current tps is 2338.000000
INFO[0020] current tps is 1704.000000
INFO[0021] current tps is 1270.000000
INFO[0022] current tps is 2418.000000
INFO[0023] current tps is 1673.000000
INFO[0024] current tps is 997.000000
INFO[0025] current tps is 1935.000000
INFO[0026] current tps is 1840.000000
INFO[0027] current tps is 710.000000
INFO[0028] current tps is 1041.000000
INFO[0029] current tps is 837.000000
INFO[0030] current tps is 1403.000000
received interrupt signal, shutting down...
INFO[0030] finish testing duration=30.468557927 number=50880 tps=1669.9181872450927 tx_delay=968.0890066430818
```

View File

@ -0,0 +1,509 @@
# 中继链部署
中继链用于应用链的跨链管理以及跨链交易的可信验证与可靠路由是一种实现IBTP
协议的开放许可链。
### 安装包获取
##### 源码下载
首先拉区bitxhub源代码
```shell
git clone https://github.com/meshplus/bitxhub.git
```
代码目录结构如下:
| 目录 | 说明 |
| ------- | --------- |
| api | 网关和GRPC服务 |
| cmd | 命令行相关代码 |
| config | 配置文件 |
| docs | 设计和用户文档 |
| intenal | 项目内部相关代码 |
| pkg | 项目重用相关代码 |
| scripts | 操作脚本 |
| tester | 单元测试 |
##### 二进制下载
可以在github上下载已经打包好的二进制安装包地址如下`https://github.com/meshplus/bitxhub/releases`, 根据需要的版本进行下载即可。请注意在bitxhub v1.6.0及之后二进制包和部署配置示例文件将分为两个压缩包提供其中配置文件是以四节点bitxhub集群为示例文件命名以examples开头如果只下载配置文件仍需要将二进制程序拷贝到指定地方之后才能启动节点。
### 修改配置文件
中继链包括bitxhub.toml、network.tom和order.toml配置文件。下面以node1为例介绍如何修改配置文件。
##### 节点配置文件bitxhub.toml
bitxhub.toml文件是bitxhub启动的主要配置文件。各配置项说明如下
| 配置项 | 说明 |
| ---------- | ------------------------------------- |
| solo | 是否按照单节点模式启动BitXHub |
| [port] | gateway、grpc、pprof和monitor服务端口 |
| [monitor] | 监控服务 |
| [security] | 证书体系 |
| [cert] | 是否开启认证节点p2p通信证书 |
| [order] | 共识模块,作为插件进行加载 |
| [gateway] | 跨域配置 |
| [ping] | ping集群节点功能 |
| [log] | 日志输出相关设置 |
| [executor] | 执行引擎类型 |
| [genesis] | 创世节点配置 |
变更gateway、grpc、pprof和monitor等端口:
```javascript
[port]
gateway = 9091
grpc = 60011
pprof = 53121
monitor = 40011
```
共识算法类型选择支持raft和rbft
```shell
[order]
plugin = "plugins/raft.so"
```
执行引擎类型选择支持serial和parallel
```sh
[executor]
type = "serial"
```
修改genesis的节点验证者地址根据节点数量配置地址数量
```shell
[genesis]
addresses = [
"0xc7F999b83Af6DF9e67d0a37Ee7e900bF38b3D013",
"0x79a1215469FaB6f9c63c1816b45183AD3624bE34",
"0x97c8B516D19edBf575D72a172Af7F418BE498C37",
"0xc0Ff2e0b3189132D815b8eb325bE17285AC898f8"
]
```
##### 网络配置文件network.toml
network.toml文件是bitxhub网络配置文件。各配置项说明如下
| 配置项 | 说明 |
| ------- | -------------------------- |
| N | 集群节点数量 |
| id | 当前节点标识 |
| new | 判断当前节点是新加入的节点 |
| [nodes] | 集群节点信息 |
| account | 节点验证者地址 |
| hosts | 节点网络地址 |
| id | 节点标识 |
| pid | p2p网络唯一标识 |
配置过程变更nodes中各个节点的信息
```shell
[[nodes]]
account = "0xc7F999b83Af6DF9e67d0a37Ee7e900bF38b3D013"
hosts = ["/ip4/127.0.0.1/tcp/4001/p2p/"]
id = 1
pid = "QmXi58fp9ZczF3Z5iz1yXAez3Hy5NYo1R8STHWKEM9XnTL"
```
配置示例如下:
```javascript
id = 1 # self id
n = 4 # the number of vp nodes
new = false # track whether the node is a new node
[[nodes]]
account = "0xc7F999b83Af6DF9e67d0a37Ee7e900bF38b3D013"
hosts = ["/ip4/127.0.0.1/tcp/4001/p2p/"]
id = 1
pid = "QmXi58fp9ZczF3Z5iz1yXAez3Hy5NYo1R8STHWKEM9XnTL"
[[nodes]]
account = "0x79a1215469FaB6f9c63c1816b45183AD3624bE34"
hosts = ["/ip4/127.0.0.1/tcp/4002/p2p/"]
id = 2
pid = "QmbmD1kzdsxRiawxu7bRrteDgW1ituXupR8GH6E2EUAHY4"
[[nodes]]
account = "0x97c8B516D19edBf575D72a172Af7F418BE498C37"
hosts = ["/ip4/127.0.0.1/tcp/4003/p2p/"]
id = 3
pid = "QmQUcDYCtqbpn5Nhaw4FAGxQaSSNvdWfAFcpQT9SPiezbS"
[[nodes]]
account = "0xc0Ff2e0b3189132D815b8eb325bE17285AC898f8"
hosts = ["/ip4/127.0.0.1/tcp/4004/p2p/"]
id = 4
pid = "QmQW3bFn8XX1t4W14Pmn37bPJUpUVBrBjnPuBZwPog3Qdy"
```
##### 共识配置文件order.toml
order.toml文件是bitxhub共识配置文件。各配置项说明如下
| 配置项 | 说明 |
| ------ | ------------- |
| [raft] | raft 相关配置 |
| [rbft] | rbft 相关配置 |
| [solo] | solo相关配置 |
配置示例如下(无特殊情况不要修改此配置):
```javascript
[raft]
batch_timeout = "0.3s" # Block packaging time period.
tick_timeout = "0.1s" # TickTimeout is the internal logical clock for the Node by a single tick, Election timeouts and heartbeat timeouts are in units of ticks.
election_tick = 10 # ElectionTick is the number of Node.Tick invocations that must pass between elections.
heartbeat_tick = 1 # HeartbeatTick is the number of Node.Tick invocations that must pass between heartbeats.
max_size_per_msg = 1048576 # 1024*1024, MaxSizePerMsg limits the max size of each append message.
max_inflight_msgs = 500 # MaxInflightMsgs limits the max number of in-flight append messages during optimistic replication phase.
check_quorum = true # Leader steps down when quorum is not active for an electionTimeout.
pre_vote = true # PreVote prevents reconnected node from disturbing network.
disable_proposal_forwarding = true # This prevents blocks from being accidentally proposed by followers.
[raft.mempool]
batch_size = 200 # How many transactions should the primary pack.
pool_size = 50000 # How many transactions could the txPool stores in total.
tx_slice_size = 10 # How many transactions should the node broadcast at once
tx_slice_timeout = "0.1s" # Node broadcasts transactions if there are cached transactions, although set_size isn't reached yet
[raft.syncer]
sync_blocks = 1 # How many blocks should the behind node fetch at once
snapshot_count = 1000 # How many apply index(blocks) should the node trigger at once
[rbft] #RBFT configurations
set_size = 25 # How many transactions should the node broadcast at once
batch_size = 500 # How many transactions should the primary pack before sending pre-prepare
pool_size = 50000 # How many transactions could the txPool stores in total
vc_period = 0 # After how many checkpoint periods( Blocks = 10 * vcperiod ) the primary gets cycled automatically. ( Set 0 to disable )
check_interval = "3m" # interval of the check loop
tolerance_time = "5m" # The max tolerance time duration (in seconds) of out-of-date
batch_mem_limit = false # Indicates whether limit batch mem size or not
batch_max_mem = 10000 # The max memory size of one batch
[rbft.timeout]
sync_state = "3s" # How long to wait quorum sync state response
sync_interval = "1m" # How long to restart sync state process
recovery = "15s" # How long to wait before recovery finished(This is for release1.2)
first_request = "30s" # How long to wait before first request should come
batch = "0.5s"# Primary send a pre-prepare if there are pending requests, although batchsize isn't reached yet,
request = "6s" # How long may a request(transaction batch) take between reception and execution, must be greater than the batch timeout
null_request = "9s" # Primary send it to inform aliveness, must be greater than request timeout
viewchange = "8s" # How long may a view change take
resend_viewchange = "10s" # How long to wait for a view change quorum before resending (the same) view change
clean_viewchange = "60s" # How long to clean out-of-data view change message
update = "4s" # How long may a update-n take
set = "0.1s" # Node broadcasts transactions if there are cached transactions, although set_size isn't reached yet
[rbft.syncer]
sync_blocks = 1 # How many blocks should the behind node fetch at once
[solo]
batch_timeout = "0.3s" # Block packaging time period.
[solo.mempool]
batch_size = 200 # How many transactions should the primary pack.
pool_size = 50000 # How many transactions could the txPool stores in total.
tx_slice_size = 10 # How many transactions should the node broadcast at once
tx_slice_timeout = "0.1s" # Node broadcasts transactions if there are cached transactions, although set_size isn't reached yet
```
### 启动程序
##### 初始化配置
```
$ bitxhub init
```
##### 生成节点验证者私钥,并通过私钥获取验证者地址
```shell
$ bitxhub key gen --target ~/.bitxhub --name key
$ bitxhub key convert --priv .bitxhub/key.priv --save .bixthub/key.json
$ bitxhub key address --path .bitxhub/key.priv
0x0beb9583C069aeC5B5C3B395a1Ee644BFdd5Ce0D
```
##### 获取节点p2p通信网络pid
```
$ bitxhub cert priv gen --name node --target ~/.bitxhub
$ bitxhub cert priv pid --path ~/.bitxhub/node.priv
QmWAaFDQ3p2Hj383WsBGU2nLMtsJk1aT9obXXXxL5UyUuA
```
##### 拷贝共识插件
```
$ mkdir /.bitxhub/plugins
1. 拷贝raft共识插件, 若源码编译即在internal/plugins/build目录内
$ cp raft.so /.bitxhub/plugins
2. 拷贝rbft共识插件, 若源码编译即在bitxhub-order-rbft/build目录内
$ cp rbft.so ~/.bitxhub/plugins
```
##### 启动bitxhub
```
$ bitxhub --repo ~/.bitxhub start
```
待节点集群打印出bitxhub的LOGO表示bitxhub集群开始正常工作
![](../assets/bitxhub.png)
## 跨链网关部署
跨链网关Pier能够支持业务所在区块链便捷、快速的接入到跨链平台BitXHub中来从而实现和其他业务区块链的跨链操作。该跨链网关支持跨链消息格式转换、跨链消息的路由、跨链操作的调用等核心功能不仅保证不同格式的跨链消息能够安全可信的到达目标应用链而且保证了跨链交易异常情况下来源链的安全。跨链网关为区块链互联形成网络提供了便捷的接入方式旨在降低跨链互联的使用成本。下面是具体的安装部署教程。
### 安装包获取
##### 源码安装
跨链网关启动的话需要应用链插件,所以从源码安装的话,还需要编译相应的应用链插件的二进制。
```shell
# 编译跨链网关本身
cd $HOME
git clone https://github.com/meshplus/pier.git
cd pier
make prepare && make install
# 编译Fabric
cd $HOME
git clone https://github.com/meshplus/pier-client-fabric.git
cd pier-client-fabric
make fabric1.4
# 编译以太坊私链插件
cd $HOME
git clone https://github.com/meshplus/pier-client-ethereum.git
cd pier-client-ethereum
make eth
# 插件执行make的编译之后都会在项目目录的之下的build目录生成相应的 .so 文件
```
编译跨链网关步骤会在 $GOPATH/bin 下生成 pier 二进制,运行下面的命令查看是否安装成功:
```shell
pier version
```
如果正常安装会打印出类似下面的说明
```text
Pier version: 1.0.0-b01a80a
App build date: 2020-08-27T10:28:05
System version: darwin/amd64
Golang version: go1.13
```
代码目录结构如下:
| 目录 | 说明 |
| -------- | ------------------ |
| agent | 对接BitXHub的Client模块 |
| cmd | 命令行相关代码 |
| internal | 项目内部相关代码 |
| pkg | 项目重用相关代码 |
| plugins | 对接应用链的Client接口 |
| scripts | 操作脚本 |
**3.2.1.2 二进制安装**
没有现有编译环境的用户也可以在GitHub开源仓库下载编译好的二进制地址`https://github.com/meshplus/pier/releases`, 根据需要的版本进行下载即可。该部署包中包含了 Pier跨链网关的二进制和 pier-client-fabric 和 pier-client-ethereum 的应用链插件的二进制。
### 修改配置文件
在进行应用链注册、验证规则部署等步骤之前,需要初始化跨链网关的配置目录
```shell
#以用户目录下的pier为例
pier --repo=~/pier init
```
该命令会生成跨链网关的一些基础配置文件模板,使用 tree 命令可查看目录信息:
```text
tree -L 1 ~/.pier
├── api
├── certs
├── key.json
├── node.priv
└── pier.toml
1 directory, 4 files
```
导入插件二进制hyperchain的插件二进制和配置文件示例需要内部授权
```
mkdir -p ~/.pier/plugins
cp fabric-client-1.4.so ~/.pier/plugins
```
pier.toml 文件描述链跨链网关启动的必要配置,具体的配置项和说明如下:
| 配置项 | 说明 |
| ---------- | --------------------- |
| [port] | http、grpc服务端口 |
| [log] | 日志输出相关设置 |
| [bitxhub] | 连接的bitxhub的IP地址、验证人地址 |
| [appchain] | 对接的应用链的基础配置信息 |
主要需要修改的部分是端口信息、中继链的信息、应用链的信息
- 修改端口信息
```none
[port]
// 如果不冲突的话,可以不用修改
http = 8987
pprof = 44555
```
- 修改中继链信息
```none
[mode]
type = "relay" # relay or direct
[mode.relay]
addrs = ["localhost:60011", "localhost:60012", "localhost:60013", "localhost:60014"]
quorum = 2
validators = [
"0x000f1a7a08ccc48e5d30f80850cf1cf283aa3abd",
"0xe93b92f1da08f925bdee44e91e7768380ae83307",
"0xb18c8575e3284e79b92100025a31378feb8100d6",
"0x856E2B9A5FA82FD1B031D1FF6863864DBAC7995D",
]
```
- 修改应用链信息
```none
[appchain]
// 所连接的应用链对应的Plugin文件在跨链网关配置文件夹下的相对路径
plugin = "fabric-client-1.4.so"
// 所连接的应用链的配置文件夹在跨链网关配置文件夹下的相对路径
config = "fabric"
```
##### 修改fabric插件配置
Fabric插件配置的模板在`pier-client-fabric`项目中并且已经在GitHub上进行开源所以直接在GitHub上下载代码即可
```shell
# 转到pier-client-fabric项目路径下
git clone https://github.com/meshplus/pier-client-fabric.git && cd pier-client-fabric
cp ./config $HOME/.pier/fabric
```
配置目录结构
```shell
├── crypto-config/
├── config.yaml
├── fabric.toml
└── fabric.validators
```
主要修改Fabric网络配置验证证书跨链合约设置
- **Fabric证书配置**
启动Fabric网络时会生成所有节点包括Order、peer等的证书信息并保存在 crypto-config文件夹中Fabric插件和Fabric交互时需要用到这些证书。
```
# 复制你所部署的Fabric所产生的crypto-config文件夹
cp -r /path/to/crypto-config $HOME/.pier1/fabric/
# 复制Fabric上验证人证书
cp $HOME/.pier1/fabric/crypto-config/peerOrganizations/org2.example.com/peers/peer1.org2.example.com/msp/signcerts/peer1.org2.example.com-cert.pem $HOME/.pier1/fabric/fabric.validators
```
- **修改Plugin配置文件 config.yaml **
`config.yaml`文件记录的Fabric网络配置如果你是按照你自己的网络拓扑部署的Fabric用你的网络拓扑配置文件替换这个样例文件需要使用绝对路径把所有的路径都修改为 `crypto-config`文件夹所在的绝对路径
```
path: {CONFIG_PATH}/fabric/crypto-config => path: /home/alex/.pier/fabric/crypto-config
```
替换为你部署的Fabric网络的拓扑设置文件即可同时需要修改所有的Fabric 的IP地址
```
url: grpcs://localhost:7050 => url: grpcs://10.1.16.48:7050
```
- **修改Plugin配置文件 fabric.toml**
配置项和说明:
| 配置项 | 说明 |
| ------------ | ----------------------------------- |
| addr | Fabric 区块链所在的服务器地址和端口 |
| event_filter | 跨链合约中抛出的跨链事件的名称 |
| username | Fabric用户名称 |
| ccid | 所部署的跨链合约名称 |
| channel_id | 部署的跨链合约所在的channel |
| org | 部署的跨链合约所在的org |
示例配置
```
addr = "localhost:7053" // 若Fabric部署在服务器上该为服务器地址
event_filter = "interchain-event-name"
username = "Admin"
ccid = "broker" // 若部署跨链broker合约名字不是broker需要修改
channel_id = "mychannel"
org = "org2"
```
- **修改Plugin配置文件fabric.validators**
fabric.validators 是Fabric验证人的证书配置示例
```
-----BEGIN CERTIFICATE-----
MIICKTCCAc+gAwIBAgIRAIBO31aZaSZoEYSy2AJuhJcwCgYIKoZIzj0EAwIwczEL
MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG
cmFuY2lzY28xGTAXBgNVBAoTEG9yZzIuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2Nh
Lm9yZzIuZXhhbXBsZS5jb20wHhcNMjAwMjA1MDgyMjAwWhcNMzAwMjAyMDgyMjAw
WjBqMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMN
U2FuIEZyYW5jaXNjbzENMAsGA1UECxMEcGVlcjEfMB0GA1UEAxMWcGVlcjEub3Jn
Mi5leGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABG3jszFPTbGm
dAYg2BxmHMTDKfQReNw3p9ttMK130qF5lQo5zLBG8Sa3viOCLnvjjg6A/P+yKnwv
isI/jEVE8T2jTTBLMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMCsGA1Ud
IwQkMCKAIMVL+daK7nMGr2/AQIXTSPFkdd3UiPVDkWtkh5ujnalEMAoGCCqGSM49
BAMCA0gAMEUCIQDMYOQiYeMiQZTxlRkj/3/jjYvwwdCcX5AWuFmraiHkugIgFkX/
6uiTSD0lz8P+wwlLf24cIABq2aZyi8q4gj0YfwA=
-----END CERTIFICATE-----
```
### 启动程序
```
#以用户目录下的pier1为例
pier --repo=~/pier start
```
观察日志信息没有报错信息pier启动成功
**说明:因为跨链合约和验证规则的部署涉及到不同应用链的细节,且需依赖应用链的安装部署,具体操作请见快速开始手册或使用文档,这里不再赘述**

View File

@ -0,0 +1,251 @@
# 中继链部署
中继链用于应用链的跨链管理以及跨链交易的可信验证与可靠路由是一种实现IBTP协议的开放许可链。部署中继链节点主要是三个步骤安装包获取准备、配置文件修改和程序启动。下面依次来进行说明。
## 安装包获取
#### 源码下载编译
您可以自行拉取BitXHub项目的源码然后在本地编译bitxhub及插件的二进制文件具体操作步骤可参考如下
```
# 1. 首先拉取bitxhub项目源代码
git clone https://github.com/meshplus/bitxhub.git
# 2. 进入bitxhub目录切换到指定的分支或版本后编译bitxhub二进制
cd bitxhub && git checkout ${TAG} && make build
# 注意⚠首次编译需要在build之前先执行 make prepare 完成依赖安装
# 编译完成后可以在项目的bin目录下看到刚刚生成的bitxhub二进制文件
# 3. 接下来需要编译共识插件,进入到 internal/plugins 目录进行编译
cd internal/plugins && make plugins
# 编译完成后可以在项目的internal/plugins/build目录下看到刚刚生成的共识插件文件raft.so和solo.so
```
**提示在bitxhub v1.7.0及以上的版本我们也提供了一键生成部署所需的文件包的make命令make release-binary执行完成后可以在项目的dist目录看到符合您系统的压缩包解压即可使用。**
经过以上的步骤相信您已经编译出了部署中继链节点所需的二进制文件中继链节点运行还需要外部依赖库均在项目build目录下Macos使用libwasmer.dylibLinux使用libwasmer.so,建议将得到的二进制和适配的依赖库文件拷贝到同一目录,方便之后的操作。
#### 二进制直接下载
除了源码编译外我们也提供了直接下载BitXHub二进制的方式下载地址链接如下[BitXHub二进制包下载](https://github.com/meshplus/bitxhub/releases),链接中已经包含了所需的二进制和依赖库,您只需跟据实际情况选择合适的版本和系统下载即可。
## 配置文件修改
获取到安装包后,接下来要根据您的实际情况修改配置文件。如果您是在本地编译的二进制包,您也可以在项目根目录执行`make cluster`一键启动四节点raft共识的BitXHub集群。本章主要是介绍普适的配置方法所以接下来将从生成、修改、检查配置文件等步骤进行详细说明。
#### 生成BitXHub配置文件
BitXHub提供了生成初始配置文件的命令行执行步骤可参考如下
```
# 1. 新建节点启动目录下面以node1为例
mkdir -p node1
# 2. 在上一步新建的node1目录中生成初始配置
./bitxhub --repo ./node1 init
# 注意⚠如果执行失败提示依赖库找不到可以将上一章中以libwasmer开头的文件加入到PATH中并执行 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PATH
# 3. 将bitxhub和共识插件的二进制分别拷贝到node1和node1/plugins目录
cp bitxhub ./node1
cp *.so ./node1/plugins
# 4. 在node1目录生成当前节点的管理员私钥并转换成json格式
./bitxhub key gen --target ./node1 --name key
./bitxhub key convert --priv ./node1/key.priv
# 注意⚠需要手动将上一步输出的内容保存为key.json文件
# 5. 在node1目录生成当前节点的网络通信私钥
./bitxhub cert priv gen --name node --target ./node1
```
执行完后可以在node1目录下看到新生成的初始配置文件
<img src="../../assets/treeForNode1.png" alt="image-20210413151432472" style="zoom:50%;" />
中继链节点主要包括bitxhub.toml、network.tom和order.toml配置文件分别代表节点本身、节点网络以及节点共识方面的配置其中order.toml一般使用默认配置即可其它两个文件均需要根据实际部署情况进行修改接下来我们以node1为例进行说明。
#### bitxhub.toml文件配置修改
bitxhub.toml文件是BitXHub节点启动的主要配置文件。各配置项说明如下
| 配置项 | 说明 |
| -------------- | ------------------------------------- |
| **solo** | 是否按照单节点模式启动BitXHub |
| **[port]** | gateway、grpc、pprof和monitor服务端口 |
| **[pprof]** | 性能剖析配置 |
| **[monitor]** | 监控服务配置 |
| **[gateway]** | 跨域配置 |
| **[ping]** | ping集群节点功能 |
| **[security]** | 证书体系 |
| **[limiter]** | 流量控制配置 |
| **[log]** | 日志输出相关设置 |
| **[cert]** | 是否开启认证节点p2p通信证书 |
| **[order]** | 共识模块,作为插件进行加载 |
| **[executor]** | 执行引擎类型 |
| **[genesis]** | 创世节点配置 |
**在实际部署过程中需要修改的配置一般只有port、order和genesis的信息其它配置默认即可。** 以下为示例参考
1. 根据您机器实际分配的端口进行变更:
```javascript
[port]
gateway = 9091
grpc = 60011
pprof = 53121
monitor = 40011
```
2. 共识算法类型选择开源版本目前支持raft和solo
```shell
[order]
plugin = "plugins/raft.so"
```
3. 修改genesis的节点管理员地址和投票权重根据初始节点配置对应地址
```shell
[genesis]
[[genesis.admins]]
address = "0xc7F999b83Af6DF9e67d0a37Ee7e900bF38b3D013"
weight = 1
[[genesis.admins]]
address = "0x79a1215469FaB6f9c63c1816b45183AD3624bE34"
weight = 1
[[genesis.admins]]
address = "0x97c8B516D19edBf575D72a172Af7F418BE498C37"
weight = 1
[[genesis.admins]]
address = "0xc0Ff2e0b3189132D815b8eb325bE17285AC898f8"
weight = 1
```
**说明以上初始节点的address字段可以通过以下命令获取**
``` 
./bitxhub key address --path ./node1/key.priv
# 示例输出0x6F7BA26056bBC332AC132afF7AD107F7e45E5613
```
#### network.toml文件配置修改
network.toml文件是BitXHub节点网络配置文件各配置项说明如下
| 配置项 | 说明 |
| ----------- | -------------------------- |
| **N** | 集群节点数量 |
| **id** | 当前节点标识 |
| **new** | 判断当前节点是新加入的节点 |
| **[nodes]** | 集群节点信息 |
| **account** | 节点验证者地址 |
| **hosts** | 节点网络地址 |
| **id** | 节点标识 |
| **pid** | p2p网络唯一标识 |
**在实际部署过程中需要修改的配置一般是节点数量、nodes的信息其它配置默认即可。** 以下为示例参考
1. 配置当前节点集群的数量以及自身的id:
```
id = 1 # self id
n = 4 # the number of vp nodes
new = false # track whether the node is a new node
```
2. 配置集群中各个节点的信息
```shell
[[nodes]]
account = "0xc7F999b83Af6DF9e67d0a37Ee7e900bF38b3D013"
hosts = ["/ip4/127.0.0.1/tcp/4001/p2p/"]
id = 1
pid = "QmXi58fp9ZczF3Z5iz1yXAez3Hy5NYo1R8STHWKEM9XnTL"
```
**说明:上面 account就是上一节中bitxhub节点的address地址hosts中一般就改变ip地址即可节点的pid信息可以通过如下命令获取**
```javascript
./bitxhub cert priv pid --path ./node1/key.priv
# 示例输出QmWAaFDQ3p2Hj383WsBGU2nLMtsJk1aT9obXXXxL5UyUuA
```
#### order.toml文件配置修改
order.toml文件是bitxhub共识配置文件。各配置项说明如下
| 配置项 | 说明 |
| ------ | ------------- |
| [raft] | raft 相关配置 |
| [rbft] | rbft 相关配置 |
| [solo] | solo相关配置 |
配置示例如下(无特殊情况不要修改此配置):
```javascript
[raft]
batch_timeout = "0.3s" # Block packaging time period.
tick_timeout = "0.1s" # TickTimeout is the internal logical clock for the Node by a single tick, Election timeouts and heartbeat timeouts are in units of ticks.
election_tick = 10 # ElectionTick is the number of Node.Tick invocations that must pass between elections.
heartbeat_tick = 1 # HeartbeatTick is the number of Node.Tick invocations that must pass between heartbeats.
max_size_per_msg = 1048576 # 1024*1024, MaxSizePerMsg limits the max size of each append message.
max_inflight_msgs = 500 # MaxInflightMsgs limits the max number of in-flight append messages during optimistic replication phase.
check_quorum = true # Leader steps down when quorum is not active for an electionTimeout.
pre_vote = true # PreVote prevents reconnected node from disturbing network.
disable_proposal_forwarding = true # This prevents blocks from being accidentally proposed by followers.
[raft.mempool]
batch_size = 200 # How many transactions should the primary pack.
pool_size = 50000 # How many transactions could the txPool stores in total.
tx_slice_size = 10 # How many transactions should the node broadcast at once
tx_slice_timeout = "0.1s" # Node broadcasts transactions if there are cached transactions, although set_size isn't reached yet
[raft.syncer]
sync_blocks = 1 # How many blocks should the behind node fetch at once
snapshot_count = 1000 # How many apply index(blocks) should the node trigger at once
[rbft] #RBFT configurations
set_size = 25 # How many transactions should the node broadcast at once
batch_size = 500 # How many transactions should the primary pack before sending pre-prepare
pool_size = 50000 # How many transactions could the txPool stores in total
vc_period = 0 # After how many checkpoint periods( Blocks = 10 * vcperiod ) the primary gets cycled automatically. ( Set 0 to disable )
check_interval = "3m" # interval of the check loop
tolerance_time = "5m" # The max tolerance time duration (in seconds) of out-of-date
batch_mem_limit = false # Indicates whether limit batch mem size or not
batch_max_mem = 10000 # The max memory size of one batch
[rbft.timeout]
sync_state = "3s" # How long to wait quorum sync state response
sync_interval = "1m" # How long to restart sync state process
recovery = "15s" # How long to wait before recovery finished(This is for release1.2)
first_request = "30s" # How long to wait before first request should come
batch = "0.5s"# Primary send a pre-prepare if there are pending requests, although batchsize isn't reached yet,
request = "6s" # How long may a request(transaction batch) take between reception and execution, must be greater than the batch timeout
null_request = "9s" # Primary send it to inform aliveness, must be greater than request timeout
viewchange = "8s" # How long may a view change take
resend_viewchange = "10s" # How long to wait for a view change quorum before resending (the same) view change
clean_viewchange = "60s" # How long to clean out-of-data view change message
update = "4s" # How long may a update-n take
set = "0.1s" # Node broadcasts transactions if there are cached transactions, although set_size isn't reached yet
[rbft.syncer]
sync_blocks = 1 # How many blocks should the behind node fetch at once
[solo]
batch_timeout = "0.3s" # Block packaging time period.
[solo.mempool]
batch_size = 200 # How many transactions should the primary pack.
pool_size = 50000 # How many transactions could the txPool stores in total.
tx_slice_size = 10 # How many transactions should the node broadcast at once
tx_slice_timeout = "0.1s" # Node broadcasts transactions if there are cached transactions, although set_size isn't reached yet
```
## 启动中继链节点
启动bitxhub节点只需要一条命令
```
cd node1
./bitxhub --repo ./ start
```
待节点集群打印出bitxhub的LOGO表示bitxhub集群开始正常工作
![](../../assets/bitxhub.png)

View File

@ -0,0 +1,204 @@
# 跨链网关部署--接入Ethereum
跨链网关Pier能够支持业务所在区块链以下简称 应用链便捷、快速的接入到跨链平台BitXHub中来从而实现和其他业务区块链的跨链操作。跨链网关的部署需要提前确定应用链类型对应不同的插件和配置也需要提前在对应的应用链上部署跨链合约为了符合用户的部署流程和提升操作体验我们按接入应用链的类型来分别介绍说明跨链网关Pier的部署流程主要分为在应用链上部署跨链合约、获取和修改Pier部署文件、注册应用链、部署验证规则和节点程序启动这五个章节。
## 在Ethereum应用链上部署跨链合约
**注意在此操作之前您需要确认已经部署或可接入的Ethereum应用链** 在Ethereum上部署跨链合约的过程本质上和部署其它合约没有区别只是合约名称和代码文件需要替换。在Ethereum上部署合约的工具有很多您可以使[Remix](https://remix.ethereum.org/)进行合约的编译和部署,这里关键的是跨链合约的获取。
1. 下载pier-client-ethereum源码
```
git clone https://github.com/meshplus/pier-client-ethereum.git
```
2. 需要部署的合约文件就在example目录下后缀名是.sol注意切换到与Pier一致的分支或版本
3. 部署broker、transfer和data_swapper合约的过程不再赘述需要特别说明的是在安装完broker合约后需要将返回的合约地址填入transfer和data_swapper合约中`BrokerAddr`字段这样业务合约才能正确跨链调用。此外与Fabric一样业务合约需要broker管理合约审计后才能进行跨链交易。
## 获取和修改Pier部署文件
#### 安装包获取
##### 源码下载编译
部署跨链网关需要应用链插件,所以从源码安装的话还需要编译相应的应用链插件的二进制。
```shell
# 编译跨链网关本身
cd $HOME
git clone https://github.com/meshplus/pier.git
cd pier && git checkout {VERSION}
make prepare && make build
# 编译Fabric 插件
cd $HOME
git clone https://github.com/meshplus/pier-client-ethereum.git
cd pier-client-fabric && git checkout {VERSION}
make eth
# 说明1.ethereum插件编译之后会在项目目录的之下的build目录生成相应的文件2.pier编译之后会在项目bin目录生成相应的文件。
```
经过以上的步骤相信您已经编译出了部署Pier节点对接ethereum应用链所需的二进制文件Pier节点运行还需要外部依赖库均在项目build目录下Macos使用libwasmer.dylibLinux使用libwasmer.so,建议将得到的二进制和适配的依赖库文件拷贝到同一目录,方便之后的操作。
##### 二进制直接下载
除了源码编译外我们也提供了直接下载Pier及其插件二进制的方式下载地址链接如下[Pier二进制包下载](https://github.com/meshplus/pier/releases) 和 [ethereum插件二进制包下载](https://github.com/meshplus/pier-client-ethereum/releases)链接中已经包含了所需的二进制程序和依赖库,您只需跟据实际情况选择合适的版本和系统下载即可。
#### 修改配置文件
##### 修改Pier的配置
在进行应用链注册、验证规则部署等步骤之前需要初始化跨链网关的配置目录以用户目录下的pier为例
```shell
pier --repo=~/.pier init
```
该命令会生成跨链网关的一些基础配置文件模板,使用 tree 命令可查看目录信息:
```text
tree -L 1 ~/.pier
├── api
├── certs
├── key.json
├── node.priv
└── pier.toml
1 directory, 4 files
```
导入fabric插件二进制文件
```
mkdir -p ~/.pier/plugins
cp eth-client ~/.pier/plugins
```
pier.toml 描述链跨链网关启动的必要配置也是Pier的主要配置具体的配置项和说明如下
| 配置项 | 说明 |
| -------------- | ------------------------------------------------ |
| **[port]** | http、grpc服务端口 |
| **[log]** | 日志输出相关设置 |
| **[mode]** | 连接的中继链配置包括relay\direct\union三种模式 |
| **[security]** | Tls配置 |
| **[HA]** | 主备高可用配置 |
| **[appchain]** | 对接的应用链的基础配置信息 |
主要需要修改的是端口信息、中继链的信息、应用链的信息
- 修改端口信息
```none
[port]
# 如果不冲突的话,可以不用修改
http = 8987
pprof = 44555
```
- 修改中继链信息
```none
[mode]
type = "relay" # relay or direct
[mode.relay]
addrs = ["localhost:60011", "localhost:60012", "localhost:60013", "localhost:60014"]
quorum = 2
validators = [
"0x000f1a7a08ccc48e5d30f80850cf1cf283aa3abd",
"0xe93b92f1da08f925bdee44e91e7768380ae83307",
"0xb18c8575e3284e79b92100025a31378feb8100d6",
"0x856E2B9A5FA82FD1B031D1FF6863864DBAC7995D",
]
```
- 修改应用链信息
```none
[appchain]
# 所连接的应用链对应的Plugin文件在跨链网关配置文件夹下的相对路径
plugin = "eth-client"
# 所连接的应用链的配置文件夹在跨链网关配置文件夹下的相对路径
config = "ether"
```
##### 修改ethereum插件配置
Ethereum插件配置的模板在`pier-client-ethereum`项目中并且已经在GitHub上进行开源所以直接在GitHub上下载代码即可
```shell
# 转到pier-client-ethereum项目路径下
git clone https://github.com/meshplus/pier-client-ethereum.git && cd pier-client-ethereum
cp ./config $HOME/.pier/ether
```
重要配置如下:
```shell
├── account.key
├── ether.validators
├── ether.toml
├── password
└── validating.wasm
```
主要修改ethereum.toml文件需要根据应用链实际情况填写示例如下
```
[ether]
addr = "wss://kovan.infura.io/ws/v3/cc512c8c74c94938aef1c833e1b50b9a"
name = "ether-kovan"
## 此处合约地址需要替换成变量代表的实际字符串
contract_address = "$brokerAddr-kovan"
abi_path = "broker.abi"
key_path = "account.key"
password = "password"
```
## 注册Ethereum应用链
在启动跨链网关Pier之前需要先注册应用链并部署绑定验证规则这些操作均是Pier命令行发起这一章我们介绍注册Ethereum应用链的操作步骤。需要注意的是在v1.6.0及以上的版本注册应用链需要中继链BitXHub节点管理员进行投票投票通过之后才能接入。
1. Pier命令行发起应用链注册
```
# 以用户目录下的pier为例
pier --repo=~/.pier appchain register --name=ethereum --type=ether --consensusType POS --validators=~/.pier1/ether/ether.validators --desc="ethereum appchain for test" --version=1.0.0
# 发起注册后会打印出应用链id和提案id
appchain register successfully, chain id is 0xcb33b10104cd217aAB4b302e9BbeEd1957EDaA31, proposal id is 0xcb33b10104cd217aAB4b302e9BbeEd1957EDaA31-0
```
2. 中继链节点依次投票
```
# 进入bitxhub节点的安装目录用上一步得到的提案id进行投票
bitxhub --repo ../node1 client governance vote --id 0xcb33b10104cd217aAB4b302e9BbeEd1957EDaA31-0 --info approve --reason approve
# 投票完后会打印vote successfully!
```
当BitXHub集群超过半数的管理员投票通过后应用链注册成功如果BitXHub是solo模式则只需要一票同意即可可以通过如下命令查询提案状态`bitxhub --repo ../node1 client governance proposals --type AppchainMgr `
## 部署Ethereum验证规则
应用链只有在可用状态下可以部署验证规则即需要应用链注册成功后才可以进行规则部署。提前准备好验证规则文件validating.wasm使用以下Pier命令行进行部署。
```
#以用户目录下的pier为例
pier --repo=~/.pier rule deploy --path=~/.pier/ether/validating.wasm
```
## 启动跨链网关节点
在完成以上步骤之后,可以启动跨链网关节点了
```
#以用户目录下的pier为例
pier --repo=~/.pier start
```
观察日志信息没有报错信息可以正常同步到中继链上的区块信息即说明pier启动成功

View File

@ -0,0 +1,293 @@
# 跨链网关部署--接入Fabric
跨链网关Pier能够支持业务所在区块链以下简称 应用链便捷、快速的接入到跨链平台BitXHub中来从而实现和其他业务区块链的跨链操作。跨链网关的部署需要提前确定应用链类型对应不同的插件和配置也需要提前在对应的应用链上部署跨链合约为了符合用户的部署流程和提升操作体验我们按接入应用链的类型来分别介绍说明跨链网关Pier的部署流程主要分为在应用链上部署跨链合约、获取和修改Pier部署文件、注册应用链、部署验证规则和节点程序启动这五个章节。
## 在Fabric应用链上部署跨链合约
**注意在此操作之前您需要确认已经部署好Fabric1.4版本的应用链(推荐使用[官方的部署脚本](https://github.com/hyperledger/fabric-samples/tree/release-1.4)** 在fabric上部署跨链合约的过程本质上和部署其它合约没有区别只是合约名称和代码文件需要替换以下的操作可供参考。
1. 安装部署合约的工具fabric-clifabric官方提供
```
go get github.com/securekey/fabric-examples/fabric-cli/cmd/fabric-cli
```
2. 获取需要部署的合约文件并解压需要与pier的版本一致下面的{VERSION}根据实际情况更改,例如 v1.6.0
```
wget https://github.com/meshplus/pier-client-fabric/raw/{VERSION}/example/contracts.zip
#解压
unzip -q contracts.zip
```
3. 部署broker、transfer和data_swapper合约
```
#安装和示例化broker合约(必需)
fabric-cli chaincode install --gopath ./contracts --ccp broker --ccid broker --config "${CONFIG_YAML}" --orgid org2 --user Admin --cid mychannel
fabric-cli chaincode instantiate --ccp broker --ccid broker --config "${CONFIG_YAML}" --orgid org2 --user Admin --cid mychannel
#安装和示例化transfer合约(可选)
fabric-cli chaincode install --gopath ./contracts --ccp transfer --ccid transfer --config "${CONFIG_YAML}" --orgid org2 --user Admin --cid mychannel
fabric-cli chaincode instantiate --ccp transfer --ccid transfer --config "${CONFIG_YAML}" --orgid org2 --user Admin --cid mychannel
#安装和示例化data_swapper合约(可选)
fabric-cli chaincode install --gopath ./contracts --ccp data_swapper --ccid data_swapper --config "${CONFIG_YAML}" --orgid org2 --user Admin --cid mychannel
fabric-cli chaincode instantiate --ccp data_swapper --ccid data_swapper --config "${CONFIG_YAML}" --orgid org2 --user Admin --cid mychannel
#业务合约需要broker管理合约审计后,才能进行跨链交易
fabric-cli chaincode invoke --cid mychannel --ccid=broker \
--args='{"Func":"audit", "Args":["mychannel", "transfer", "1"]}' \
--user Admin --orgid org2 --payload --config "${CONFIG_YAML}"
fabric-cli chaincode invoke --cid mychannel --ccid=broker \
--args='{"Func":"audit", "Args":["mychannel", "data_swapper", "1"]}' \
--user Admin --orgid org2 --payload --config "${CONFIG_YAML}"
```
## 获取和修改Pier部署文件
#### 安装包获取
##### 源码下载编译
部署跨链网关需要应用链插件,所以从源码安装的话还需要编译相应的应用链插件的二进制。
```shell
# 编译跨链网关本身
cd $HOME
git clone https://github.com/meshplus/pier.git
cd pier && git checkout {VERSION}
make prepare && make build
# 编译Fabric 插件
cd $HOME
git clone https://github.com/meshplus/pier-client-fabric.git
cd pier-client-fabric && git checkout {VERSION}
make fabric1.4
# 说明1.fabric插件编译之后会在项目目录的之下的build目录生成相应的文件2.pier编译之后会在项目bin目录生成相应的文件。
```
经过以上的步骤相信您已经编译出了部署Pier节点对接fabric应用链所需的二进制文件Pier节点运行还需要外部依赖库均在项目build目录下Macos使用libwasmer.dylibLinux使用libwasmer.so,建议将得到的二进制和适配的依赖库文件拷贝到同一目录,方便之后的操作。
##### 二进制直接下载
除了源码编译外我们也提供了直接下载Pier及其插件二进制的方式下载地址链接如下[Pier二进制包下载](https://github.com/meshplus/pier/releases) 和 [fabric插件二进制包下载](https://github.com/meshplus/pier-client-fabric/releases)链接中已经包含了所需的二进制程序和依赖库,您只需跟据实际情况选择合适的版本和系统下载即可。
#### 修改配置文件
##### 修改Pier的配置
在进行应用链注册、验证规则部署等步骤之前需要初始化跨链网关的配置目录以用户目录下的pier为例
```shell
pier --repo=~/.pier init
```
该命令会生成跨链网关的一些基础配置文件模板,使用 tree 命令可查看目录信息:
```text
tree -L 1 ~/.pier
├── api
├── certs
├── key.json
├── node.priv
└── pier.toml
1 directory, 4 files
```
导入fabric插件二进制文件
```
mkdir -p ~/.pier/plugins
cp fabric-client ~/.pier/plugins
```
pier.toml 描述链跨链网关启动的必要配置也是Pier的主要配置具体的配置项和说明如下
| 配置项 | 说明 |
| -------------- | ------------------------------------------------ |
| **[port]** | http、grpc服务端口 |
| **[log]** | 日志输出相关设置 |
| **[mode]** | 连接的中继链配置包括relay\direct\union三种模式 |
| **[security]** | Tls配置 |
| **[HA]** | 主备高可用配置 |
| **[appchain]** | 对接的应用链的基础配置信息 |
主要需要修改的是端口信息、中继链的信息、应用链的信息
- 修改端口信息
```none
[port]
# 如果不冲突的话,可以不用修改
http = 8987
pprof = 44555
```
- 修改中继链信息
```none
[mode]
type = "relay" # relay or direct
[mode.relay]
addrs = ["localhost:60011", "localhost:60012", "localhost:60013", "localhost:60014"]
quorum = 2
validators = [
"0x000f1a7a08ccc48e5d30f80850cf1cf283aa3abd",
"0xe93b92f1da08f925bdee44e91e7768380ae83307",
"0xb18c8575e3284e79b92100025a31378feb8100d6",
"0x856E2B9A5FA82FD1B031D1FF6863864DBAC7995D",
]
```
- 修改应用链信息
```none
[appchain]
# 所连接的应用链对应的Plugin文件在跨链网关配置文件夹下的相对路径
plugin = "fabric-client"
# 所连接的应用链的配置文件夹在跨链网关配置文件夹下的相对路径
config = "fabric"
```
##### 修改fabric插件配置
Fabric插件配置的模板在`pier-client-fabric`项目中并且已经在GitHub上进行开源所以直接在GitHub上下载代码即可
```shell
# 转到pier-client-fabric项目路径下
git clone https://github.com/meshplus/pier-client-fabric.git && cd pier-client-fabric
cp ./config $HOME/.pier/fabric
```
配置目录结构
```shell
├── crypto-config/
├── config.yaml
├── fabric.toml
└── fabric.validators
```
主要修改Fabric网络配置验证证书跨链合约设置
- **Fabric证书配置**
启动Fabric网络时会生成所有节点包括Order、peer等的证书信息并保存在 crypto-config文件夹中Fabric插件和Fabric交互时需要用到这些证书。
```
# 复制您所部署的Fabric所产生的crypto-config文件夹
cp -r /path/to/crypto-config $HOME/.pier1/fabric/
# 复制Fabric上验证人证书
cp $HOME/.pier1/fabric/crypto-config/peerOrganizations/org2.example.com/peers/peer1.org2.example.com/msp/signcerts/peer1.org2.example.com-cert.pem $HOME/.pier1/fabric/fabric.validators
```
- **修改Plugin配置文件 config.yaml **
`config.yaml`文件记录的Fabric网络配置用您的网络拓扑配置文件替换这个样例文件需要使用绝对路径把所有的路径都修改为 `crypto-config`文件夹所在的绝对路径
```
path: {CONFIG_PATH}/fabric/crypto-config => path: /home/alex/.pier/fabric/crypto-config
```
替换为您部署的Fabric网络的拓扑设置文件即可同时需要修改所有的Fabric 的IP地址
```
url: grpcs://localhost:7050 => url: grpcs://10.1.16.48:7050
```
- **修改Plugin配置文件 fabric.toml**
配置项和说明:
| 配置项 | 说明 |
| ---------------- | ----------------------------------- |
| **addr** | Fabric 区块链所在的服务器地址和端口 |
| **event_filter** | 跨链合约中抛出的跨链事件的名称 |
| **username** | Fabric用户名称 |
| **ccid** | 所部署的跨链合约名称 |
| **channel_id** | 部署的跨链合约所在的channel |
| **org** | 部署的跨链合约所在的org |
示例配置
```
addr = "localhost:7053" // 若Fabric部署在服务器上该为服务器地址
event_filter = "interchain-event-name"
username = "Admin"
ccid = "broker" // 若部署跨链broker合约名字不是broker需要修改
channel_id = "mychannel"
org = "org2"
```
- **修改Plugin配置文件fabric.validators**
fabric.validators 是Fabric验证人的证书配置示例
```
-----BEGIN CERTIFICATE-----
MIICKTCCAc+gAwIBAgIRAIBO31aZaSZoEYSy2AJuhJcwCgYIKoZIzj0EAwIwczEL
MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG
cmFuY2lzY28xGTAXBgNVBAoTEG9yZzIuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2Nh
Lm9yZzIuZXhhbXBsZS5jb20wHhcNMjAwMjA1MDgyMjAwWhcNMzAwMjAyMDgyMjAw
WjBqMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMN
U2FuIEZyYW5jaXNjbzENMAsGA1UECxMEcGVlcjEfMB0GA1UEAxMWcGVlcjEub3Jn
Mi5leGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABG3jszFPTbGm
dAYg2BxmHMTDKfQReNw3p9ttMK130qF5lQo5zLBG8Sa3viOCLnvjjg6A/P+yKnwv
isI/jEVE8T2jTTBLMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMCsGA1Ud
IwQkMCKAIMVL+daK7nMGr2/AQIXTSPFkdd3UiPVDkWtkh5ujnalEMAoGCCqGSM49
BAMCA0gAMEUCIQDMYOQiYeMiQZTxlRkj/3/jjYvwwdCcX5AWuFmraiHkugIgFkX/
6uiTSD0lz8P+wwlLf24cIABq2aZyi8q4gj0YfwA=
-----END CERTIFICATE-----
```
## 注册Fabric应用链
在启动跨链网关Pier之前需要先注册应用链并部署绑定验证规则这些操作均是Pier命令行发起这一章我们介绍注册Fabric应用链的操作步骤。需要注意的是在v1.6.0及以上的版本注册应用链需要中继链BitXHub节点管理员进行投票投票通过之后才能接入。
1. Pier命令行发起应用链注册
```
# 以用户目录下的pier为例
pier --repo=~/.pier appchain register --name=fabric --type=fabric --consensusType raft --validators=~/.pier1/fabric/fabric.validators --desc="fabric appchain for test" --version=1.4.3
# 发起注册后会打印出应用链id和提案id
appchain register successfully, chain id is 0xcb33b10104cd217aAB4b302e9BbeEd1957EDaA31, proposal id is 0xcb33b10104cd217aAB4b302e9BbeEd1957EDaA31-0
```
2. 中继链节点依次投票
```
# 进入bitxhub节点的安装目录用上一步得到的提案id进行投票
bitxhub --repo ../node1 client governance vote --id 0xcb33b10104cd217aAB4b302e9BbeEd1957EDaA31-0 --info approve --reason approve
# 投票完后会打印vote successfully!
```
当BitXHub集群超过半数的管理员投票通过后应用链注册成功如果BitXHub是solo模式则只需要一票同意即可可以通过如下命令查询提案状态`bitxhub --repo ../node1 client governance proposals --type AppchainMgr `
## 部署Fabric验证规则
应用链只有在可用状态下可以部署验证规则即需要应用链注册成功后才可以进行规则部署。提前准备好验证规则文件validating.wasm使用以下Pier命令行进行部署。
```
#以用户目录下的pier为例
pier --repo=~/.pier rule deploy --path=~/.pier/fabric/validating.wasm
```
## 启动跨链网关节点
在完成以上步骤之后,可以启动跨链网关节点了
```
#以用户目录下的pier为例
pier --repo=~/.pier start
```
观察日志信息没有报错信息可以正常同步到中继链上的区块信息即说明pier启动成功

View File

@ -0,0 +1,71 @@
# 中继跨链 部署文档
中继链用于应用链的跨链管理以及跨链交易的可信验证与可靠路由是一种实现IBTP协议的开放许可链。目前BitXHub已经支持中继链之间相互连接达到跨链交易在中继链之间进行同步极大地提升了BitXHub的可扩展性。
### 部署前准备
安装包获取可参照单独部署中继链的文档进行下载和操作注意中继跨链至少需要部署两套中继链节点环境其中中继链之间通过特殊配置的pier我们称之为Upier相连由此可知部署中继跨链系统的关键是在于配置好Upier
### 修改BitXHub配置文件并启动两套节点
这部分内容也可参照单独部署中继链的文档进行部署待节点集群均打印出bitxhub的LOGO表示bitxhub集群开始正常工作
![](../assets/bitxhub.png)
## UPier配置和部署
### 部署前准备
安装包获取可参照单独部署跨链网关的文档进行下载和操作注意每套中继链节点均对应一个UPier所以也至少需要配置和部署两个Upier节点。
### 修改配置文件
UPier和常规部署的pier配置大体一致只是其启动的模式应选择为union具体配置项如下
```shell
#打开pier.toml进行配置修改
[mode]
type = "union" # relay, direct or union
...
...
[mode.union]
connectors = [
"/ip4/127.0.0.1/tcp/44502/p2p/QmSqkijKLziphdTHpjqx6nRwhogqjhrMv2uGdbga7SqmdN",
"/ip4/127.0.0.1/tcp/44501/p2p/QmXdcTWNXBCDyhmoQ2wuf8cwomjccWr2jZpD9jGZcwj8YY"
]
```
**注意上面connectors字段需要配置两方的UPier的地址其中后面那一段是根据pier私钥生成的id可以使用pier p2p id 命令来获取这也是union模式区别于中继模式的关键配置。**
导入应用链的二进制插件及相关的配置均可参照基础的配置文档进行操作
**说明:因为跨链合约和验证规则的部署涉及到不同应用链的细节,且需依赖应用链的安装部署,具体操作请见快速开始手册或使用文档,这里不再赘述**
### 互相注册中继链并启动
编写互相注册中继链的脚本然后启动Upier下面以Upier1为例
```
##互相注册中继链:
##1 通过UPier1向Relay1注册Relay1:
pier --repo=UPier1 appchain register --name Relay1 --type relaychain --desc Relay1 --version 1 --validators UPier1/genesis.json --addr localhost:60011
##回显: appchain register successfully, id is 0x454e2569dD093D09E5E8B4aB764692780D795C9a
##2 通过UPier1向Relay2注册Relay1:
pier --repo=UPier1 appchain register --name Relay1 --type relaychain --desc Relay1 --version 1 --validators UPier1/genesis.json --addr localhost:50011
##回显: appchain register successfully, id is 0x454e2569dD093D09E5E8B4aB764692780D795C9a
##3 启动UPier:
## 清除下Pier下的store目录
rm -rf UPier1/store
## 启动UPier1
pier --repo=UPier1 start
```
**说明1. 以上两个注册的步骤,--addr后面的参数分别是两套中继链节点的地址需要根据实际情况进行更改2. 另一个Upier2的脚本跟上面的内容基本一致相互注册的bitxhub地址调换即可这里不再赘述**
运行`pier --repo=UPier start`分别启动两个UPier其日志显示两个UPier连接成功两方的bitxhub节点日志上均显示 “appchain register successfully”即正常启动。

View File

@ -0,0 +1,239 @@
# 跨链网关直连模式部署
跨链网关Pier能够支持跨链消息格式转换、跨链消息的路由、跨链操作的调用等核心功能不仅保证不同格式的跨链消息能够安全可信的到达目标应用链而且保证了跨链交易异常情况下来源链的安全。跨链网关为区块链互联形成网络提供了便捷的接入方式旨在降低跨链互联的使用成本。在之前的文档中介绍了中继模式的pier安装步骤下面介绍直连模式下pier的安装步骤。请注意pier直连不依赖于中继链节点所以本文档不赘述bitxhub的部署了。
### 安装包获取
##### 源码安装
跨链网关启动的话需要应用链插件,所以从源码安装的话,还需要编译相应的应用链插件的二进制。
```shell
# 编译跨链网关本身
cd $HOME
git clone https://github.com/meshplus/pier.git
cd pier
make prepare && make install
# 编译Fabric
cd $HOME
git clone https://github.com/meshplus/pier-client-fabric.git
cd pier-client-fabric
make fabric1.4
# 编译以太坊私链插件
cd $HOME
git clone https://github.com/meshplus/pier-client-ethereum.git
cd pier-client-ethereum
make eth
# 插件执行make的编译之后都会在项目目录的之下的build目录生成相应的 .so 文件
```
编译跨链网关步骤会在 $GOPATH/bin 下生成 pier 二进制,运行下面的命令查看是否安装成功:
```shell
pier version
```
如果正常安装会打印出类似下面的说明
```text
Pier version: 1.0.0-b01a80a
App build date: 2020-08-27T10:28:05
System version: darwin/amd64
Golang version: go1.13
```
代码目录结构如下:
| 目录 | 说明 |
| -------- | ------------------ |
| agent | 对接BitXHub的Client模块 |
| cmd | 命令行相关代码 |
| internal | 项目内部相关代码 |
| pkg | 项目重用相关代码 |
| plugins | 对接应用链的Client接口 |
| scripts | 操作脚本 |
**3.2.1.2 二进制安装**
没有现有编译环境的用户也可以在GitHub开源仓库下载编译好的二进制地址`https://github.com/meshplus/pier/releases`, 根据需要的版本进行下载即可。该部署包中包含了 Pier跨链网关的二进制和 pier-client-fabric 和 pier-client-ethereum 的应用链插件的二进制。
### 修改配置文件
在进行应用链注册、验证规则部署等步骤之前,需要初始化跨链网关的配置目录
```shell
#以用户目录下的pier为例
pier --repo=~/pier init
```
该命令会生成跨链网关的一些基础配置文件模板,使用 tree 命令可查看目录信息:
```text
tree -L 1 ~/.pier
├── api
├── certs
├── key.json
├── node.priv
└── pier.toml
1 directory, 4 files
```
导入插件二进制hyperchain的插件二进制和配置文件示例需要内部授权
```
mkdir -p ~/.pier/plugins
cp fabric-client-1.4.so ~/.pier/plugins
```
pier.toml 文件描述链跨链网关启动的必要配置,具体的配置项和说明如下:
| 配置项 | 说明 |
| ---------- | --------------------- |
| [port] | http、grpc服务端口 |
| [log] | 日志输出相关设置 |
| [bitxhub] | 连接的bitxhub的IP地址、验证人地址 |
| [appchain] | 对接的应用链的基础配置信息 |
主要需要修改的部分是端口信息、中继链的信息、应用链的信息
- 修改端口信息
```none
[port]
// 如果不冲突的话,可以不用修改
http = 44544
pprof = 44555
```
- 修改跨链网关信息
```none
[mode]
type = "direct" # relay or direct
...
...
[mode.direct]
peers = ["/ip4/127.0.0.1/tcp/3003/p2p/QmXfAngyiAkb44ofp1633Ak4nKTKWaBhmQbvE1tsPJzQTX", "/ip4/127.0.0.1/tcp/3004/p2p/QmWLrVrbJxkZxBZsr2UmNEz7eLgCExW6KTax89wDRMXaWw"]
```
**注意上面peers字段需要配置两方的pier的p2p地址其中后面那一段是根据pier私钥生成的id可以使用pier p2p id 命令来获取,这也是直连模式区别于中继模式的关键配置。**
- 修改应用链信息
```none
[appchain]
// 所连接的应用链对应的Plugin文件在跨链网关配置文件夹下的相对路径
plugin = "fabric-client-1.4.so"
// 所连接的应用链的配置文件夹在跨链网关配置文件夹下的相对路径
config = "fabric"
```
##### 修改fabric插件配置
Fabric插件配置的模板在`pier-client-fabric`项目中并且已经在GitHub上进行开源所以直接在GitHub上下载代码即可
```shell
# 转到pier-client-fabric项目路径下
git clone https://github.com/meshplus/pier-client-fabric.git && cd pier-client-fabric
cp ./config $HOME/.pier/fabric
```
配置目录结构
```shell
├── crypto-config/
├── config.yaml
├── fabric.toml
└── fabric.validators
```
主要修改Fabric网络配置验证证书跨链合约设置
- **Fabric证书配置**
启动Fabric网络时会生成所有节点包括Order、peer等的证书信息并保存在 crypto-config文件夹中Fabric插件和Fabric交互时需要用到这些证书。
```
# 复制你所部署的Fabric所产生的crypto-config文件夹
cp -r /path/to/crypto-config $HOME/.pier1/fabric/
# 复制Fabric上验证人证书
cp $HOME/.pier1/fabric/crypto-config/peerOrganizations/org2.example.com/peers/peer1.org2.example.com/msp/signcerts/peer1.org2.example.com-cert.pem $HOME/.pier1/fabric/fabric.validators
```
- **修改Plugin配置文件 config.yaml **
`config.yaml`文件记录的Fabric网络配置如果你是按照你自己的网络拓扑部署的Fabric用你的网络拓扑配置文件替换这个样例文件需要使用绝对路径把所有的路径都修改为 `crypto-config`文件夹所在的绝对路径
```
path: {CONFIG_PATH}/fabric/crypto-config => path: /home/alex/.pier/fabric/crypto-config
```
替换为你部署的Fabric网络的拓扑设置文件即可同时需要修改所有的Fabric 的IP地址
```
url: grpcs://localhost:7050 => url: grpcs://10.1.16.48:7050
```
- **修改Plugin配置文件 fabric.toml**
配置项和说明:
| 配置项 | 说明 |
| ------------ | ----------------------------------- |
| addr | Fabric 区块链所在的服务器地址和端口 |
| event_filter | 跨链合约中抛出的跨链事件的名称 |
| username | Fabric用户名称 |
| ccid | 所部署的跨链合约名称 |
| channel_id | 部署的跨链合约所在的channel |
| org | 部署的跨链合约所在的org |
示例配置
```
addr = "localhost:7053" // 若Fabric部署在服务器上该为服务器地址
event_filter = "interchain-event-name"
username = "Admin"
ccid = "broker" // 若部署跨链broker合约名字不是broker需要修改
channel_id = "mychannel"
org = "org2"
```
- **修改Plugin配置文件fabric.validators**
fabric.validators 是Fabric验证人的证书配置示例
```
-----BEGIN CERTIFICATE-----
MIICKTCCAc+gAwIBAgIRAIBO31aZaSZoEYSy2AJuhJcwCgYIKoZIzj0EAwIwczEL
MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG
cmFuY2lzY28xGTAXBgNVBAoTEG9yZzIuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2Nh
Lm9yZzIuZXhhbXBsZS5jb20wHhcNMjAwMjA1MDgyMjAwWhcNMzAwMjAyMDgyMjAw
WjBqMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMN
U2FuIEZyYW5jaXNjbzENMAsGA1UECxMEcGVlcjEfMB0GA1UEAxMWcGVlcjEub3Jn
Mi5leGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABG3jszFPTbGm
dAYg2BxmHMTDKfQReNw3p9ttMK130qF5lQo5zLBG8Sa3viOCLnvjjg6A/P+yKnwv
isI/jEVE8T2jTTBLMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMCsGA1Ud
IwQkMCKAIMVL+daK7nMGr2/AQIXTSPFkdd3UiPVDkWtkh5ujnalEMAoGCCqGSM49
BAMCA0gAMEUCIQDMYOQiYeMiQZTxlRkj/3/jjYvwwdCcX5AWuFmraiHkugIgFkX/
6uiTSD0lz8P+wwlLf24cIABq2aZyi8q4gj0YfwA=
-----END CERTIFICATE-----
```
### 启动程序
```
#以用户目录下的pier1为例
pier --repo=~/pier start
```
观察日志信息没有报错信息pier启动成功
**说明1. 因为跨链合约和验证规则的部署涉及到不同应用链的细节且需依赖应用链的安装部署具体操作请见快速开始手册或使用文档这里不再赘述。2. 本文是以一方的跨链网关为例进行部署,而另一方的跨链网关的部署与之基本一样,这里不再赘述。**

View File

@ -0,0 +1,613 @@
# Go SDK
# 1 前言
此SDK文档面向BitXHub平台的应用开发者提供BitXHub Go SDK的使用指南。
# 2 接口使用流程示例
## 2.1 基础流程示例
为了更好的理解接口的使用本示例将从初始化Client部署合约调用合约和返回值解析这个大致流程作介绍具体详细接口可参考第三章SDK文档。
### 2.1.1 初始化Client
配置集群网络地址、日志以及密钥。
例如:
```go
privKey, err := asym.GenerateKeyPair(crypto.Secp256k1)
var cfg = &config{
addrs: []string{
"localhost:60011",
"localhost:60012",
"localhost:60013",
"localhost:60014",
},
logger: logrus.New(),
privateKey: privKey,
}
```
初始化Client所有RPC操作将通过该接口与BitXHub交互。
例如:
```go
cli, err := New(
WithAddrs(cfg.addrs),
WithLogger(cfg.logger),
WithPrivateKey(cfg.privateKey),
)
```
### 2.1.2 部署合约
开发者需提供已经编译的`WebAssembly`文件。
例如:
```go
contract, err := ioutil.ReadFile("./testdata/example.wasm")
```
通过client部署合约部署完成后可以获取合约地址`addr`。
例如:
```go
addr, err := cli.DeployContract(contract, nil) // 第二个参数为跨链交易nonce为nil时可以自动获取
```
### 2.1.3 调用合约
调用合约需传入合约地址、合约方法名和对应的参数。
例如:
```go
result, err := cli.InvokeXVMContract(addr, "a", nil, Int32(1), Int32(2)) // 方法名为a跨链交易nonce传参1传参2
```
### 2.1.4 返回值解析
得到返回值结果后,获得状态码可以判断是否调用成功,若调用成功,解析返回值可看到调用之后的结果。
例如:
```go
if cli.CheckReceipt(result) {
fmt.Println(string(result.Ret))
}
```
### 2.1.5 完整示例
```go
//获取wasm合约字节数组
contract, _ := ioutil.ReadFile("./testdata/example.wasm")
//部署合约,获取合约地址
addr, _ := cli.DeployContract(contract, nil)
//调用合约,获取交易回执
result, _ := cli.InvokeXVMContract(addr, "a", nil, Int32(1), Int32(2))
//判断合约调用交易成功与否,打印合约调用数据
if cli.CheckReceipt(result) {
fmt.Println(string(result.Ret))
}
```
## 2.2 应用链管理流程示例
本示例展示应用链管理流程中的注册、审核以及注销操作。
### 2.2.1 应用链注册
调用BVM合约的`Register`方法向BitXHub注册应用链。
例如:
```go
args := []*pb.Arg{
rpcx.String(""), //validators
rpcx.Int32(0), //consensus_type
rpcx.String("hyperchain"), //chain_type
rpcx.String("税务链"), //name
rpcx.String("趣链税务链"), //desc
rpcx.String("1.8"), //version
}
ret, err := cli.InvokeBVMContract(constant.InterchainContractAddr.Address(), "Register", nil, args...)
```
获取到成功的交易回执后,解析交易回执内容。
例如:
```go
{
"id": "0x5098cc26b0d485145fb8258d2e79c49886cd4662", \\应用链ID
"name": "税务链",
"validators": "",
"consensus_type": 0,
"status": 0,
"chain_type": "hyperchain",
"desc": "趣链税务链",
"version": "1.8"
}
```
### 2.2.2 应用链审核
调用BVM合约的`Aduit`方法向BitXHub审核应用链。
例如:
```go
args := []*pb.Arg{
rpcx.String(appchainID),
rpcx.Int32(1), //审核通过
rpcx.String(""), //desc
}
ret, err = cli.InvokeBVMContract(constant.InterchainContractAddr.Address(),"Aduit", nil, args...)
```
### 2.2.3 应用链注销
调用BVM合约的`DeleteAppchain`方法向BitXHub注销应用链。
例如:
```go
ret, err = cli.InvokeBVMContract(constant.InterchainContractAddr.Address(), "DeleteAppchain", nil, rpcx.String(appchainID))
```
## 2.3 验证规则使用示例
本示例展示验证规则中的注册、审核操作以及WebAssembly合约示例。
### 2.3.1 验证规则注册
调用BVM合约的`RegisterRule`方法向应用链注册验证规则WebAssembly合约这里我们需要先注册应用链和部署验证规则合约然后获取应用链ID和合约地址。
例如:
```go
ret, err = cli.InvokeBVMContract(constant.RoleContractAddr.Address(), "RegisterRule", nil, rpcx.String(chainAddr), rpcx.String(contractAddr))
```
### 2.3.2 验证规则审核
调用BVM合约的`Aduit`方法向BitXHub审核验证规则。
例如:
```go
args := []*pb.Arg{
rpcx.String(appchainID),
rpcx.Int32(1), //审核通过
rpcx.String(""), //desc
}
ret, err = cli.InvokeBVMContract(constant.RuleManagerContractAddr.Address(),"Aduit", nil, args...)
```
### 2.3.3 验证规则示例WebAssembly合约, Fabric实例
```rust
extern crate protobuf;
extern crate sha2;
use crate::crypto::ecdsa;
use crate::model::transaction;
use sha2::{Digest, Sha256};
pub fn verify(proof: &[u8], validator: &[u8]) -> bool {
let cap =
protobuf::parse_from_bytes::<transaction::ChaincodeActionPayload>(proof).expect("error");
let cap_act = cap.action.unwrap();
let endorsers = cap_act.endorsements;
let mut digest = Sha256::new();
let mut payload = cap_act.proposal_response_payload.to_owned();
payload.extend(&endorsers[0].endorser);
digest.input(&payload);
let digest_byte = digest.result();
return ecdsa::verify(
&endorsers[0].signature,
&digest_byte,
&validator,
ecdsa::EcdsaAlgorithmn::P256,
);
}
```
# 3 SDK文档
## 3.1 初始化和启动
### 3.1.1 初始化Client
用途调用该接口获取与中继链交互的Client。
参数:
- `opts` 是中继链的网络地址,日志以及密钥的配置。
```go
func New(opts ...Option) (*ChainClient, error)
```
### 3.1.2 停止Client
用途:调用该接口将与中继链交互的`Client`关闭。
```go
func Stop() error
```
## 3.2 交易接口
### 3.2.1 发送交易
用途:调用该接口向中继链发送交易,交易类型包括普通交易、跨链交易和智能合约。
参数:
- `tx`交易实例。
- `opts`跨链交易nonce。
```go
func SendTransaction(tx *pb.Transaction, opts *TransactOpts) (string, error)
```
### 3.2.2 查询交易回执
用途调用该接口向BitXHub查询交易回执。
参数:
- `hash`交易哈希。
```go
func GetReceipt(hash string) (*pb.Receipt, error)
```
用例:
```go
func TestChainClient_SendTransactionWithReceipt(t *testing.T) {
// 生成from私钥
privKey, _ := asym.GenerateKeyPair(crypto.Secp256k1)
// 生成to私钥
toPrivKey, _ := asym.GenerateKeyPair(crypto.Secp256k1)
// 配置Client
cli, _ := New(
WithAddrs(cfg.addrs),
WithLogger(cfg.logger),
WithPrivateKey(privKey),
)
// 获取from地址
from, _ := privKey.PublicKey().Address()
// 获取to地址
to, _ := toPrivKey.PublicKey().Address()
// 构建交易体
tx := &pb.Transaction{
From: from,
To: to,
Data: &pb.TransactionData{
Amount: 10,
},
Timestamp: time.Now().UnixNano(),
Nonce: rand.Int63(),
}
// 用from的私钥签名交易
_ = tx.Sign(privKey)
// 通过client发送交易
hash, _ := cli.SendTransaction(tx, nil)
// 获取交易回执,判断交易执行状态
ret, _ := cli.GetReceipt(hash)
require.Equal(t, tx.Hash().String(), ret.TxHash.String())
// 停止client
_ = cli.Stop()
}
```
### 3.2.3 查询交易
用途调用该接口向BitXHub查询交易。
参数:
- `hash`交易哈希。
```go
func GetTransaction(hash string) (*proto.GetTransactionResponse, error)
```
## 3.3 合约接口
合约类型:
- BVMBitXHub内置合约。
- XVMWebAssembly合约。
### 3.3.1 部署合约
用途调用该接口向BitXHub部署XVM合约返回合约地址。
参数:
- `contract`wasm合约编译后的字节数据。
- `opts`跨链交易nonce。
```go
func DeployContract(contract []byte, opts *TransactOpts) (contractAddr *types.Address, err error)
```
### 3.3.2 调用合约
用途:该接口向中继链调用合约获取交易回执。
参数:
- `vmType`合约类型BVM和XVM
- `address`合约地址;
- `method`合约方法;
- `opts`跨链交易nonce
- `args`合约方法参数。
```go
func InvokeContract(vmType pb.TransactionData_VMType, address types.Address, method string, opts *TransactOpts, args ...*pb.Arg) (*pb.Receipt, error)
```
调用XVM合约用例
```go
func TestChainClient_InvokeXVMContract(t *testing.T) {
privKey, err := asym.GenerateKeyPair(crypto.Secp256k1)
require.Nil(t, err)
cli, err := New(
WithAddrs(cfg.addrs),
WithLogger(cfg.logger),
WithPrivateKey(privKey),
)
require.Nil(t, err)
contract, err := ioutil.ReadFile("./testdata/example.wasm")
require.Nil(t, err)
addr, err := cli.DeployContract(contract, nil)
require.Nil(t, err)
result, err := cli.InvokeXVMContract(addr, "a", nil, Int32(1), Int32(2))
require.Nil(t, err)
require.Equal(t, "336", string(result.Ret))
}
```
调用BVM合约用例
```go
func TestChainClient_InvokeBVMContract(t *testing.T) {
privKey, err := asym.GenerateKeyPair(crypto.Secp256k1)
require.Nil(t, err)
cli, err := New(
WithAddrs(cfg.addrs),
WithLogger(cfg.logger),
WithPrivateKey(privKey),
)
require.Nil(t, err)
result, err := cli.InvokeBVMContract(constant.StoreContractAddr.Address(), "Set", nil, String("a"), String("10"))
require.Nil(t, err)
require.Nil(t, result.Ret)
res, err := cli.InvokeBVMContract(constant.StoreContractAddr.Address(), "Get", nil, String("a"))
require.Nil(t, err)
require.Equal(t, string(res.Ret), "10")
}
```
## 3.4 区块接口
### 3.4.1 查询区块
参数:
- `value`区块高度或者区块哈希。
- `blockType` 查询类型。
```go
GetBlock(value string, blockType pb.GetBlockRequest_Type) (*pb.Block, error)
```
### 3.4.2 批量查询区块
用途批量查询区块返回指定块高度范围start到end的区块信息。
参数:
- `start`指定范围的起始区块高度。
- `end`指定范围的结束区块高度。
```go
func GetBlocks(start uint64, end uint64) (*pb.GetBlocksResponse, error)
```
### 3.4.3 查询区块Meta
用途:返回当前链的高度和区块哈希。
```go
func GetChainMeta() (*pb.ChainMeta, error)
```
### 3.4.4 查询区块链状态
用途:返回当前区块链共识的状态(正常或者不正常)。
```go
func GetChainStatus() (*pb.Response, error)
```
## 3.5 订阅接口
### 3.5.1 订阅事件
用途:调用该接口向中继链发起订阅事件的。
参数:
- `type` 事件类型,包含区块事件,区块头事件,跨链交易事件。
```go
func Subscribe(ctx context.Context, typ pb.SubscriptionRequest_Type, extra []byte) (<-chan interface{}, error)
```
用例:
```go
func TestChainClient_Subscribe(t *testing.T) {
privKey, err := asym.GenerateKeyPair(crypto.Secp256k1)
require.Nil(t, err)
from, err := privKey.PublicKey().Address()
require.Nil(t, err)
cli, err := New(
WithAddrs(cfg.addrs),
WithLogger(cfg.logger),
WithPrivateKey(privKey),
)
require.Nil(t, err)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c, err := cli.Subscribe(ctx, pb.SubscriptionRequest_BLOCK, nil)
assert.Nil(t, err)
go func() {
tx := &pb.Transaction{
From: from,
To: from,
Timestamp: time.Now().UnixNano(),
Nonce: rand.Int63(),
}
err = tx.Sign(privKey)
require.Nil(t, err)
hash, err := cli.SendTransaction(tx, nil)
require.Nil(t, err)
require.EqualValues(t, 66, len(hash))
}()
for {
select {
case block := <-c:
if block == nil {
assert.Error(t, fmt.Errorf("channel is closed"))
return
}
if err := cli.Stop(); err != nil {
return
}
return
case <-ctx.Done():
return
}
}
}
```
### 3.5.2 获取MerkleWrapper
用途获取指定区块高度范围的MerkleWrapper
参数:
- `pid` 应用链ID。
- `begin` 起始区块高度。
- `end` 结束区块高度。
- `ch` Merkle Wrapper的通道。
```go
func GetInterchainTxWrappers(ctx context.Context, pid string, begin, end uint64, ch chan<- *pb.InterchainTxWrapper) error
```
## 3.6 其它接口
### 3.6.1 查询节点网络信息
用途:返回当前区块链网络的节点信息。
```go
func GetNetworkMeta() (*pb.Response, error)
```
### 3.6.2 查询账户余额
参数:
- `address`地址。
```go
func GetAccountBalance(address string) (*pb.Response, error)
```
### 3.6.3 删除节点
用途删除区块链网络中的节点须往f+1个节点发送请求才可以删除
参数:
- `pid`节点的pid
```go
func DelVPNode(pid string) (*pb.Response, error)
```

View File

@ -0,0 +1,465 @@
# Java SDK
# 1 前言
此SDK文档面向BitXHub平台的应用开发者提供BitXHub Java SDK的使用指南。
# 2 接口使用流程示例
## 2.1 基础流程示例
为了更好的理解接口的使用本示例将从初始化Client部署合约调用合约和返回值解析这个大致流程作介绍具体详细接口可参考第三章SDK文档。
### 2.1.1 初始化Client
使用默认的配置类初始化Grpc Client。
例如:
```java
GrpcClient client = new GrpcClientImpl(Config.defaultConfig());
```
使用定制化的配置类初始化Grpc Client。
例如:
```Java
Config config = Config.builder()
.host("localhost")
.port(60011)
.ecKey(new ECKeyP256())
.build();
GrpcClient client = new GrpcClientImpl(config);
```
### 2.1.2 部署合约
开发者需提供已经编译的`WebAssembly`文件。
例如:
```java
byte[] contractBytes = IOUtils.toByteArray(new FileInputStream("./example.wasm"));
```
通过client部署合约部署完成后可以获取合约地址`contractAddress`。
例如:
```java
String contractAddress = client.deployContract(contractBytes);
```
### 2.1.3 调用合约
调用合约需传入合约地址、合约方法名和对应的参数。
例如:
```java
ReceiptOuterClass.Receipt receipt =
client.invokeXVMContract(contractAddress, "a", Types.i32(1), Types.i32(1)); \\方法名为a传参1传参2
```
### 2.1.4 返回值解析
得到返回值结果后,获得状态码可以判断是否调用成功,若调用成功,解析返回值可看到调用之后的结果。
例如:
```java
if (receipt.getStatus() == ReceiptOuterClass.Receipt.Status.SUCCESS) {
log.info(receipt.getRet().toStringUtf8());
}
```
### 2.1.5 完整示例
```java
//获取wasm字节数组
byte[] contractBytes = IOUtils.toByteArray(
new FileInputStream("./example.wasm"));
//部署合约,获取合约地址
String contractAddress = client.deployContract(contractBytes);
//调用合约,获取交易回执
ReceiptOuterClass.Receipt receipt = client.invokeXVMContract(contractAddress, "a", Types.i32(1), Types.i32(1));
//判断合约调用交易成功与否,打印合约调用数据
if (receipt.getStatus() == ReceiptOuterClass.Receipt.Status.SUCCESS) {
log.info(receipt.getRet().toStringUtf8());
}
```
## 2.2 应用链管理流程示例
本示例展示应用链管理流程中的注册、审核以及注销操作。
### 2.2.1 应用链注册
调用BVM合约的`Register`方法向BitXHub注册应用链。
例如:
```java
ArgOuterClass.Arg[] args = Types.toArgArray(
Types.string(""), //validators
Types.i32(0), //consensus_type
Types.string("hyperchain"), //chain_type
Types.string("税务链"), //name
Types.string("趣链税务链"), //desc
Types.string("1.8")); //version
Types.string("")); //public key
ReceiptOuterClass.Receipt receipt = client.invokeBVMContract(BVMAddr.APPCHAIN_MANAGER_CONTRACT_ADDR, "Register", args);
```
获取到成功的交易回执后,解析交易回执内容。
例如:
```java
{
"id": "0x5098cc26b0d485145fb8258d2e79c49886cd4662", \\应用链ID
"name": "税务链",
"validators": "",
"consensus_type": 0,
"status": 0,
"chain_type": "hyperchain",
"desc": "趣链税务链",
"version": "1.8",
"public_key": ""
}
```
### 2.2.2 应用链审核
调用BVM合约的`Audit`方法向BitXHub审核应用链。
例如:
```java
ArgOuterClass.Arg[] adultArgs = Types.toArgArray(
Types.string(appchainID), //应用链ID
Types.i32(1), //审核通过
Types.string("")); //描述信息
ReceiptOuterClass.Receipt adultReceipt = client.invokeBVMContract(BVMAddr.APPCHAIN_MANAGER_CONTRACT_ADDR, "Audit", adultArgs);
```
### 2.2.3 应用链注销
调用BVM合约的`DeleteAppchain`方法向BitXHub注销应用链。
例如:
```java
ArgOuterClass.Arg[] deleteArgs = Types.toArgArray(
Types.string(appchainID); //应用链ID
ReceiptOuterClass.Receipt deleteReceipt = client.invokeBVMContract(BVMAddr.APPCHAIN_MANAGER_CONTRACT_ADDR, "DeleteAppchain", deleteArgs);
```
## 2.3 验证规则使用示例
本示例展示验证规则中的注册、审核操作以及WebAssembly合约示例。
### 2.3.1 验证规则注册
调用BVM合约的`RegisterRule`方法向应用链注册验证规则WebAssembly合约这里我们需要先注册应用链和部署验证规则合约然后获取应用链ID和合约地址。
例如:
```java
ArgOuterClass.Arg[] ruleArgs = Types.toArgArray(
Types.string(appchainID),
Types.string(contractAddress));
ReceiptOuterClass.Receipt ruleReceipt = client.invokeBVMContract(BVMAddr.RULE_MANAGER_CONTRACT_ADDR, "RegisterRule", ruleArgs);
```
### 2.3.2 验证规则审核
调用BVM合约的`Audit`方法向BitXHub审核验证规则。
例如:
```java
ArgOuterClass.Arg[] adultArgs = Types.toArgArray(
Types.string(contractAddress), //验证规则的合约地址
Types.i32(1), //审核通过
Types.string("")); //描述信息
ReceiptOuterClass.Receipt adultReceipt = client.invokeBVMContract(BVMAddr.RULE_MANAGER_CONTRACT_ADDR, "Audit", adultArgs);
```
### 2.3.3 验证规则示例WebAssembly合约, Fabric实例
```rust
extern crate protobuf;
extern crate sha2;
use crate::crypto::ecdsa;
use crate::model::transaction;
use sha2::{Digest, Sha256};
pub fn verify(proof: &[u8], validator: &[u8]) -> bool {
let cap =
protobuf::parse_from_bytes::<transaction::ChaincodeActionPayload>(proof).expect("error");
let cap_act = cap.action.unwrap();
let endorsers = cap_act.endorsements;
let mut digest = Sha256::new();
let mut payload = cap_act.proposal_response_payload.to_owned();
payload.extend(&endorsers[0].endorser);
digest.input(&payload);
let digest_byte = digest.result();
return ecdsa::verify(
&endorsers[0].signature,
&digest_byte,
&validator,
ecdsa::EcdsaAlgorithmn::P256,
);
}
```
# 3 SDK文档
## 3.1 初始化
### 3.1.1 初始化Client
用途调用该接口获取与BitXHub交互的Client。
```java
GrpcClient client = new GrpcClientImpl(Config.defaultConfig());
```
入参:`Config` 是BitXHub的网络地址, 端口以及密钥的配置。
返回值与BitXHub交互的`Client`。
### 3.1.2 停止Client
用途调用该接口将与BitXHub交互的`Client`关闭。
```java
public void stop() throws InterruptedException
```
## 3.2 交易接口
### 3.2.1 发送交易
用途调用该接口向BitXHub发送交易交易类型包括普通交易、跨链交易和智能合约交易。
参数:
- `transaction`交易。
- `opts`跨链交易nonce。
```java
public String sendTransaction(TransactionOuterClass.Transaction transaction, TransactOpts opts);
```
用例:
```java
public void sendTransaction() {
TransactionOuterClass.Transaction unsignedTx = TransactionOuterClass.Transaction.newBuilder()
.setFrom(ByteString.copyFrom(from))
.setTo(ByteString.copyFrom(to))
.setTimestamp(Utils.genTimestamp())
.setPayload(TransactionOuterClass.TransactionData.newBuilder().setAmount(100000L).build().toByteString())
.build();
TransactionOuterClass.Transaction signedTx = SignUtils.sign(unsignedTx, config.getEcKey());
String txHash = client.sendTransaction(signedTx, null);
}
```
### 3.2.2 查询交易回执
参数:
- `hash`交易哈希。
```java
ReceiptOuterClass.Receipt getReceipt(String hash);
```
### 3.2.3 查询交易
参数:
- `hash`交易哈希。
```java
Broker.GetTransactionResponse getTransaction(String hash);
```
## 3.3 合约接口
合约类型:
- BVMBitXHub内置合约
- XVMWebAssembly合约
### 3.3.1 部署合约
用途调用该接口向BitXHub部署XVM合约。
参数:
- `contract`合约数据。
```java
String deployContract(byte[] contract);
```
### 3.3.2 调用合约
用途调用该接口向BitXHub调用BVM或者XVM合约。
参数:
- `vmType`合约类型BVM和XVM。
- `contractAddress`合约地址。
- `method `合约方法;
- `args`合约方法参数。
```java
ReceiptOuterClass.Receipt invokeContract(TransactionOuterClass.TransactionData.VMType vmType, String contractAddress, String method, ArgOuterClass.Arg... args);
```
用例:
```java
public void invokeContract() throws IOException {
byte[] contractBytes = IOUtils.toByteArray(
new FileInputStream("./example.wasm"));
String contractAddress = client.deployContract(contractBytes);
ReceiptOuterClass.Receipt receipt = client.invokeContract(TransactionOuterClass.TransactionData.VMType.XVM
, contractAddress, "a", Types.i32(1), Types.i32(1));
}
```
## 3.4 区块接口
### 3.4.1 查询区块
参数:
- `value`区块高度或者区块哈希。
- `type` 查询类型。`type类型`。
```java
BlockOuterClass.Block getBlock(String value, Broker.GetBlockRequest.Type type);
```
### 3.4.2 批量查询区块
用途批量查询区块返回指定块高度范围start到end的区块信息。
参数:
- `start`指定范围的起始区块高度。
- `end`指定范围的结束区块高度。
```java
Broker.GetBlocksResponse getBlocks(Long start, Long end);
```
### 3.4.3 查询区块Meta
用途:返回当前链的高度和区块哈希。
```java
Chain.ChainMeta getChainMeta();
```
### 3.4.4 查询区块链状态
用途:返回当前区块链共识的状态(正常或者不正常)。
```java
Broker.Response getChainStatus();
```
## 3.5 订阅接口
### 3.5.1 订阅事件
用途调用该接口向BitXHub发起订阅事件。
参数:
- `streamObserver` 事件通道。
- `type` 事件类型。
用例:
```java
void subscribe(Broker.SubscriptionRequest.Type type, StreamObserver<Broker.Response> observer);
```
```java
public void subscribe() throws InterruptedException {
CountDownLatch asyncLatch = new CountDownLatch(1);
StreamObserver<Broker.Response> observer = new StreamObserver<Broker.Response>() {
@Override
public void onNext(Broker.Response response) {
ByteString data = response.getData();
BlockOuterClass.Block block = null;
try {
block = BlockOuterClass.Block.parseFrom(data);
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
}
@Override
public void onError(Throwable throwable) {
throwable.printStackTrace();
asyncLatch.countDown();
}
@Override
public void onCompleted() {
asyncLatch.countDown();
}
};
client.subscribe(Broker.SubscriptionRequest.Type.BLOCK, observer);
asyncLatch.await();
}
```
## 3.6 其它接口
### 3.6.1 查询节点网络信息
用途:返回当前区块链网络的节点信息。
```java
Broker.Response getNetworkMeta();
```
### 3.6.2 查询账户余额
参数:
- `address`地址。
```java
Broker.Response getAccountBalance(String address);
```

View File

@ -0,0 +1,323 @@
# Js SDK
# 1 前言
此SDK文档面向BitXHub平台的应用开发者提供BitXHub JS SDK的使用指南。
# 2 接口使用流程示例
为了更好的理解接口的使用本示例将从初始化Client部署合约调用合约和返回值解析这个大致流程作介绍具体详细接口可参考第三章SDK文档。
## 2.1 基本流程
### 2.1.1 安装并初始化Client
可以通过npm安装JS SDK并引入到JS的项目中
```shell
npm install @meshplus/js-bitxhub-client@1.5.0
```
JS SDK分为几个磨块供用户分开进行调用分别为Client, PbType, Config, Transaction, Block, TripleDES以及AES。
用户在调用JS SDK与BitXHub进行交互时主要是需要使用Client, PbType和Config这三个模块。
用户引入JS SDK的库以后首先需要配置网络地址和接口。
例如:
```javascript
import { Config } from '@meshplus/js-bitxhub-client';
Config.setHosts(["localhost"]);
Config.setPorts(["9091"]);
```
初始化Client所有操作将通过该对象与BitXHub交互。
例如:
```javascript
import { Client } from '@meshplus/js-bitxhub-client';
let client = new Client(privateKey);
```
### 2.1.2 部署合约
开发者需提供已经编译的`WebAssembly`文件。
例如:
```javascript
import { fs } from 'fs';
let contract = fs.readFileSync("./testdata/example.wasm");
```
通过client部署合约部署完成后可以获取合约地址`addr`。
例如:
```javascript
let address = await cli.DeployContract(contract)
```
### 2.1.3 调用合约
调用合约需传入合约地址、合约方法名和对应的参数。
例如:
```javascript
result = cli.InvokeContract(0, address, "a", PbType.pbInt32(1), PbType.pbInt32(2))
//第一个参数指定调用XVM合约还是BVM合约第二个参数是合约地址 方法名为a传参1传参2
```
### 2.1.4 完整示例
```javascript
import { fs } from 'fs';
import { Client } from '@meshplus/js-bitxhub-client';
let contract = fs.readFileSync("./testdata/example.wasm");
let client = new Client(privateKey);
//部署合约,获取合约地址
let address = await cli.DeployContract(contract);
//调用合约,获取交易回执
result = cli.InvokeContract(1, address, "a", PbType.pbInt32(1), PbType.pbInt32(2));
//打印合约返回数据
console.log(result);
```
## 2.2 应用链管理流程示例
本示例展示应用链管理流程中的注册、审核以及注销操作。
### 2.2.1 应用链注册
调用BVM合约的`Register`方法向BitXHub注册应用链。
例如:
```javascript
let ret = cli.InvokeContract(0, InterchainContractAddr, "Register", PbType.pbString(validator),
PbType.pbInt32(0), PbType.pbString(chainType), PbType.pbString(name),
PbType.pbString(desc), PbType.pbString(version), PbType.pbString(pubKey)
);
```
获取到成功的交易回执后,得到交易回执内容。
例如:
```javascript
{
"id": "0x5098cc26b0d485145fb8258d2e79c49886cd4662", \\应用链ID
"name": "税务链",
"validators": "",
"consensus_type": 0,
"status": 0,
"chain_type": "hyperchain",
"desc": "趣链税务链",
"version": "1.8"
}
```
### 2.2.2 应用链审核
调用BVM合约的`Aduit`方法向BitXHub审核应用链。
例如:
```javascript
let ret = cli.InvokeContract(0, InterchainContractAddr, "Aduit", PbType.pbString(address),
PbType.pbInt32(1), PbType.pbString(desc)
);
```
### 2.2.3 应用链注销
调用BVM合约的`DeleteAppchain`方法向BitXHub注销应用链。
例如:
```javascript
let ret = cli.InvokeContract(0, InterchainContractAddr, "DeleteAppchain", PbType.pbString(address));
```
## 2.3 验证规则使用示例
本示例展示验证规则中的注册、审核操作以及WebAssembly合约示例。
### 2.3.1 验证规则注册
调用BVM合约的`RegisterRule`方法向应用链注册验证规则WebAssembly合约这里我们需要先注册应用链和部署验证规则合约然后获取应用链ID和合约地址。
例如:
```javascript
let ret = cli.InvokeContract(0, RoleContractAddr, "RegisterRule", PbType.pbString(chainAddr), PbType.pbString(contractAddr));
```
### 2.3.2 验证规则审核
调用BVM合约的`Aduit`方法向BitXHub审核验证规则。
例如:
```javascript
let ret = cli.InvokeContract(0, RoleContractAddr, "Aduit", PbType.pbString(chainAddr),
PbType.pbInt32(1), PbType.pbString(desc)
);
```
### 2.3.3 验证规则示例WebAssembly合约, Fabric实例
```rust
extern crate protobuf;
extern crate sha2;
use crate::crypto::ecdsa;
use crate::model::transaction;
use sha2::{Digest, Sha256};
pub fn verify(proof: &[u8], validator: &[u8]) -> bool {
let cap =
protobuf::parse_from_bytes::<transaction::ChaincodeActionPayload>(proof).expect("error");
let cap_act = cap.action.unwrap();
let endorsers = cap_act.endorsements;
let mut digest = Sha256::new();
let mut payload = cap_act.proposal_response_payload.to_owned();
payload.extend(&endorsers[0].endorser);
digest.input(&payload);
let digest_byte = digest.result();
return ecdsa::verify(
&endorsers[0].signature,
&digest_byte,
&validator,
ecdsa::EcdsaAlgorithmn::P256,
);
}
```
# 3 SDK文档
## 3.1 交易接口
### 3.1.1 发送交易
用途:调用该接口向中继链发送交易,交易类型包括普通交易、跨链交易和智能合约。
参数:
- `tx`交易实例。
```javascript
function SendTransaction(transaction)
```
### 3.1.2 查询交易回执
用途调用该接口向BitXHub查询交易回执。
参数:
- `hash`交易哈希。
```javascript
function GetReceipt(hash)
```
### 3.1.3 查询交易
用途调用该接口向BitXHub查询交易。
参数:
- `hash`交易哈希。
```javascript
function GetTransaction(hash)
```
## 3.2 合约接口
合约类型:
- BVMBitXHub内置合约。
- XVMWebAssembly合约。
### 3.2.1 部署合约
用途调用该接口向BitXHub部署XVM合约返回合约地址。
参数:
- `ctx`wasm合约编译后的字节数据。
```javascript
function DeployContract(ctx)
```
### 3.2.2 调用合约
用途:该接口向中继链调用合约获取交易回执。
参数:
- `vmType`合约类型BVM和XVM
- `address`合约地址;
- `method`合约方法;
- `args`合约方法参数。
```javascript
function InvokeContract(vmType, address, method, ...args)
```
## 3.3 区块接口
### 3.3.1 查询区块
参数:
- `value`区块高度或者区块哈希。
- `type` 查询类型。
```javascript
function GetBlock(type, value)
```
### 3.3.2 批量查询区块
用途批量查询区块返回指定块高度范围start到end的区块信息。
参数:
- `start`指定范围的起始区块高度。
- `end`指定范围的结束区块高度。
```javascript
function GetBlocks(start, end)
```

View File

@ -0,0 +1,225 @@
# Goduck运维小工具
# 1 安装
---
## 1.1 获取源码
```
git clone git@github.com:meshplus/goduck
```
## 1.2 编译安装
```
cd goduck
sudo make install
```
## 1.3 初始化
```
goduck init
```
使用之前一定要先初始化
# 2 使用
## 2.1 命令格式
```
goduck [global options] command [command options] [arguments...]
```
**command**
- `deploy` 远程部署bitxhub和pier
- `version` 查看组件版本信息
- `init` 初始化配置
- `status` 列举实例化组件状态
- `key` 创建并展示密钥信息
- `bitxhub` 启动或关闭bithxub节点
- `pier` 有关pier的操作
- `playground` 一键启动跨链组件
- `info` 展示跨链基本信息
- `prometheus` 启动或关闭prometheus
- `help, h`
这些命令中比较重要的是init使用前一定要初始化、status查看当前运行组件状态、bitxhub、pier。
**global options**
- `--repo value` goduck配置文件默认存储路径
- `--help, -h`
## 2.2 关于BitXHub的操作
```
goduck bitxhub command [command options] [arguments...]
```
### 2.2.1 启动BitXHub节点
```
goduck bitxhub start
```
该命令会初始化并启动BitXHub节点如果有已启动的BitXHub节点会执行失败。执行成功后提示如下
```
1 BitXHub nodes at /Users/fangbaozhu/.goduck/bitxhub are initialized successfully
exec: /bin/bash /Users/fangbaozhu/.goduck/playground.sh up v1.4.0 solo binary 4
Start bitxhub solo by binary
===> Start bitxhub solo successful
```
这是默认启动方式,也可以携带参数自定义启动,参数设置如下:
- `--type value` 配置类型binary或docker 默认“binary”
- `--mode value` 配置模式solo或cluster 默认“solo”
- `--num value` 节点个数只在cluster 模式下作用默认4
- `--tls` 是否启动TLS, 只在v1.4.0+版本有效 (default: false)
- `--version value` BitXHub版本 (default: "v1.4.0")
- `--help, -h`
### 2.2.2 为BitXHub节点生成配置文件
```
goduck bitxhub config
```
该命令默认初始化4个BitXHub节点。执行成功后当前文件夹会生成相关证书、私钥文件以及四个节点的文件夹成功提示如下
```
initializing 4 BitXHub nodes at .
4 BitXHub nodes at . are initialized successfully
```
You can see the following in the current directory
```
.
├── agency.cert
├── agency.priv
├── ca.cert
├── ca.priv
├── key.priv
├── node1/
├── node2/
├── node3/
└── node4/
```
可以携带参数自定义配置情况:
- `--num value` 节点个数只在cluster 模式下作用默认4
- `--type value` 配置类型binary或docker 默认“binary”
- `--mode value` 配置模式solo或cluster 默认“solo”
- `--ips value` 节点IP, 默认所有节点为127.0.0.1, e.g. --ips "127.0.0.1" --ips "127.0.0.2" --ips "127.0.0.3" --ips "127.0.0.4"
- `--target value` 节点的配置文件位置(默认:当前目录)
- `--tls` 是否启动TLS, 只在v1.4.0+版本有效 (default: false)
- `--version value` BitXHub版本 (default: "v1.4.0")
- `--help, -h`
### 2.2.3 关闭BitXHub节点
```
goduck bitxhub stop
```
该命令会关闭所有启动的BitXHub节点。执行成功后会提示里会给出关闭节点的id
```
exec: /bin/bash /Users/fangbaozhu/.goduck/playground.sh down
===> Stop bitxhub
node pid:65246 exit
```
### 2.2.4 清除BitXHub节点
```
goduck bitxhub clean
```
该命令会清除bitxhub节点的配置文件。如果bitxhub节点没有关闭会先关闭节点再清除配置文件。
当bitxhub solo节点成功在关闭后执行此命令会打印出提示如下
```
exec: /bin/bash /Users/fangbaozhu/.goduck/playground.sh clean
===> Stop bitxhub
===> Clean bitxhub
remove bitxhub configure nodeSolo
```
当bitxhub solo节点成功在未关闭的情况下执行此命令会打印出提示如下
```
exec: /bin/bash /Users/fangbaozhu/.goduck/playground.sh clean
===> Stop bitxhub
node pid:65686 exit
===> Clean bitxhub
remove bitxhub configure nodeSolo
```
## 2.3 关于pier的操作
```
GoDuck pier command [command options] [arguments...]
```
### 2.3.1 启动pier
```
goduck pier start
```
参数设置如下:
- `--chain value` 应用链类型ethereum 或 fabric默认ethereum
- `--cryptoPath value` crypto-config路径, 只对fabric链有效, e.g $HOME/crypto-config
- `--pier-type value` pier类型docker或者binary (默认: "docker")
- `--version value` pier版本 (默认: "v1.4.0")
- `--tls value` 是否启动TLS, true or false, 只对v1.4.0+版本有效 (默认: "false")
- `--http-port value` pier的http端口号, 只对v1.4.0+版本有效 (默认: "44544")
- `--pprof-port value` pier的pprof端口号, 只对binary有效 (默认: "44550")
- `--api-port value` pier的api端口号, 只对binary有效 (默认: "8080")
- `--overwrite value` 当本地默认路径存在pier配置文件时是否重写配置 (默认: "true")
- `--appchainIP value` pier连接的应用链ip (默认: "127.0.0.1")
- `--help, -h`
在使用此命令之前您需要启动一个相同版本的BitxHub和一个需要pier连接的应用链。如果应用链或bitxhub不存在pier将无法启动打印提示如下:
```
exec: /bin/bash run_pier.sh up -m ethereum -t docker -r .pier_ethereum -v v1.4.0 -c -p 44550 -a 8080
===> Start pier of ethereum-v1.4.0 in docker...
===> Start a new pier-ethereum container
===> Wait for ethereum-node container to start for seconds...
136d323b1418a026101515313dbbdafee240ac0f0c0d63b4f202304019e13e24
===> Start pier fail
```
如果要连接的应用链和BitxHub已经启动且PIER已经启动成功打印提示如下:
```
exec: /bin/bash run_pier.sh up -m ethereum -t docker -r .pier_ethereum -v v1.0.0-rc1 -c -p 44550 -a 8080
===> Start pier of ethereum-v1.0.0-rc1 in docker...
===> Start a new pier-ethereum container
===> Wait for ethereum-node container to start for seconds...
351bd8e8eb8d5a1803690ac0cd9b77c274b775507f30cb6271164fb843442bfd
===> Start pier successfully
```
### 2.3.2 关闭pier
```
goduck pier stop
```
该命令可以关闭pier可以通过携带参数指定关闭那种类型应用链的pier
### 2.3.3 清除Pier
```
goduck pier clean
```
该命令可以清除pier的配置文件如果pier还没有关闭该命令会先关闭pier再清除其配置文件。
### 2.3.4 生成Pier的配置文件
```
goduck pier config
```
- `--mode value` 配置模式, 直连模式或者中继模式(默认: "direct")
- `--type value` 配置类型, binary或者docker (默认: "binary")
- `--bitxhub value` BitXHub的地址只在中继模式有效
- `--validators value` BitXHub的验证人地址只在中继模式有效, 例如 --validators "0xc7F999b83Af6DF9e67d0a37Ee7e900bF38b3D013" --validators "0x79a1215469FaB6f9c63c1816b45183AD3624bE34" --validators "0x97c8B516D19edBf575D72a172Af7F418BE498C37" --validators "0x97c8B516D19edBf575D72a172Af7F418BE498C37"
- `--port value` pier的端口号只在直连模式有效 (默认: 5001)
- `--peers value` 连接节点的地址,只在直连模式有效, 例如 --peers "/ip4/127.0.0.1/tcp/4001/p2p/Qma1oh5JtrV24gfP9bFrVv4miGKz7AABpfJhZ4F2Z5ngmL"
- `--connectors value` 待连接节点的地址只在v1.4.0+版本的union模式有效, 例如 --connectors "/ip4/127.0.0.1/tcp/4001/p2p/Qma1oh5JtrV24gfP9bFrVv4miGKz7AABpfJhZ4F2Z5ngmL" --connectors "/ip4/127.0.0.1/tcp/4002/p2p/Qma1oh5JtrV24gfP9bFrVv4miGKz7AABpfJhZ4F2Z5abcD"
- `--appchain-type value` 应用链类型, ethereum或者fabric (默认: "ethereum")
- `--appchain-IP value` 应用链IP地址 (默认: "127.0.0.1")
- `--target value` 生成配置文件路径 (默认: ".")
- `--tls value` 是否开启TLS只在v1.4.0+版本有效 (默认: "false")
- `--http-port value` pier的http端口号, 只在v1.4.0+版本有效 (默认: "44544")
- `--pprof-port value` pier的pprof端口号 (默认: "44550")
- `--api-port value` pier的api端口号 (默认: "8080")
- `--cryptoPath value` crypto-config文件的路径只对fabric链有效例如 $HOME/crypto-config (default: "$HOME/.goduck/crypto-config")
- `--version value` pier版本 (默认: "v1.4.0")
- `--help, -h`

View File

@ -0,0 +1,76 @@
# 共识算法插件方案
## 项目结构
该项目为BitXHub提供共识算法的插件化具体项目结构如下
```text
./
├── Makefile //编译文件
├── README.md
├── build
│ └── rbft.so //编译后的共识算法二进制插件
├── go.mod
├── go.sum
├── order.toml //共识配置文件
└── rbft //共识算法代码
├── config.go
├── node.go
└── stack.go
```
其中注意在`go.mod`中需要引用BitXHub项目源码需要让该插件项目与BitXHub在同一目录下建议在$GOPATH路径下
```none
replace github.com/meshplus/bitxhub => ../bitxhub/
```
## 编译Plugin
我们采用GO语言提供的插件模式实现`BitXHub`对于Plugin的动态加载。
编写`Makefile`编译文件:
```shell
SHELL := /bin/bash
CURRENT_PATH = $(shell pwd)
GO = GO111MODULE=on go
plugin:
@mkdir -p build
$(GO) build --buildmode=plugin -o build/rbft.so rbft/*.go
```
运行下面的命令,能够得到 `rbft.so`文件。
```shell
$ make plugin
```
修改节点的`bitxhub.toml`
```none
[order]
plugin = "plugins/rbft.so"
```
将你编写的动态链接文件和`order.toml`文件分别放到节点的plugins文件夹和配置文件下。
```text
./
├── api
├── bitxhub.toml
├── certs
│ ├── agency.cert
│ ├── ca.cert
│ ├── node.cert
│ └── node.priv
├── key.json
├── logs
├── network.toml
├── order.toml //共识算法配置文件
├── plugins
│ ├── rbft.so //共识算法插件
├── start.sh
└── storage
```
结合我们提供的`BitXHub`中继链,就能接入到跨链平台来。

View File

@ -0,0 +1,176 @@
# ERC20资产跨链
# 1. 概述
解决ERC20资产跨链问题支持任意ERC20资产的跨链交易。
# 2.详细设计
ERC20资产跨链方案采用中继节点多签的资产交换方案中继链每个节点是应用链托管合约的成员节点需同步对应应用链的区块头信息通过这种方式验证跨链交易的有效性关于验证的过程可以放在验证规则内同时在用户充值和提现的流程中保证跨链交易的原子性达到跨链资产与锚定资产的一致性。这里以三个方面来解决ERC20的跨链问题
1. 如何同步可靠的应用链区块头;
2. 如何设计托管合约,同时兼顾其安全性;
3. 如何设计用户的充值、提现流程,尽量简化用户操作;
## 2.1 同步区块头
同步区块头有两种方案一种是Oracle预言机的方式一种是系统内置集成同步区块头模块。
方案一:
通过Oracle可信预言机(跨链网关),向中继链区块头合约注入应用链的区块头,区块头合约包含验证跨链交易和增加区块的功能。该方案的优点是可以灵活适配各个应用链,缺点是依赖外部的可信数据源。
![](../../../assets/oracle.png)
区块头合约的主要接口如下:
```go
func AppendAppchainBlockHeader(appchainId, blockHeader)
func VerifyInterchainTxProof(originTx, proof, blockHeight)
```
方案二:
跨链平台内置集成同步区块头模块,该方案的优点是去中心化,缺点是耦合性强,每接入新的应用链需要加入新的同步区块头模块;同步区块头是中继链节点主动获取,还需要考虑网络问题。
![](../../../assets/bvm_block_header.png)
区块头模块的主要接口如下:
```go
func FetchAppchainBlockHeader(blockHeader)
func VerifyInterchainTxProof(originTx, proof, blockHeight)
```
## 2.2 托管合约
ERC20包含如下接口
```javascript
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
```
在跨链充值过程中我们需要用到ERC20合约中的**approve**授权方法和**transferFrom**转账方法,
同时还需要提供**name**和**decimal**的元信息。
```javascript
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
```
在跨链提现过程中我们需要用到ERC20合约中的**transfer**转账方法。
```javascript
function transfer(address recipient, uint256 amount) external returns (bool);
```
托管合约主要接口如下:
```javascript
event lock(address erc20Token, uint256 amount, string relay_addr)
event unlock(address unlock, uint256 amount, string ralay_tx_hash,)
// 添加中继链多签节点地址
func addRelayAdmin(address relay_admin) onlyAdmin external returns (bool)
// 移除中继链多签节点地址
func removeRelayAdmin(address relay_admin) onlyAdmin external returns (bool)
// 用户授权托管合约可以使用ERC20的代币
function approve(address erc20Token, address spender, uint256 amount) external returns (bool)
// 用户发起跨链交易,将ERC20代币锁仓进托管合约
function lock(address erc20Token, uint256 amount, string relay_addr) external returns (bool);
// 中继链解锁跨链资产将ERC20代币归还用户
// 该接口需要接收quorum个中继链节点的签名后才可归还资产
function unlock(address erc20Token, uint256 amount, address unlocker, string ralay_tx_hash, r [][]byte, s [][]byte, v[][]byte) external returns (bool);
```
## 2.3 充值、提现流程设计
**用户充值流程:**
1. 用户调用托管合约授权托管合约允许使用指定ERC20的资产
2. 用户调用托管合约发起跨链交易;
3. 托管合约接收到ERC20资产抛出跨链事件ERC20_Contract、ERC20_Name、ERC20_Decimal、ERC20_Amount、Relay_Addr;
4. 跨链网关监听到跨链事件封装IBTP原应用链Tx、跨链交易证明、中继链地址、跨链金额等等
5. 跨链网关发送跨链交易给中继链;
6. 中继链接收到跨链交易后,验证跨链交易的有效性;
7. 验证失败后会在跨链网关诚信合约惩罚对应的跨链网关(待定);
8. 验证成功后检查资产列表如果是新增资产则新建对应的ERC20资产托管合约
9. 通过跨链交易的Amount、Addr增发等额的锚定资产用户完成跨链充值操作
![](../../../assets/mint.png)
**用户提现流程:**
1. 用户在中继链上发起提现请求目的链地址ERC20地址金额用户应用链地址
2. 中继链接收到交易,冻结用户在中继链等额的锚定资产;
3. 中继链发起跨链交易抛出跨链事件目的链地址ERC20地址金额用户应用链地址
4. 跨链网关监听到跨链事件,构造对应应用链的交易;
5. 跨链网关将应用链的交易发往中继链;
6. 中继链解析并校验应用链的交易,包括地址,金额等等;
7. 中继链验证通过后签名,发完其它中继链要求其签名;
8. 跨链网关获取到中继链节点的多重签名数据,调用应用链的托管合约;
9. 应用链归还资产,抛出解锁事件;
10. 跨链网关监听到解锁事件,发往中继链;
11. 中继链验证通过后销毁对应的锚定资产;
![](../../../assets/burn.png)

View File

@ -0,0 +1,59 @@
# 跨链事务方案
跨链需要保证跨链交易的原子性和一致性,即来源链和目的链上的交易要么都成功,要么都失败回滚。
为此,中继链提供了事务管理机制,通过内置的事务管理合约,来保证不同业务场景下跨链交易的事务性。
多链消息表事务方案是针对非资产交换类业务场景的事务方案,它可以支持一对一跨链和一对多跨链。
## 一对一跨链
当来源链业务合约发起跨链交易时,除了提供目的链的目标业务合约中要调用的方法和参数外,还可以提供来源链的回调方法和回滚方法。
来源链跨链网关捕获跨链事件封装成IBTP提交到中继链并由中继链跨链事务合约进行处理。跨链事务合约将初始化该跨链交易所对应的跨链事务
在跨链事务合约中以KV形式记录跨链事务ID和事务状态信息其中跨链事务ID由**来源链ID || 目的链ID || IBTP index**组成初始的跨链状态信息为TransactionStatus_BEGIN。
当目的链执行完跨链交易时由目的链跨链网关得到执行的结果成功或失败并将该信息封装成IBTP receipt提交给中继链。
中继链跨链事务合约根据其中的回执信息更新跨链事务ID的状态如果成功则状态更新为TransactionStatus_SUCCESS否则为TransactionStatus_FAILURE。
如果跨链事务最终状态是成功,则来源链跨链网关调用来源链业务合约注册的回调函数进行回调;如果跨链事务状态为失败,则来源链跨链网关将调用来源链业务合约注册的回滚函数进行回滚操作。
## 一对多跨链
一对多跨链是指,在一个来源链的业务合约的交易中,抛出了多个指向不同目的链的跨链事件。 一对多跨链和一对一跨链的整体流程一样,只是其中事务分为全局事务和子事务的概念:
- 子事务:指来源链和某个目的链的跨链事务
- 全局事务:综合各个子事务的整体事务
因此在中继链的跨链事务合约中,为一对多跨链事务特别设计了一个事务信息结构来支持该场景下的跨链事务状态记录和更新。该结构如下:
```go
type txInfo struct {
// 全局事务状态
globalState string
// 子事务信息key为子事务ID即各目的链的地址value为子事务状态
childTxInfo map<string, string>
}
```
其中全局事务状态和子事务初始状态为BEGIN。之后各目的链的跨链网关获取跨链交易并提交给各目的链执行目的链执行完子事务后由跨链网关根据执行结果向中继链反馈子事务状态中继链事务管理合约将进行相应的更新
- 如果交易执行成功则将对应的子事务状态设置为SUCCESS当所有子事务状态均为SUCCESS时跨链事务管理合约将该全局事务ID对应的事务状态更新为SUCCESS
- 如果交易执行失败则将对应的子事务状态设置为FAILURE并将该全局事务ID对应的事务状态更新为FAILURE
之后,各个应用链(包含来源链和目的链)的跨链网关可以获取中继链的全局事务和子事务的状态, 来对应用链进行业务上的“回调”操作或“回滚”操作。
![Transaction](../../../assets/transaction.svg)
## 跨链事务合约设计
中继链上的跨链事务管理合约接口设计如下:
```go
// 一对一跨链事务初始化
Begin(txId string)
// 一对多跨链事务初始化
BeginMultiTXs(globalId string, childTxIds ...string)
// 报告事务执行结果
Report(txId string, result int32)
// 获取事务状态
GetStatus(txId string)
```

View File

@ -0,0 +1,211 @@
# 跨链网关设计方案
## 整体架构
在中继链的设计中,对于跨链网关的主要功能作了简要的介绍。本文主要详细介绍跨链网关的主要设计架构思想。
从跨链网关的功能上来说,设计上需要解决的难点包括以下几点:
1. 跨链网关需要对接不同架构的区块链,如何简化跨链网关接入不同区块链的跨链网关设计上需要考虑的问题。
2. 跨链网关需要支持中继模式直接和中继链连接和直连模式直接和其他的Pier进行连接如何在不同模式间切换时设计上需要考虑的问题。
从总体架构来说跨链网关根据不同的功能采取了模块划分的方式主要的功能模块有MonitorExecutorExchangerValidate EngineAppchain ManagerNetwork等。
![](../../../assets/pier.png)
## 处理流程
一次完整的跨链交易的处理过程如下:
AMonitor监听
跨链网关PA启动之后Appchain A发起一笔跨链交易Monitor模块监听到该跨链交易跨链网关对于该跨链交易做出检查之后保存相应的跨链交易。
BExchanger转发
Exchanger获取Monitor收到的跨链交易作相应的检查后进行转发。转发过程中根据跨链交易的目的链ID以及连接的是中继链还是直连的其他跨链网关等信息转发到正确的路由路径。
1. 中继链模式
通过中继链的SDK提交跨链交易到中继链的内置合约上中继链记录并执行验证转发等操作。
2. 直连模式
通过P2P网络连接其他跨链网关通过跨链交易的目的链ID来转发到相应的跨链网关。
C. Exchanger接受外部跨链交易
1. 中继链模式
Exchanger 的子模块Lite和Syncer负责同步中继链的区块头和跨链交易的信息对于验证通过的跨链交易Exchanger进行转送到Executor中。
2. 直连模式
Exchanger通过P2P网络收到对方跨链网关发送的跨链交易并作出相应的验证操作。验证通过的跨链交易转送到Executor中。
Executor提交跨链交易到应用链上并根据执行的结果构造返回的回执类型的IBTP包转送到Exchanger进行下一步的转发工作。
DExchanger 接收外部回执
来源链发送的跨链交易在目的链执行之后,返回回执信息又回到了来源链的跨链网关之后,进行如下的处理。
1. 中继链模式
Exchanger 的子模块Lite和Syncer负责同步中继链的区块头和跨链交易的信息对于验证通过的跨链交易和回执信息Exchanger进行转送到Executor中。Executor 模块能够按照IBTP的类型判断对应的处理方式。对于其他链主动的调用的IBTP需要发回回执而对于其他链发回的回执就不再构造回执返回。
2. 直连模式
Exchanger通过P2P网络收到对方跨链网关发送的跨链交易并作出相应的验证操作。验证通过的跨链交易或者回执转送到Executor中。
Executor 之后的处理方式和中继链模式下类似。
以上,就是一次完整的跨链交易的执行过程。
## 模块接口设计
为了简化不同模块之间的协作流程,在上述设计思路的基础上,我们明确了不同模块的服务方式,规定了各个模块之间通信的接口。
### Monitor
```go
type Monitor interface {
// Start starts the service of monitor
Start() error
// Stop stops the service of monitor
Stop() error
// listen on interchain ibtp from now on
ListenOnIBTP() chan *pb.IBTP
// query historical ibtp by its id
QueryIBTP(id string) *pb.ibtp
// QueryLatestMeta queries latest index map of ibtps executed on appchain
QueryLatestMeta() map[string]uint64
}
```
除了能监听从监听开始之后的所有的跨链交易还可以根据IBTP的ID查询历史数据。
### Executor
```go
type Executor interface {
// Start starts the service of executor
Start() error
// Stop stops the service of executor
Stop() error
// HandleIBTP handles interchain ibtps from other appchains
// and return the receipt ibtp for ack or callback
HandleIBTP(ibtp *pb.IBTP) (*pb.IBTP, error)
// QueryLatestMeta queries latest index map of ibtps executed on appchain
QueryLatestMeta() map[string]uint64
}
```
除了提供启动和结束服务的接口还提供在在Appchain上执行跨链交易和给出执行的结果信息的接口。
### Lite
```go
type Lite interface {
// Start starts service of lite client
Start() error
// Stop stops service of lite client
Stop() error
// QueryHeader gets block header given block height
QueryHeader(height uint64) (*pb.BlockHeader, error)
}
```
区块链的轻客户端,能够自动同步区块头并提供查询区块头的接口。
### Validate Engine
```go
type Checker interface {
// Start starts service of validate engine
Start() error
// Stop stops service of validate engine
Stop() error
// CheckIBTP checks if ibtp is complied with registered validating rule
CheckIBTP(ibtp *pb.IBTP) error
}
```
提供检查跨链交易是否满足注册的验证规则的接口。验证规则需要提前部署并绑定在应用链ID上。验证引擎根据跨链交易的目的链ID自动触发绑定的验证规则。
### Appchain Manager
```go
type AM interface {
// Start starts service of appchain management
Start() error
// Stop stops service of appchain management
Stop() error
// check if the appchain of destination is registered
Verify(ibtp *pb.IBTP) error
}
```
负责应用链互相注册、审核和注册应用链审查规则等功能。并在每一个跨链交易真正执行前进行检查。
### Exchanger
```go
// 无论是直连还是连接BitXHub都通过Exchanger
type Exchanger interface {
HandleIBTP(ibtp *pb.IBTP) error
SendIBTP(ibtp *pb.IBTP) error
QueryIBTP(id string) (*pb.IBTP, error)
QueryReceipt(id string) (*pb.IBTP, error)
}
```
## 重启机制
在我们的设计中,极端情况下,跨链网关可以在没有保存任何跨链相关的数据就能正确启动。当然这需要不断的恢复之前的数据,重启的网络通信代价比较大。为了减少网络传输的启动负担,我们在对于一些关键的跨链信息还是进行了数据库的保存操作。
### **Exchanger**
重启后Exchanger 会根据启动模式去对方(可以是中继链或者其他网关)查询和自己应用链相关的几个最新序号信息。
1. 中继模式
向中继链查询两个序号信息:
中继链上收到的由自己应用链作为来源链主动发出的最新跨链交易序号。
中继链上收到的由自己应用链作为目的链被动回复其他链的最新跨链回执序号。
2. 直连模式
向连接的其他网关查询两个序号信息:
其他网关上收到的由自己应用链作为来源链主动发出的最新跨链交易序号。
其他网关上收到的由自己应用链作为目的链被动回复其他链的最新跨链回执序号。
### Monitor
重启后查询Appchain得到最新抛出的跨链交易的序号。Exchanger会查询相应的对方链的情况Exchanger再调用Monitor提供的查询最新序号的接口通过比较能够知道未转发的本链上发起的跨链交易。
### Executor
重启后查询Appchain得到最新已执行的跨链交易的序号。
Exchanger查询相应的对方链的已收到回执的最新序号Exchanger再调用Executor提供的查询最新已执行跨链交易序号的接口通过比较能够知道未转发到本链执行的跨链交易。
## 总结
总的来说通过比较两方记录的最新序号信息网关能够知道需要从何处重新开始工作并保证交易的序号。通过上面的设计Pier能够做到无状态启动并且能解决整体架构中提出的两个问题。

View File

@ -0,0 +1,25 @@
# 隐私保护
## 前提
在跨链场景中,中继链需要记录不同的应用链的跨链请求,所有的跨链交易对于加入了中继链跨链系统的应用链来说都是可见的。这种情况下,如果应用链用户发起的跨链交易带有隐私数据,隐私数据非常容易泄露。
![](/assets/privacy1.png)
所以我们在中继链的设计中需要隐私保护相关的机制,并且能够保持比较高的灵活度。在跨链系统中,我们提出了两种可行的隐私保护机制。
## 隐私交易
跨链交易在提交到中继链的共识节点之后,会通过共识发送到所有的其他节点去,所以跨链交易对于整个中继链来说没有隐私性可言。而隐私交易这种机制下,收到跨链交易的节点并不会将跨链交易的内容直接进行共识,而是计算跨链交易的哈希值进行共识。跨链交易在共识完成之后,参与方的共识节点将完整的跨链交易直接发送到参与方负责的中继链节点。
![](/assets/privacy2.png)
这样不相关的节点就无法直接获取到跨链交易,能够保护用户的交易隐私信息。
## 端到端加密
另一种进行隐私保护的思路是在跨链交易提交之前,就对跨链交易进行加密操作。为了提升加解密的效率,采用对称加密和秘钥协商的机制来实现。具体的一次加密交易过程如下图:
![](/assets/privacy3.png)
要进行加密交易的双方需要在中继链上注册自己的私钥衍生的公钥,这样双方通过自己拥有的私钥和对方注册的公钥,能够计算出一个相同的对称加密的秘钥,用于提交跨链交易之前的加密和获取跨链交易之后的解密。这个秘钥只有公钥的话无法解出用于加密的对称秘钥,所以能够保证跨链交易的隐私性。

View File

@ -4,8 +4,71 @@ repo_name: meshplus/bitxhub
extra_css:
- stylesheets/extra.css
theme:
logo: /assets/logo.png
logo: assets/logo.png
name: material
features:
- navigation.tabs
font:
text: Roboto
code: Roboto Mono
palette:
scheme: blue
accent: blue
primary: red
scheme: default
accent: red
version:
provider: mike
markdown_extensions:
- pymdownx.highlight:
linenums: false
use_pygments: true
- pymdownx.superfences
#extra_javascript:
# - https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/highlight.min.js
# - javascripts/config.js
#extra_css:
# - https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/styles/default.min.css
nav:
- Home:
- 快速开始: bitxhub/quick_start.md
- 产品介绍:
- 产品优势: bitxhub/introduction/advantage.md
- 技术架构: bitxhub/introduction/arch.md
- 基本概念: bitxhub/introduction/conception.md
- 应用场景: bitxhub/introduction/scenaries.md
- 跨链服务平台介绍: bitxhub/introduction/summary.md
- 开发手册:
- 共识算法插件: bitxhub/dev/consensus.md
- 跨链合约: bitxhub/dev/cross_contract.md
- 应用链插件编写: bitxhub/dev/plugin.md
- 验证规则编写: bitxhub/dev/rule.md
- 设计文档:
- 共识算法插件方案: bitxhub/dev/design/consensus_plugin.md
- 跨链事务方案: bitxhub/dev/design/interchain_transaction.md
- 跨链网关设计方案: bitxhub/dev/design/pier.md
- 隐私保护方案: bitxhub/dev/design/privacy_protection.md
- 部署使用:
- 环境准备: bitxhub/usage/env.md
- 中继链部署: bitxhub/usage/deploy_bitxhub.md
- 跨链网关部署--接入Fabric: bitxhub/usage/deploy_pier_access_fabric.md
- 跨链网关部署--接入Ethereum: bitxhub/usage/deploy_pier_access_ethereum.md
- 中继跨链部署: bitxhub/usage/inter_relay_deploy.md
- 跨链网关直连模式部署: bitxhub/usage/pier_direct_mode_deploy.md
- 版本发布记录:
- BitXHub v1.0.0: bitxhub/changelog/bitxhub_v1.0.0.md
- BitXHub v1.3.0: bitxhub/changelog/bitxhub_v1.3.0.md
- BitXHub v1.4.0: bitxhub/changelog/bitxhub_v1.4.0.md
- BitXHub v1.5.0: bitxhub/changelog/bitxhub_v1.5.0.md
- FAQ: bitxhub/faq.md
- 加入我们: bitxhub/join_us.md
- 社区介绍: bitxhub/community_introduction.md
- 小工具:
- Goduck运维工具: goduck/index.md
- Premo测试工具: premo/usage.md
- SDK文档:
- GoSDK使用文档: sdk/gosdk.md
- JavaSDK使用: sdk/javasdk.md
- JsSDK使用文档: sdk/jssdk.md

18
go.mod
View File

@ -22,33 +22,26 @@ require (
github.com/grpc-ecosystem/grpc-gateway v1.16.0
github.com/hashicorp/golang-lru v0.5.4
github.com/hokaccha/go-prettyjson v0.0.0-20190818114111-108c894c2c0e
github.com/hyperledger/fabric v2.1.1+incompatible // indirect
github.com/hyperledger/fabric-protos-go v0.0.0-20201028172056-a3136dde2354 // indirect
github.com/juju/ratelimit v1.0.1
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible // indirect
github.com/lestrrat-go/strftime v1.0.3 // indirect
github.com/libp2p/go-libp2p-core v0.5.6
github.com/magiconair/properties v1.8.4
github.com/meshplus/bitxhub-core v0.1.0-rc1.0.20210318102029-494ee3060b0c
github.com/meshplus/bitxhub-core v0.1.0-rc1.0.20210330035001-b327cf056572
github.com/meshplus/bitxhub-kit v1.1.2-0.20210112075018-319e668d6359
github.com/meshplus/bitxhub-model v1.1.2-0.20210309053945-afaea82e9fe1
github.com/meshplus/bitxhub-model v1.1.2-0.20210409071219-0526019e06c4
github.com/meshplus/go-libp2p-cert v0.0.0-20210125063330-7c25fd5b7a49
github.com/meshplus/go-lightp2p v0.0.0-20210120082108-df5a536a6192
github.com/mitchellh/go-homedir v1.1.0
github.com/multiformats/go-multiaddr v0.3.0
github.com/orcaman/concurrent-map v0.0.0-20190826125027-8c72a8bb44f6
github.com/pelletier/go-toml v1.2.0
github.com/pelletier/go-toml v1.8.1
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.5.0
github.com/rogpeppe/go-internal v1.5.2 // indirect
github.com/rs/cors v1.7.0
github.com/sirupsen/logrus v1.7.0
github.com/spf13/cast v1.3.1
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.7.1
github.com/stretchr/objx v0.2.0 // indirect
github.com/stretchr/testify v1.6.0
github.com/sykesm/zap-logfmt v0.0.4 // indirect
github.com/stretchr/testify v1.6.1
github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d
github.com/tidwall/gjson v1.6.8
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5
@ -57,9 +50,6 @@ require (
github.com/willf/bitset v1.1.11 // indirect
github.com/willf/bloom v2.0.3+incompatible
go.uber.org/atomic v1.7.0
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.16.0 // indirect
google.golang.org/genproto v0.0.0-20201119123407-9b1e624d6bc4 // indirect
google.golang.org/grpc v1.33.2
)

148
go.sum
View File

@ -32,6 +32,7 @@ github.com/Knetic/govaluate v3.0.0+incompatible h1:7o6+MAPhYTCF0+fdvoz1xDedhRb4f
github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETFUpAzWW2ep1Y=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM=
github.com/Rican7/retry v0.1.0 h1:FqK94z34ly8Baa6K+G8Mmza9rYWTKOJk+yckIBB5qVk=
github.com/Rican7/retry v0.1.0/go.mod h1:FgOROf8P5bebcC1DS0PdOQiqGUridaZvikzUmkFW6gg=
github.com/Shopify/sarama v1.26.1/go.mod h1:NbSGBSSndYaIhRcBtY9V0U7AyH+x71bG668AuWys/yU=
@ -69,6 +70,7 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/bombsimon/wsl/v2 v2.0.0/go.mod h1:mf25kr/SqFEPhhcxW1+7pxzGlW+hIl/hYTKY95VwV8U=
github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ=
github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8=
github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI=
@ -116,6 +118,7 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -167,23 +170,41 @@ github.com/frankban/quicktest v1.10.0 h1:Gfh+GAJZOAoKZsIZeZbdn2JF10kN1XHNvjsvQK8
github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI=
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
github.com/go-critic/go-critic v0.4.1/go.mod h1:7/14rZGnZbY6E38VEGk2kVhoq6itzc1E68facVDK23g=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4=
github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ=
github.com/go-toolsmith/astequal v0.0.0-20180903214952-dcb477bfacd6/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY=
github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY=
github.com/go-toolsmith/astfmt v0.0.0-20180903215011-8f8ee99c3086/go.mod h1:mP93XdblcopXwlyN4X4uodxXQhldPGZbcEJIimQHrkg=
github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw=
github.com/go-toolsmith/astinfo v0.0.0-20180906194353-9809ff7efb21/go.mod h1:dDStQCHtmZpYOmjRP/8gHHnCCch3Zz3oEgCdZVdtweU=
github.com/go-toolsmith/astp v0.0.0-20180903215135-0af7e3c24f30/go.mod h1:SV2ur98SGypH1UjcPpCatrV5hPazG6+IfNHbkDXBRrk=
github.com/go-toolsmith/astp v1.0.0/go.mod h1:RSyrtpVlfTFGDYRbrjyWP1pYu//tSFcvdYrA8meBmLI=
github.com/go-toolsmith/pkgload v0.0.0-20181119091011-e9e65178eee8/go.mod h1:WoMrjiy4zvdS+Bg6z9jZH82QXwkcgCBX6nOfnmdaHks=
github.com/go-toolsmith/pkgload v1.0.0/go.mod h1:5eFArkbO80v7Z0kdngIxsRXRMTaX4Ilcwuh3clNrQJc=
github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8=
github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU=
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
github.com/gobuffalo/envy v1.9.0 h1:eZR0DuEgVLfeIb1zIKt3bT4YovIMf9O9LXQeCZLXpqE=
github.com/gobuffalo/envy v1.9.0/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w=
@ -194,7 +215,10 @@ github.com/gobuffalo/packd v1.0.0 h1:6ERZvJHfe24rfFmA9OaoKBdC7+c9sydrytMg8SdFGBM
github.com/gobuffalo/packd v1.0.0/go.mod h1:6VTc4htmJRFB7u1m/4LeMTWjFoYrUiBkU9Fdec9hrhI=
github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg=
github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=
github.com/gobuffalo/packr/v2 v2.5.1 h1:TFOeY2VoGamPjQLiNDT3mn//ytzk236VMO2j7iHxJR4=
github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gofrs/flock v0.0.0-20190320160742-5135e617513b/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
@ -218,6 +242,21 @@ github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8l
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.2-0.20200707131729-196ae77b8a26 h1:lMm2hD9Fy0ynom5+85/pbdkiYcBqM1JWmhpAXLmy0fw=
github.com/golang/snappy v0.0.2-0.20200707131729-196ae77b8a26/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4=
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk=
github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6/go.mod h1:DbHgvLiFKX1Sh2T1w8Q/h4NAI8MHIpzCdnBUDTXU3I0=
github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8=
github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3/go.mod h1:JXrF4TWy4tXYn62/9x8Wm/K/dm06p8tCKwFRDPZG/1o=
github.com/golangci/gocyclo v0.0.0-20180528134321-2becd97e67ee/go.mod h1:ozx7R9SIwqmqf5pRP90DhR2Oay2UIjGuKheCBCNwAYU=
github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU=
github.com/golangci/golangci-lint v1.23.0/go.mod h1:LNexeEyqT5hQH7v47e67JekL0V51lXFUjbPkopxNSK4=
github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc/go.mod h1:e5tpTHCfVze+7EpLEozzMB3eafxo2KT5veNg1k6byQU=
github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg=
github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o=
github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA=
github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21/go.mod h1:tf5+bzsHdTM0bsB7+8mt0GUMvjCgwLpTapNZHU8AajI=
github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0/go.mod h1:qOQCunEYvmd/TLamH+7LlVccLvUH5kZNhbCgTHoBbp4=
github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@ -234,6 +273,7 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
@ -242,10 +282,12 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE=
github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 h1:FlFbCRLd5Jr4iYXZufAvgWN6Ao0JrI5chLINnUXDDr0=
@ -370,7 +412,10 @@ github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 h1:IPJ3dvxmJ4uc
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a/go.mod h1:xRskid8CManxVta/ALEhJha/pweKBaVG6fWgc0yH25s=
github.com/jirfag/go-printf-func-name v0.0.0-20191110105641-45db9963cdd3/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmoiron/sqlx v1.2.1-0.20190826204134-d7d95172beb5/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
@ -388,6 +433,7 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0=
github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356 h1:I/yrLt2WilKxlQKCM52clh5rGzTKpVctGT1lH4Dc8Jw=
github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU=
github.com/karrick/godirwalk v1.10.12 h1:BqUm+LuJcXjGv1d2mj3gBiQyrQ57a0rYoAmhvJQ7RDU=
github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
@ -395,10 +441,14 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.9.8/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.10.1/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.10.10 h1:a/y8CglcM7gLGYmlbP/stPE5sR3hbhFRUjCBfd/0B3I=
github.com/klauspost/compress v1.10.10/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/reedsolomon v1.9.3/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@ -406,12 +456,14 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxv
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d h1:68u9r4wEvL3gYg2jvAOgROwZ3H+Y3hIDk4tbbmIjcYQ=
github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
@ -426,6 +478,8 @@ github.com/lestrrat-go/strftime v1.0.0 h1:wZIfTHGdu7TeGu318uLJwuQvTMt9UpRyS+XV2R
github.com/lestrrat-go/strftime v1.0.0/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR76fd03sz+Qz4g=
github.com/lestrrat-go/strftime v1.0.3 h1:qqOPU7y+TM8Y803I8fG9c/DyKG3xH/xkng6keC1015Q=
github.com/lestrrat-go/strftime v1.0.3/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR76fd03sz+Qz4g=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/libp2p/go-addr-util v0.0.1/go.mod h1:4ac6O7n9rIAKB1dnd+s8IbbMXkt+oBpzX4/+RACcnlQ=
github.com/libp2p/go-addr-util v0.0.2 h1:7cWK5cdA5x72jX0g8iLrQWm5TRJZ6CzGdPEhWj7plWU=
github.com/libp2p/go-addr-util v0.0.2/go.mod h1:Ecd6Fb3yIuLzq4bD7VcywcVSBtefcAwnUISBM3WG15E=
@ -596,6 +650,7 @@ github.com/libp2p/go-yamux v1.3.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZ
github.com/libp2p/go-yamux v1.3.5/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=
github.com/libp2p/go-yamux v1.3.6 h1:O5qcBXRcfqecvQ/My9NqDNHB3/5t58yuJYqthcKhhgE=
github.com/libp2p/go-yamux v1.3.6/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/looplab/fsm v0.2.0 h1:M8hf5EF4AYLcT1FNKVUX8nu7D0xfp291iGeuigSxfrw=
github.com/looplab/fsm v0.2.0/go.mod h1:p+IElwgCnAByqr2DWMuNbPjgMwqcHvTRZZn3dvKEke0=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
@ -604,6 +659,7 @@ github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP
github.com/magiconair/properties v1.8.4 h1:8KGKTcQQGm0Kv7vEbKFErAoAOFyyacLStRtQSeYtvkY=
github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
@ -621,10 +677,13 @@ github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcME
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/meshplus/bitxhub-core v0.1.0-rc1.0.20210318102029-494ee3060b0c h1:78bm/wsSv0IORdGw6xdoeFVsILn/hwG6vk4eAg3rgLE=
github.com/meshplus/bitxhub-core v0.1.0-rc1.0.20210318102029-494ee3060b0c/go.mod h1:G19Wrz1u66UmwaES/iLM19jmlv3APAZ5qfYOlNnIIZw=
github.com/meshplus/bitxhub-core v0.1.0-rc1.0.20210330035001-b327cf056572 h1:kKcS6taPdH8fOi8kU35ciUlk5cbYCyKa4QbA+04OQ4s=
github.com/meshplus/bitxhub-core v0.1.0-rc1.0.20210330035001-b327cf056572/go.mod h1:G19Wrz1u66UmwaES/iLM19jmlv3APAZ5qfYOlNnIIZw=
github.com/meshplus/bitxhub-kit v1.1.1 h1:vkPO88oA3+Kpc0N8lIgfj/U52KBuI+633hPbMYt1xm8=
github.com/meshplus/bitxhub-kit v1.1.1/go.mod h1:r4l4iqn0RPJreb/OmoYKfjCjQJrXpZX++6Qc31VG/1k=
github.com/meshplus/bitxhub-kit v1.1.2-0.20201021105954-468d0a9d7957/go.mod h1:r4l4iqn0RPJreb/OmoYKfjCjQJrXpZX++6Qc31VG/1k=
@ -633,9 +692,16 @@ github.com/meshplus/bitxhub-kit v1.1.2-0.20201203072410-8a0383a6870d h1:J9tzTNf2
github.com/meshplus/bitxhub-kit v1.1.2-0.20201203072410-8a0383a6870d/go.mod h1:KR7ZlXhII9n0Bu8viaZTScvXCYn0MCQnYlsTvHPp0XA=
github.com/meshplus/bitxhub-kit v1.1.2-0.20210112075018-319e668d6359 h1:GdgS14bnCF4b/a5zhQi2wlu92pHc9cfl6A1HcbO7zmE=
github.com/meshplus/bitxhub-kit v1.1.2-0.20210112075018-319e668d6359/go.mod h1:KR7ZlXhII9n0Bu8viaZTScvXCYn0MCQnYlsTvHPp0XA=
github.com/meshplus/bitxhub-model v1.1.1/go.mod h1:lUl9vPZXM9tP+B0ABRW/2eOW/6KCmjFTdoiTj5Vut/A=
github.com/meshplus/bitxhub-model v1.1.2-0.20201021152621-0b3c17c54b23/go.mod h1:4qWBZx5wv7WZzUqiuBsbkQqQ2Ju8aOFpsoNpBBNy8Us=
github.com/meshplus/bitxhub-model v1.1.2-0.20210120083349-c7a006b03fcb/go.mod h1:x3H+TL24wcByzHegenLfs+5PQkQGNsk8eCm31QJMa+Q=
github.com/meshplus/bitxhub-model v1.1.2-0.20210309053945-afaea82e9fe1 h1:ziae0L0cbCMKp66OYzjZuU1WtNoB2TgfFhNIVWOTod4=
github.com/meshplus/bitxhub-model v1.1.2-0.20210309053945-afaea82e9fe1/go.mod h1:x3H+TL24wcByzHegenLfs+5PQkQGNsk8eCm31QJMa+Q=
github.com/meshplus/bitxid v0.0.0-20210331062815-ef07c54e5ab0 h1:SK2eve3MfqiX7WTz6Mt62Zw+HdJDGqs4WPax4zOO1Ls=
github.com/meshplus/bitxid v0.0.0-20210331062815-ef07c54e5ab0/go.mod h1:vAldSRfDe2Qo7exsSTbchVmZWXPY7fhWQrRw18QJHho=
github.com/meshplus/did-registry v0.0.0-20210331065856-fb7f8bc2f8a2/go.mod h1:s8sqpmd+1N8Y+O3WYOKj7zFU3lajZ4MDd75J1h0yUj8=
github.com/meshplus/did-registry v0.0.0-20210407092831-8da970934f93 h1:fgGlFkT6ZODEWqUaJ2fyu1am0W+GC+ge8exOMf+n038=
github.com/meshplus/did-registry v0.0.0-20210407092831-8da970934f93/go.mod h1:jKMndvgIcNA1BAikkeRo6/0HWa62HL5q1kfQEdyvF6s=
github.com/meshplus/go-libp2p-cert v0.0.0-20210120021632-1578cf63e06a h1:eg1BDjSOsz3cdH49kPE8c2XnIFlLTPEMJLqpofV/OEY=
github.com/meshplus/go-libp2p-cert v0.0.0-20210120021632-1578cf63e06a/go.mod h1:rS4AYMqKypLn2IPEnHICP//V2v16SZo4CWUbwMdihl0=
github.com/meshplus/go-libp2p-cert v0.0.0-20210125063330-7c25fd5b7a49 h1:F8dpLJZW6FxqinAQcZKTkoymZgxnqlNvTebNqWVMEYI=
@ -660,16 +726,20 @@ github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceT
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
github.com/mr-tron/base58 v1.1.1/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
@ -731,6 +801,7 @@ github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXS
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
@ -762,17 +833,22 @@ github.com/orcaman/concurrent-map v0.0.0-20190826125027-8c72a8bb44f6/go.mod h1:L
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222 h1:goeTyGkArOZIVOMA0dQbyuPWGNQJZGPwPu/QS9GlpnA=
github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM=
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 h1:oYW+YCJ1pachXTQmzR3rNLYGGz4g/UgFcjb28p/viDM=
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
github.com/pierrec/lz4 v2.4.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI=
github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
@ -803,6 +879,7 @@ github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/prometheus/tsdb v0.10.0 h1:If5rVCMTp6W2SiRAQFlbpJNgVlgMEd+U2GZckwK38ic=
github.com/prometheus/tsdb v0.10.0/go.mod h1:oi49uRhEe9dPUTlS3JRZOwJuVi6tmh10QSgwXEyGCt4=
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI=
github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ=
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
@ -815,8 +892,8 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.5.2 h1:qLvObTrvO/XRCqmkKxUlOBc48bI3efyDuAZe25QiF0w=
github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
@ -828,8 +905,13 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/securego/gosec v0.0.0-20200103095621-79fbf3af8d83/go.mod h1:vvbZ2Ae7AzSq3/kywjUDxSNq2SJ27RxCz2un0H3ePqE=
github.com/shirou/gopsutil v0.0.0-20190901111213-e4ec7b275ada/go.mod h1:WWnYX4lzhCH5h/3YBfyVA3VbLYjlMZZAQcW9ojMexNc=
github.com/shirou/gopsutil v2.20.5+incompatible h1:tYH07UPoQt0OCQdgWWMgYHy3/a9bcxNpBIysykNIP7I=
github.com/shirou/gopsutil v2.20.5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
@ -837,14 +919,16 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo=
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/smola/gocompat v0.2.0/go.mod h1:1B0MlxbmoZNo3h8guHp8HztB3BSYR5itql9qtVc0ypY=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34cd2MNlA9u1mE=
github.com/spacemonkeygo/openssl v0.0.0-20181017203307-c2dcc5cca94a/go.mod h1:7AyxJNCJ7SBZ1MfVQCWD6Uqo2oubI2Eq2y2eqf+A5r0=
github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+1+HkWaX/Yh71Ee5ZHaHYt7ZP4sQgUrm6cDU=
github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc=
@ -853,6 +937,8 @@ github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0b
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.5.1 h1:VHu76Lk0LSP1x254maIu2bplkWpfBWI+B+6fdoZprcg=
github.com/spf13/afero v1.5.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
@ -861,11 +947,14 @@ github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.6.1/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k=
github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/src-d/envconfig v1.0.0/go.mod h1:Q9YQZ7BKITldTBnoxsE5gOeB5y66RyPXeue/R4aaNBc=
@ -887,6 +976,8 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.0 h1:jlIyCplCJFULU/01vCkhKuTyc3OorI3bJFuw6obfgho=
github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/sykesm/zap-logfmt v0.0.3 h1:3Wrhf7+I9JEUD8B6KPtDAr9j2jrS0/EPLy7GCE1t/+U=
@ -906,15 +997,26 @@ github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE=
github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.0.2 h1:Z7S3cePv9Jwm1KwS0513MRaoUe3S01WPbLNV40pwWZU=
github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk=
github.com/tjfoc/gmsm v1.3.0/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tommy-muehle/go-mnd v1.1.1/go.mod h1:dSUh0FtTP8VhvkL1S+gUR1OKd9ZnSaozuI6r3m6wOig=
github.com/treasersimplifies/cstr v0.0.0-20201216143046-7ec53ac8c37b/go.mod h1:vDeqle3kbbevUDgoxyM/SG6xkJI5gnprSwDGaZC0zRc=
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef h1:wHSqTBrZW24CsNJDfeh9Ex6Pm0Rcpc7qrgKBiL44vF4=
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ultraware/funlen v0.0.2/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA=
github.com/ultraware/whitespace v0.0.4/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA=
github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
github.com/uudashr/gocognit v1.0.1/go.mod h1:j44Ayx2KW4+oB6SWMv8KsmHzZrOInQav7D3cQMJ5JUM=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s=
github.com/valyala/quicktemplate v1.2.0/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4=
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
github.com/wangjia184/sortedset v0.0.0-20160527075905-f5d03557ba30/go.mod h1:YkocrP2K2tcw938x9gCOmT5G5eCD6jsTz0SZuyAqwIE=
github.com/wasmerio/go-ext-wasm v0.3.1 h1:G95XP3fE2FszQSwIU+fHPBYzD0Csmd2ef33snQXNA5Q=
github.com/wasmerio/go-ext-wasm v0.3.1/go.mod h1:VGyarTzasuS7k5KhSIGpM3tciSZlkP31Mp9VJTHMMeI=
@ -984,6 +1086,7 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@ -993,6 +1096,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnk
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -1019,17 +1124,20 @@ golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190227160552-c95aed5357e7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -1047,6 +1155,8 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -1060,6 +1170,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -1085,6 +1197,7 @@ golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -1098,7 +1211,11 @@ golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -1106,37 +1223,51 @@ golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181117154741-2ddaf7f79a09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181130052023-1c3d964395ce/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190221204921-83362c3779f5/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190719005602-e377ae9d6386/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113232020-e2727e816f5a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200102140908-9497f49d5709/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200221224223-e1da425f72fd h1:hHkvGJK23seRCflePJnVa9IMv8fsuavSCWKd11kDQFs=
golang.org/x/tools v0.0.0-20200221224223-e1da425f72fd/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20201118030313-598b068a9102/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a h1:CB3a9Nez8M13wwlr/E2YtwoU+qYHKfC+JrDa45RXXoQ=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -1168,6 +1299,8 @@ gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/jcmturner/aescts.v1 v1.0.1 h1:cVVZBK2b1zY26haWB4vbBiZrfFQnfbTVrE3xZq6hrEw=
gopkg.in/jcmturner/aescts.v1 v1.0.1/go.mod h1:nsR8qBOg+OucoIW+WMhB3GspUQXq9XorLnQb9XtvcOo=
gopkg.in/jcmturner/dnsutils.v1 v1.0.1 h1:cIuC1OLRGZrld+16ZJvvZxVJeKPsvd5eUIvxfoN5hSM=
@ -1194,8 +1327,11 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200601152816-913338de1bd2 h1:VEmvx0P+GVTgkNu2EdTN988YCZPcD3lo9AoczZpucwc=
gopkg.in/yaml.v3 v3.0.0-20200601152816-913338de1bd2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
@ -1206,6 +1342,10 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=

View File

@ -64,6 +64,7 @@ func NewBitXHub(rep *repo.Repo) (*BitXHub, error) {
order.WithDigest(chainMeta.BlockHash.String()),
order.WithGetChainMetaFunc(bxh.Ledger.GetChainMeta),
order.WithGetBlockByHeightFunc(bxh.Ledger.GetBlock),
order.WithGetAccountNonceFunc(bxh.Ledger.GetNonce),
)
if err != nil {
return nil, err

View File

@ -22,15 +22,29 @@ type RegisterResult struct {
}
func (am *AppchainManager) Manager(des string, proposalResult string, extra []byte) *boltvm.Response {
specificAddrs := []string{constant.GovernanceContractAddr.Address().String()}
addrsData, err := json.Marshal(specificAddrs)
if err != nil {
return boltvm.Error("marshal specificAddrs error:" + string(err.Error()))
}
res := am.CrossInvoke(constant.RoleContractAddr.String(), "CheckPermission",
pb.String(string(PermissionSpecific)),
pb.String(""),
pb.String(am.CurrentCaller()),
pb.Bytes(addrsData))
if !res.Ok {
return boltvm.Error("check permission error:" + string(res.Result))
}
am.AppchainManager.Persister = am.Stub
chain := &appchainMgr.Appchain{}
if err := json.Unmarshal(extra, chain); err != nil {
return boltvm.Error("unmarshal json error:" + err.Error())
}
ok, err := am.AppchainManager.ChangeStatus(chain.ID, proposalResult)
ok, errData := am.AppchainManager.ChangeStatus(chain.ID, proposalResult)
if !ok {
return boltvm.Error(string(err))
return boltvm.Error(string(errData))
}
if proposalResult == string(APPOVED) {
@ -47,7 +61,7 @@ func (am *AppchainManager) Manager(des string, proposalResult string, extra []by
// Register appchain managers registers appchain info caller is the appchain
// manager address return appchain id and error
func (am *AppchainManager) Register(validators string, consensusType int32, chainType, name, desc, version, pubkey string) *boltvm.Response {
func (am *AppchainManager) Register(validators string, consensusType, chainType, name, desc, version, pubkey string) *boltvm.Response {
am.AppchainManager.Persister = am.Stub
ok, idData := am.AppchainManager.Register(am.Caller(), validators, consensusType, chainType, name, desc, version, pubkey)
if ok {
@ -81,10 +95,122 @@ func (am *AppchainManager) Register(validators string, consensusType int32, chai
return boltvm.Success(resData)
}
// UpdateAppchain updates approved appchain
func (am *AppchainManager) UpdateAppchain(validators string, consensusType int32, chainType, name, desc, version, pubkey string) *boltvm.Response {
// UpdateAppchain updates available appchain
func (am *AppchainManager) UpdateAppchain(validators string, consensusType, chainType, name, desc, version, pubkey string) *boltvm.Response {
am.AppchainManager.Persister = am.Stub
return responseWrapper(am.AppchainManager.UpdateAppchain(am.Caller(), validators, consensusType, chainType, name, desc, version, pubkey))
if ok, data := am.AppchainManager.ChangeStatus(am.Caller(), appchainMgr.EventUpdate); !ok {
return boltvm.Error(string(data))
}
chain := &appchainMgr.Appchain{
ID: am.Caller(),
Name: name,
Validators: validators,
ConsensusType: consensusType,
Status: appchainMgr.AppchainUpdating,
ChainType: chainType,
Desc: desc,
Version: version,
PublicKey: pubkey,
}
data, err := json.Marshal(chain)
if err != nil {
return boltvm.Error(err.Error())
}
return am.CrossInvoke(constant.GovernanceContractAddr.String(), "SubmitProposal",
pb.String(am.Caller()),
pb.String(appchainMgr.EventUpdate),
pb.String(string(AppchainMgr)),
pb.Bytes(data),
)
}
// FreezeAppchain freezes available appchain
func (am *AppchainManager) FreezeAppchain(id string) *boltvm.Response {
res := am.CrossInvoke(constant.RoleContractAddr.String(), "CheckPermission",
pb.String(string(PermissionSelfAdmin)),
pb.String(id),
pb.String(am.CurrentCaller()),
pb.Bytes(nil))
if !res.Ok {
return boltvm.Error("check permission error:" + string(res.Result))
}
am.AppchainManager.Persister = am.Stub
if ok, data := am.AppchainManager.ChangeStatus(id, appchainMgr.EventFreeze); !ok {
return boltvm.Error(string(data))
}
chain := &appchainMgr.Appchain{
ID: id,
}
data, err := json.Marshal(chain)
if err != nil {
return boltvm.Error(err.Error())
}
return am.CrossInvoke(constant.GovernanceContractAddr.String(), "SubmitProposal",
pb.String(am.Caller()),
pb.String(appchainMgr.EventFreeze),
pb.String(string(AppchainMgr)),
pb.Bytes(data),
)
}
// ActivateAppchain updates freezing appchain
func (am *AppchainManager) ActivateAppchain(id string) *boltvm.Response {
res := am.CrossInvoke(constant.RoleContractAddr.String(), "CheckPermission",
pb.String(string(PermissionSelfAdmin)),
pb.String(id),
pb.String(am.CurrentCaller()),
pb.Bytes(nil))
if !res.Ok {
return boltvm.Error("check permission error:" + string(res.Result))
}
am.AppchainManager.Persister = am.Stub
if ok, data := am.AppchainManager.ChangeStatus(id, appchainMgr.EventActivate); !ok {
return boltvm.Error(string(data))
}
chain := &appchainMgr.Appchain{
ID: id,
}
data, err := json.Marshal(chain)
if err != nil {
return boltvm.Error(err.Error())
}
return am.CrossInvoke(constant.GovernanceContractAddr.String(), "SubmitProposal",
pb.String(am.Caller()),
pb.String(appchainMgr.EventActivate),
pb.String(string(AppchainMgr)),
pb.Bytes(data),
)
}
// LogoutAppchain updates available appchain
func (am *AppchainManager) LogoutAppchain() *boltvm.Response {
am.AppchainManager.Persister = am.Stub
if ok, data := am.AppchainManager.ChangeStatus(am.Caller(), appchainMgr.EventLogout); !ok {
return boltvm.Error(string(data))
}
chain := &appchainMgr.Appchain{
ID: am.Caller(),
}
data, err := json.Marshal(chain)
if err != nil {
return boltvm.Error(err.Error())
}
return am.CrossInvoke(constant.GovernanceContractAddr.String(), "SubmitProposal",
pb.String(am.Caller()),
pb.String(appchainMgr.EventLogout),
pb.String(string(AppchainMgr)),
pb.Bytes(data),
)
}
// CountApprovedAppchains counts all approved appchains
@ -154,3 +280,23 @@ func responseWrapper(ok bool, data []byte) *boltvm.Response {
}
return boltvm.Error(string(data))
}
func (am *AppchainManager) IsAvailable(chainId string) *boltvm.Response {
am.AppchainManager.Persister = am.Stub
is, data := am.AppchainManager.GetAppchain(chainId)
if !is {
return boltvm.Error("get appchain info error: " + string(data))
}
app := &appchainMgr.Appchain{}
if err := json.Unmarshal(data, app); err != nil {
return boltvm.Error("unmarshal error: " + err.Error())
}
if app.Status != appchainMgr.AppchainAvailable {
return boltvm.Error("the appchain status is " + string(app.Status))
}
return boltvm.Success(nil)
}

View File

@ -37,7 +37,7 @@ func TestAppchainManager_Appchain(t *testing.T) {
ID: addr0,
Name: "appchain A",
Validators: "",
ConsensusType: 0,
ConsensusType: "",
ChainType: "fabric",
Desc: "",
Version: "",
@ -79,7 +79,7 @@ func TestAppchainManager_Appchains(t *testing.T) {
ID: addr,
Name: "appchain" + addr,
Validators: "",
ConsensusType: int32(i),
ConsensusType: "",
ChainType: "fabric",
Desc: "",
Version: "",
@ -178,7 +178,7 @@ func TestAppchainManager_Manager(t *testing.T) {
ID: "addr",
Name: "appchain A",
Validators: "",
ConsensusType: int32(1),
ConsensusType: "",
ChainType: "fabric",
Desc: "",
Version: "",
@ -192,7 +192,7 @@ func TestAppchainManager_Manager(t *testing.T) {
ID: "addr1",
Name: "appchain A",
Validators: "",
ConsensusType: int32(1),
ConsensusType: "",
ChainType: "fabric",
Desc: "",
Version: "",
@ -206,59 +206,124 @@ func TestAppchainManager_Manager(t *testing.T) {
mockStub.EXPECT().Has(AppchainKey("addr")).Return(true).AnyTimes()
mockStub.EXPECT().Has(AppchainKey("addr1")).Return(false).AnyTimes()
mockStub.EXPECT().SetObject(gomock.Any(), gomock.Any()).Return().AnyTimes()
mockStub.EXPECT().GetObject(gomock.Any(), gomock.Any()).Do(
func(key string, ret interface{}) bool {
chain := ret.(*appchainMgr.Appchain)
chain.Status = appchainMgr.AppchainAvailable
assert.Equal(t, key, AppchainKey("addr"))
return true
})
mockStub.EXPECT().CurrentCaller().Return("addrNotAdmin").Times(1)
mockStub.EXPECT().CurrentCaller().Return(constant.GovernanceContractAddr.String()).AnyTimes()
mockStub.EXPECT().CrossInvoke(constant.RoleContractAddr.String(), "CheckPermission", gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(boltvm.Error("")).Times(1)
mockStub.EXPECT().CrossInvoke(constant.RoleContractAddr.String(), "CheckPermission", gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(boltvm.Success(nil)).AnyTimes()
res := am.Manager(appchainMgr.EventUpdate, string(APPOVED), data1)
// test without permission
res := am.Manager(appchainMgr.EventUpdate, string(APPOVED), data)
assert.False(t, res.Ok)
// test with permission
res = am.Manager(appchainMgr.EventUpdate, string(APPOVED), data1)
assert.False(t, res.Ok)
res = am.Manager(appchainMgr.EventUpdate, string(REJECTED), data1)
assert.False(t, res.Ok)
res = am.Manager(appchainMgr.EventUpdate, string(APPOVED), data)
assert.True(t, res.Ok)
assert.True(t, res.Ok, string(res.Result))
res = am.Manager(appchainMgr.EventUpdate, string(REJECTED), data)
assert.True(t, res.Ok)
assert.True(t, res.Ok, string(res.Result))
mockStub.EXPECT().CrossInvoke(constant.InterchainContractAddr.String(), "Register", gomock.Any()).Return(boltvm.Error("")).Times(1)
mockStub.EXPECT().CrossInvoke(constant.InterchainContractAddr.String(), "Register", gomock.Any()).Return(boltvm.Success(nil)).AnyTimes()
res = am.Manager(appchainMgr.EventRegister, string(APPOVED), data)
assert.False(t, res.Ok)
res = am.Manager(appchainMgr.EventRegister, string(APPOVED), data)
assert.True(t, res.Ok)
assert.True(t, res.Ok, string(res.Result))
}
func TestUpdateChain(t *testing.T) {
am, mockStub, chains, _ := prepare(t)
func TestAppchainManager_IsAvailable(t *testing.T) {
am, mockStub, chains, chainsData := prepare(t)
mockStub.EXPECT().Get(AppchainKey(chains[0].ID)).Return(true, chainsData[0]).AnyTimes()
mockStub.EXPECT().Get(AppchainKey(chains[1].ID)).Return(true, chainsData[1]).AnyTimes()
mockStub.EXPECT().Get(AppchainKey("errId")).Return(false, nil).AnyTimes()
mockStub.EXPECT().Get(AppchainKey("unmarshalErrId")).Return(true, []byte("1")).AnyTimes()
res := am.IsAvailable(chains[0].ID)
assert.Equal(t, true, res.Ok, string(res.Result))
res = am.IsAvailable(chains[1].ID)
assert.Equal(t, false, res.Ok, string(res.Result))
res = am.IsAvailable("errId")
assert.Equal(t, false, res.Ok, string(res.Result))
res = am.IsAvailable("unmarshalErrId")
assert.Equal(t, false, res.Ok, string(res.Result))
}
func TestManageChain(t *testing.T) {
am, mockStub, chains, chainsData := prepare(t)
logger := log.NewWithModule("contracts")
// test for DeleteAppchain
mockStub.EXPECT().Caller().Return(caller).AnyTimes()
mockStub.EXPECT().Has(AppchainKey(caller)).Return(true).AnyTimes()
mockStub.EXPECT().CurrentCaller().Return(caller).AnyTimes()
mockStub.EXPECT().Has(gomock.Any()).Return(true).AnyTimes()
mockStub.EXPECT().SetObject(gomock.Any(), gomock.Any()).Return().AnyTimes()
mockStub.EXPECT().Logger().Return(logger).AnyTimes()
// TODO: test UpdateAppchain without register (false)
// test UpdateAppchain with register
mockStub.EXPECT().GetObject(gomock.Any(), gomock.Any()).Do(
func(key string, ret interface{}) bool {
chain := ret.(*appchainMgr.Appchain)
chain.Status = appchainMgr.AppchainAvailable
chain.PublicKey = chains[0].PublicKey
assert.Equal(t, key, AppchainKey(caller))
return true
})
mockStub.EXPECT().Get(AppchainKey(caller)).Return(true, chainsData[0]).AnyTimes()
mockStub.EXPECT().Get(AppchainKey("freezingChain")).Return(true, chainsData[1]).AnyTimes()
mockStub.EXPECT().CrossInvoke(constant.GovernanceContractAddr.String(), "SubmitProposal", gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(boltvm.Success(nil)).AnyTimes()
mockStub.EXPECT().CrossInvoke(constant.RoleContractAddr.String(), "CheckPermission", gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(boltvm.Success(nil)).AnyTimes()
// test UpdateAppchain
res := am.UpdateAppchain(chains[0].Validators, chains[0].ConsensusType, chains[0].ChainType,
chains[0].Name, chains[0].Desc, chains[0].Version, chains[0].PublicKey)
assert.Equal(t, true, res.Ok)
assert.Equal(t, true, res.Ok, string(res.Result))
// test FreezeAppchain
res = am.FreezeAppchain(caller)
assert.Equal(t, true, res.Ok, string(res.Result))
// test ActivateAppchain
res = am.ActivateAppchain("freezingChain")
assert.Equal(t, true, res.Ok, string(res.Result))
// test LogoutAppchain
res = am.LogoutAppchain()
assert.Equal(t, true, res.Ok, string(res.Result))
}
func TestManageChain_WithoutPermission(t *testing.T) {
am, mockStub, _, _ := prepare(t)
mockStub.EXPECT().Caller().Return(caller).AnyTimes()
mockStub.EXPECT().CurrentCaller().Return(caller).AnyTimes()
mockStub.EXPECT().CrossInvoke(constant.GovernanceContractAddr.String(), "SubmitProposal", gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(boltvm.Success(nil)).AnyTimes()
mockStub.EXPECT().CrossInvoke(constant.RoleContractAddr.String(), "CheckPermission", gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(boltvm.Error("")).AnyTimes()
// test FreezeAppchain
res := am.FreezeAppchain("addr")
assert.Equal(t, false, res.Ok, string(res.Result))
res = am.FreezeAppchain("addr")
assert.Equal(t, false, res.Ok, string(res.Result))
// test ActivateAppchain
res = am.ActivateAppchain("addr")
assert.Equal(t, false, res.Ok, string(res.Result))
}
func TestManageChain_Error(t *testing.T) {
am, mockStub, chains, _ := prepare(t)
logger := log.NewWithModule("contracts")
mockStub.EXPECT().Caller().Return(caller).AnyTimes()
mockStub.EXPECT().CurrentCaller().Return(caller).AnyTimes()
mockStub.EXPECT().Has(gomock.Any()).Return(true).AnyTimes()
mockStub.EXPECT().SetObject(gomock.Any(), gomock.Any()).Return().AnyTimes()
mockStub.EXPECT().Logger().Return(logger).AnyTimes()
mockStub.EXPECT().Get(gomock.Any()).Return(false, nil).AnyTimes()
mockStub.EXPECT().CrossInvoke(constant.RoleContractAddr.String(), "CheckPermission", gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(boltvm.Success(nil)).AnyTimes()
// test UpdateAppchain
res := am.UpdateAppchain(chains[0].Validators, chains[0].ConsensusType, chains[0].ChainType,
chains[0].Name, chains[0].Desc, chains[0].Version, chains[0].PublicKey)
assert.Equal(t, false, res.Ok, string(res.Result))
// test FreezeAppchain
res = am.FreezeAppchain(caller)
assert.Equal(t, false, res.Ok, string(res.Result))
// test ActivateAppchain
res = am.ActivateAppchain(caller)
assert.Equal(t, false, res.Ok, string(res.Result))
// test LogoutAppchain
res = am.LogoutAppchain()
assert.Equal(t, false, res.Ok, string(res.Result))
}
func TestCountApprovedAppchains(t *testing.T) {
am, mockStub, _, chainsData := prepare(t)
logger := log.NewWithModule("contracts")
// test for DeleteAppchain
mockStub.EXPECT().Caller().Return(caller).AnyTimes()
mockStub.EXPECT().SetObject(gomock.Any(), gomock.Any()).Return().AnyTimes()
mockStub.EXPECT().Logger().Return(logger).AnyTimes()
@ -266,7 +331,7 @@ func TestCountApprovedAppchains(t *testing.T) {
mockStub.EXPECT().Query(appchainMgr.PREFIX).Return(true, chainsData)
res := am.CountAvailableAppchains()
assert.Equal(t, true, res.Ok)
assert.Equal(t, "2", string(res.Result))
assert.Equal(t, "1", string(res.Result))
}
func TestDeleteAppchain(t *testing.T) {
@ -281,12 +346,26 @@ func TestDeleteAppchain(t *testing.T) {
mockStub.EXPECT().Caller().Return(caller).AnyTimes()
mockStub.EXPECT().SetObject(gomock.Any(), gomock.Any()).Return().AnyTimes()
mockStub.EXPECT().Logger().Return(logger).AnyTimes()
mockStub.EXPECT().CrossInvoke(constant.RoleContractAddr.String(), "IsAdmin", gomock.Any()).Return(approveRes)
mockStub.EXPECT().CrossInvoke(constant.RoleContractAddr.String(), "IsAdmin", gomock.Any()).Return(boltvm.Success(nil)).Times(1)
mockStub.EXPECT().CrossInvoke(constant.RoleContractAddr.String(), "IsAdmin", gomock.Any()).Return(boltvm.Success([]byte(strconv.FormatBool(false)))).Times(1)
mockStub.EXPECT().CrossInvoke(constant.RoleContractAddr.String(), "IsAdmin", gomock.Any()).Return(approveRes).AnyTimes()
mockStub.EXPECT().CrossInvoke(constant.InterchainContractAddr.String(), "DeleteInterchain",
gomock.Any()).Return(approveRes)
gomock.Any()).Return(boltvm.Error("")).Times(1)
mockStub.EXPECT().CrossInvoke(constant.InterchainContractAddr.String(), "DeleteInterchain",
gomock.Any()).Return(approveRes).AnyTimes()
mockStub.EXPECT().Delete(AppchainKey(caller)).Return()
// judge caller type error
res := am.DeleteAppchain(caller)
assert.Equal(t, false, res.Ok)
// caller is not an admin account
res = am.DeleteAppchain(caller)
assert.Equal(t, false, res.Ok)
// CrossInvoke DeleteInterchain error
res = am.DeleteAppchain(caller)
assert.Equal(t, false, res.Ok)
res = am.DeleteAppchain(caller)
assert.Equal(t, true, res.Ok)
}
@ -317,15 +396,16 @@ func prepare(t *testing.T) (*AppchainManager, *mock_stub.MockStub, []*appchainMg
var chains []*appchainMgr.Appchain
var chainsData [][]byte
chainType := []string{string(appchainMgr.AppchainAvailable), string(appchainMgr.AppchainFrozen)}
for i := 0; i < 2; i++ {
addr := types.NewAddress([]byte{byte(i)}).String()
chain := &appchainMgr.Appchain{
Status: appchainMgr.AppchainAvailable,
Status: appchainMgr.AppchainStatus(chainType[i]),
ID: addr,
Name: "appchain" + addr,
Validators: "",
ConsensusType: int32(i),
ConsensusType: "",
ChainType: "fabric",
Desc: "",
Version: "",
@ -491,7 +571,8 @@ func TestInterchainManager_HandleIBTP(t *testing.T) {
ID: "",
Name: "Relay1",
Validators: "",
ConsensusType: 0,
ConsensusType: "",
Status: appchainMgr.AppchainAvailable,
ChainType: "appchain",
Desc: "Relay1",
Version: "1",
@ -515,12 +596,6 @@ func TestInterchainManager_HandleIBTP(t *testing.T) {
From: from,
}
// srcChain := &appchainMgr.Appchain{}
// ress := im.CrossInvoke(constant.AppchainMgrContractAddr.String(), "GetAppchain", pb.String(ibtp.From))
// t.Log("ress: ", ress)
// if err := json.Unmarshal(ress.Result, srcChain); err != nil {
// t.Log("err: ", err)
// }
res := im.HandleIBTP(ibtp)
assert.False(t, res.Ok)
assert.Equal(t, "this appchain does not exist", string(res.Result))
@ -653,9 +728,24 @@ func TestInterchainManager_HandleIBTPs(t *testing.T) {
data0, err := interchain.Marshal()
assert.Nil(t, err)
appchain := &appchainMgr.Appchain{
ID: "",
Name: "Relay1",
Validators: "",
ConsensusType: "",
Status: appchainMgr.AppchainAvailable,
ChainType: "appchain",
Desc: "Relay1",
Version: "1",
PublicKey: "",
}
appchainData, err := json.Marshal(appchain)
assert.Nil(t, err)
mockStub.EXPECT().Get(appchainMgr.PREFIX+caller).Return(true, data0).AnyTimes()
mockStub.EXPECT().Get(appchainMgr.PREFIX+to).Return(true, data0).AnyTimes()
mockStub.EXPECT().CrossInvoke(gomock.Any(), gomock.Any(), gomock.Any()).Return(boltvm.Success(nil)).AnyTimes()
mockStub.EXPECT().CrossInvoke(constant.TransactionMgrContractAddr.String(), gomock.Any(), gomock.Any()).Return(boltvm.Success(nil)).AnyTimes()
mockStub.EXPECT().CrossInvoke(constant.AppchainMgrContractAddr.String(), "GetAppchain", pb.String(caller)).Return(boltvm.Success(appchainData)).AnyTimes()
mockStub.EXPECT().AddObject(gomock.Any(), gomock.Any()).AnyTimes()
mockStub.EXPECT().GetTxIndex().Return(uint64(1)).AnyTimes()
mockStub.EXPECT().PostInterchainEvent(gomock.Any()).AnyTimes()
@ -685,7 +775,7 @@ func TestInterchainManager_HandleIBTPs(t *testing.T) {
assert.Nil(t, err)
res := im.HandleIBTPs(data)
fmt.Printf("result is %v", string(res.Result))
assert.Equal(t, true, res.Ok)
assert.Equal(t, true, res.Ok, string(res.Result))
}
func TestInterchainManager_HandleUnionIBTP(t *testing.T) {
@ -715,7 +805,7 @@ func TestInterchainManager_HandleUnionIBTP(t *testing.T) {
ID: from,
Name: "appchain" + from,
Validators: "",
ConsensusType: int32(1),
ConsensusType: "",
ChainType: "fabric",
Desc: "",
Version: "",
@ -742,6 +832,7 @@ func TestInterchainManager_HandleUnionIBTP(t *testing.T) {
data, err := json.Marshal(relayChain)
assert.Nil(t, err)
mockStub.EXPECT().Get(appchainMgr.PREFIX+from).Return(true, data0).AnyTimes()
mockStub.EXPECT().Get(appchainMgr.PREFIX+from+"-"+from).Return(true, data0).AnyTimes()
mockStub.EXPECT().CrossInvoke(gomock.Any(), gomock.Any(), gomock.Any()).Return(boltvm.Success(data)).AnyTimes()
mockStub.EXPECT().AddObject(gomock.Any(), gomock.Any()).AnyTimes()
@ -990,6 +1081,38 @@ func TestRole_GetRoleWeight(t *testing.T) {
assert.False(t, res.Ok)
}
func TestRole_CheckPermission(t *testing.T) {
mockCtl := gomock.NewController(t)
mockStub := mock_stub.NewMockStub(mockCtl)
admins := []*repo.Admin{
&repo.Admin{
Address: "0xc7F999b83Af6DF9e67d0a37Ee7e900bF38b3D013",
Weight: 1,
},
}
mockStub.EXPECT().GetObject(adminRolesKey, gomock.Any()).SetArg(1, admins).AnyTimes()
im := &Role{mockStub}
res := im.CheckPermission(string(PermissionAdmin), "", admins[0].Address, nil)
assert.True(t, res.Ok, string(res.Result))
res = im.CheckPermission(string(PermissionAdmin), "", types.NewAddress([]byte{2}).String(), nil)
assert.False(t, res.Ok, string(res.Result))
res = im.CheckPermission(string(PermissionSelfAdmin), "", admins[0].Address, nil)
assert.True(t, res.Ok, string(res.Result))
res = im.CheckPermission(string(PermissionSelfAdmin), "", types.NewAddress([]byte{2}).String(), nil)
assert.False(t, res.Ok, string(res.Result))
addrData, err := json.Marshal([]string{admins[0].Address})
assert.Nil(t, err)
res = im.CheckPermission(string(PermissionSpecific), "", admins[0].Address, addrData)
assert.True(t, res.Ok, string(res.Result))
res = im.CheckPermission(string(PermissionSpecific), "", "", addrData)
assert.False(t, res.Ok, string(res.Result))
}
func TestRuleManager_RegisterRule(t *testing.T) {
mockCtl := gomock.NewController(t)
mockStub := mock_stub.NewMockStub(mockCtl)
@ -997,8 +1120,8 @@ func TestRuleManager_RegisterRule(t *testing.T) {
id0 := types.NewAddress([]byte{0}).String()
id1 := types.NewAddress([]byte{1}).String()
mockStub.EXPECT().CrossInvoke(constant.AppchainMgrContractAddr.String(), "GetAppchain", pb.String(id0)).Return(boltvm.Success(nil))
mockStub.EXPECT().CrossInvoke(constant.AppchainMgrContractAddr.String(), "GetAppchain", pb.String(id1)).Return(boltvm.Error(""))
mockStub.EXPECT().CrossInvoke(constant.AppchainMgrContractAddr.String(), "IsAvailable", pb.String(id0)).Return(boltvm.Success(nil))
mockStub.EXPECT().CrossInvoke(constant.AppchainMgrContractAddr.String(), "IsAvailable", pb.String(id1)).Return(boltvm.Error(""))
mockStub.EXPECT().SetObject(gomock.Any(), gomock.Any()).AnyTimes()
im := &RuleManager{mockStub}
@ -1009,7 +1132,6 @@ func TestRuleManager_RegisterRule(t *testing.T) {
res = im.RegisterRule(id1, addr)
assert.False(t, res.Ok)
assert.Equal(t, "this appchain does not exist", string(res.Result))
}
func TestRuleManager_GetRuleAddress(t *testing.T) {
@ -1590,7 +1712,11 @@ func TestGovernance_SubmitProposal(t *testing.T) {
mockStub.EXPECT().CrossInvoke(constant.RoleContractAddr.String(), "GetAdminRoles").Return(boltvm.Success(adminsData)).AnyTimes()
mockStub.EXPECT().GetObject(gomock.Any(), gomock.Any()).Return(false).AnyTimes()
mockStub.EXPECT().AddObject(gomock.Any(), gomock.Any()).AnyTimes()
mockStub.EXPECT().CrossInvoke(constant.RoleContractAddr.String(), "CheckPermission", gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(boltvm.Error("")).Times(1)
mockStub.EXPECT().CrossInvoke(constant.RoleContractAddr.String(), "CheckPermission", gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(boltvm.Success(nil)).AnyTimes()
mockStub.EXPECT().CurrentCaller().Return("").AnyTimes()
// check permission error
res := g.SubmitProposal("", "des", string(AppchainMgr), []byte{})
assert.False(t, res.Ok, string(res.Result))
// GetAdminRoles error

View File

@ -76,6 +76,20 @@ type Proposal struct {
}
func (g *Governance) SubmitProposal(from, des string, typ string, extra []byte) *boltvm.Response {
specificAddrs := []string{constant.AppchainMgrContractAddr.Address().String()}
addrsData, err := json.Marshal(specificAddrs)
if err != nil {
return boltvm.Error("marshal specificAddrs error:" + string(err.Error()))
}
res := g.CrossInvoke(constant.RoleContractAddr.String(), "CheckPermission",
pb.String(string(PermissionSpecific)),
pb.String(""),
pb.String(g.CurrentCaller()),
pb.Bytes(addrsData))
if !res.Ok {
return boltvm.Error("check permission error:" + string(res.Result))
}
ret, err := g.getProposalsByFrom(from)
if err != nil {
return boltvm.Error(err.Error())
@ -103,9 +117,6 @@ func (g *Governance) SubmitProposal(from, des string, typ string, extra []byte)
ThresholdNum: tn,
Extra: extra,
}
if err := checkProposalInfo(p); err != nil {
return boltvm.Error(err.Error())
}
g.AddObject(ProposalKey(p.Id), *p)
@ -115,7 +126,7 @@ func (g *Governance) SubmitProposal(from, des string, typ string, extra []byte)
func (g *Governance) getElectorateNum() (uint64, error) {
res := g.CrossInvoke(constant.RoleContractAddr.String(), "GetAdminRoles")
if !res.Ok {
return 0, fmt.Errorf(string(res.Result))
return 0, fmt.Errorf("get admin roles error: %s", string(res.Result))
}
var admins []*repo.Admin
@ -549,6 +560,8 @@ func (g *Governance) setVote(p *Proposal, addr string, approve string, reason st
p.ApproveNum = p.ApproveNum + uint64(num)
case BallotReject:
p.AgainstNum = p.AgainstNum + uint64(num)
default:
return fmt.Errorf("the info of vote should be approve or reject")
}
g.SetObject(ProposalKey(p.Id), *p)

View File

@ -108,9 +108,9 @@ func (x *InterchainManager) HandleIBTP(ibtp *pb.IBTP) *boltvm.Response {
return x.handleUnionIBTP(ibtp)
}
interchain, ok := x.getInterchain(ibtp.From)
if !ok {
return boltvm.Error("this appchain does not exist")
interchain, _, err := x.checkAppchain(ibtp.From)
if err != nil {
return boltvm.Error(err.Error())
}
if err := x.checkIBTP(ibtp, interchain); err != nil {
@ -139,9 +139,9 @@ func (x *InterchainManager) HandleIBTP(ibtp *pb.IBTP) *boltvm.Response {
}
func (x *InterchainManager) HandleIBTPs(data []byte) *boltvm.Response {
ok := x.Has(AppchainKey(x.Caller()))
if !ok {
return boltvm.Error("this appchain does not exist")
interchain, _, err := x.checkAppchain(x.Caller())
if err != nil {
return boltvm.Error(err.Error())
}
ibtps := &pb.IBTPs{}
@ -149,9 +149,6 @@ func (x *InterchainManager) HandleIBTPs(data []byte) *boltvm.Response {
return boltvm.Error(err.Error())
}
interchain := &pb.Interchain{}
x.GetObject(AppchainKey(x.Caller()), interchain)
for _, ibtp := range ibtps.Ibtps {
if err := x.checkIBTP(ibtp, interchain); err != nil {
return boltvm.Error(err.Error())
@ -217,6 +214,29 @@ func (x *InterchainManager) checkIBTP(ibtp *pb.IBTP, interchain *pb.Interchain)
return nil
}
func (x *InterchainManager) checkAppchain(id string) (*pb.Interchain, *appchainMgr.Appchain, error) {
interchain, ok := x.getInterchain(id)
if !ok {
return nil, nil, fmt.Errorf("this appchain does not exist")
}
app := &appchainMgr.Appchain{}
res := x.CrossInvoke(constant.AppchainMgrContractAddr.String(), "GetAppchain", pb.String(id))
if !res.Ok {
return nil, nil, fmt.Errorf("get appchain info error: " + string(res.Result))
}
if err := json.Unmarshal(res.Result, app); err != nil {
return nil, nil, fmt.Errorf("unmarshal error: " + err.Error())
}
if app.Status != appchainMgr.AppchainAvailable {
return nil, nil, fmt.Errorf("the appchain status is " + string(app.Status) + ", can not handle IBTP")
}
return interchain, app, nil
}
// isRelayIBTP returns whether ibtp.from is relaychain type
func (x *InterchainManager) isRelayIBTP(from string) bool {
srcChain := &appchainMgr.Appchain{}
@ -328,9 +348,10 @@ func (x *InterchainManager) GetIBTPByID(id string) *boltvm.Response {
func (x *InterchainManager) handleUnionIBTP(ibtp *pb.IBTP) *boltvm.Response {
srcRelayChainID := strings.Split(ibtp.From, "-")[0]
ok := x.Has(AppchainKey(srcRelayChainID))
if !ok {
return boltvm.Error("this relay chain does not exist")
_, app, err := x.checkAppchain(srcRelayChainID)
if err != nil {
return boltvm.Error(err.Error())
}
if ibtp.To == "" {
@ -340,12 +361,6 @@ func (x *InterchainManager) handleUnionIBTP(ibtp *pb.IBTP) *boltvm.Response {
return boltvm.Error(fmt.Sprintf("target appchain does not exist: %s", ibtp.To))
}
app := &appchainMgr.Appchain{}
res := x.CrossInvoke(constant.AppchainMgrContractAddr.String(), "GetAppchain", pb.String(srcRelayChainID))
if err := json.Unmarshal(res.Result, app); err != nil {
return boltvm.Error(err.Error())
}
interchain, ok := x.getInterchain(ibtp.From)
if !ok {
interchain = &pb.Interchain{

View File

@ -36,16 +36,20 @@ func (r *Role) GetRole() *boltvm.Response {
}
func (r *Role) IsAdmin(address string) *boltvm.Response {
return boltvm.Success([]byte(strconv.FormatBool(r.isAdmin(address))))
}
func (r *Role) isAdmin(address string) bool {
var admins []*repo.Admin
r.GetObject(adminRolesKey, &admins)
for _, admin := range admins {
if admin.Address == address {
return boltvm.Success([]byte(strconv.FormatBool(true)))
return true
}
}
return boltvm.Success([]byte(strconv.FormatBool(false)))
return false
}
func (r *Role) GetAdminRoles() *boltvm.Response {
@ -88,5 +92,43 @@ func (r *Role) GetRoleWeight(address string) *boltvm.Response {
}
}
return boltvm.Error("account at the address does not exist:" + address)
return boltvm.Error("the account at this address is not an administrator: " + address)
}
// Permission manager
type Permission string
const (
PermissionAdmin Permission = "PermissionAdmin"
PermissionSelfAdmin Permission = "PermissionSelfAdmin"
PermissionSpecific Permission = "PermissionSpecific"
)
func (r *Role) CheckPermission(permission string, regulatedId string, regulatorAddr string, specificAddrsData []byte) *boltvm.Response {
switch permission {
case string(PermissionAdmin):
if !r.isAdmin(regulatorAddr) {
return boltvm.Error("caller is not an admin account: " + regulatorAddr)
}
case string(PermissionSelfAdmin):
if regulatorAddr != regulatedId && !r.isAdmin(regulatorAddr) {
return boltvm.Error("caller is not an admin account or appchain self: " + regulatorAddr)
}
case string(PermissionSpecific):
specificAddrs := []string{}
err := json.Unmarshal(specificAddrsData, &specificAddrs)
if err != nil {
return boltvm.Error(err.Error())
}
for _, addr := range specificAddrs {
if addr == regulatorAddr {
return boltvm.Success(nil)
}
}
return boltvm.Error("caller is not specific account: " + regulatorAddr)
default:
return boltvm.Error("unsupport permission: " + permission)
}
return boltvm.Success(nil)
}

View File

@ -32,9 +32,8 @@ type ruleRecord struct {
// SetRule can map the validation rule address with the chain id
func (r *RuleManager) RegisterRule(id string, address string) *boltvm.Response {
if res := r.CrossInvoke(constant.AppchainMgrContractAddr.String(), "GetAppchain", pb.String(id)); !res.Ok {
return boltvm.Error("this appchain does not exist")
if res := r.CrossInvoke(constant.AppchainMgrContractAddr.String(), "IsAvailable", pb.String(id)); !res.Ok {
return boltvm.Error(string(res.Result))
}
rl := &Rule{

View File

@ -200,6 +200,8 @@ func TestBlockExecutor_ApplyReadonlyTransactions(t *testing.T) {
mockLedger.EXPECT().PersistExecutionResult(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
mockLedger.EXPECT().FlushDirtyDataAndComputeJournal().Return(make(map[string]*ledger.Account), &ledger.BlockJournal{}).AnyTimes()
mockLedger.EXPECT().PersistBlockData(gomock.Any()).AnyTimes()
mockLedger.EXPECT().GetNonce(gomock.Any()).Return(uint64(0)).AnyTimes()
mockLedger.EXPECT().SetNonce(gomock.Any(), gomock.Any()).AnyTimes()
logger := log.NewWithModule("executor")
exec, err := New(mockLedger, logger, executorType)

View File

@ -20,6 +20,7 @@ import (
"github.com/meshplus/bitxhub/pkg/vm"
"github.com/meshplus/bitxhub/pkg/vm/boltvm"
"github.com/meshplus/bitxhub/pkg/vm/wasm"
"github.com/meshplus/bitxhub/pkg/vm/wasm/vmledger"
"github.com/sirupsen/logrus"
)
@ -271,6 +272,9 @@ func (exec *BlockExecutor) postBlockEvent(block *pb.Block, interchainMeta *pb.In
}
func (exec *BlockExecutor) applyTransaction(i int, tx *pb.Transaction, opt *agency.TxOpt) ([]byte, error) {
curNonce := exec.ledger.GetNonce(tx.From)
defer exec.ledger.SetNonce(tx.From, curNonce+1)
if tx.IsIBTP() {
ctx := vm.NewContext(tx, uint64(i), nil, exec.ledger, exec.logger)
instance := boltvm.New(ctx, exec.validationEngine, exec.getContracts(opt))
@ -298,7 +302,7 @@ func (exec *BlockExecutor) applyTransaction(i int, tx *pb.Transaction, opt *agen
instance = boltvm.New(ctx, exec.validationEngine, exec.getContracts(opt))
case pb.TransactionData_XVM:
ctx := vm.NewContext(tx, uint64(i), data, exec.ledger, exec.logger)
imports, err := wasm.EmptyImports()
imports, err := vmledger.New()
if err != nil {
return nil, err
}

View File

@ -58,7 +58,9 @@ func (ac *AccountCache) addToReadCache(accounts map[string]*Account) error {
for addr, account := range accounts {
var stateCache *lru.Cache
ac.innerAccountCache.Add(addr, account.dirtyAccount)
if account.dirtyAccount != nil {
ac.innerAccountCache.Add(addr, account.dirtyAccount)
}
value, ok := ac.stateCache.Get(addr)
if ok {
stateCache = value.(*lru.Cache)
@ -91,7 +93,9 @@ func (ac *AccountCache) addToWriteBuffer(accounts map[string]*Account) {
defer ac.rwLock.Unlock()
for addr, account := range accounts {
ac.innerAccounts[addr] = account.dirtyAccount
if account.dirtyAccount != nil {
ac.innerAccounts[addr] = account.dirtyAccount
}
stateMap, ok := ac.states[addr]
if !ok {
stateMap = make(map[string][]byte)

View File

@ -4,25 +4,27 @@ import (
"fmt"
"github.com/meshplus/bitxhub-kit/crypto"
"github.com/meshplus/bitxhub-kit/types"
"github.com/meshplus/bitxhub-model/pb"
"github.com/meshplus/bitxhub/pkg/peermgr"
"github.com/sirupsen/logrus"
)
type Config struct {
ID uint64
IsNew bool
RepoRoot string
StoragePath string
PluginPath string
PeerMgr peermgr.PeerManager
PrivKey crypto.PrivateKey
Logger logrus.FieldLogger
Nodes map[uint64]*pb.VpInfo
Applied uint64
Digest string
GetChainMetaFunc func() *pb.ChainMeta
GetBlockByHeight func(height uint64) (*pb.Block, error)
ID uint64
IsNew bool
RepoRoot string
StoragePath string
PluginPath string
PeerMgr peermgr.PeerManager
PrivKey crypto.PrivateKey
Logger logrus.FieldLogger
Nodes map[uint64]*pb.VpInfo
Applied uint64
Digest string
GetChainMetaFunc func() *pb.ChainMeta
GetBlockByHeight func(height uint64) (*pb.Block, error)
GetAccountNonce func(address *types.Address) uint64
}
type Option func(*Config)
@ -105,6 +107,12 @@ func WithGetBlockByHeightFunc(f func(height uint64) (*pb.Block, error)) Option {
}
}
func WithGetAccountNonceFunc(f func(address *types.Address) uint64) Option {
return func(config *Config) {
config.GetAccountNonce = f
}
}
func checkConfig(config *Config) error {
if config.Logger == nil {
return fmt.Errorf("logger is nil")

View File

@ -93,10 +93,11 @@ func NewNode(opts ...order.Option) (order.Order, error) {
return nil, fmt.Errorf("generate raft txpool config: %w", err)
}
mempoolConf := &mempool.Config{
ID: config.ID,
ChainHeight: config.Applied,
Logger: config.Logger,
StoragePath: config.StoragePath,
ID: config.ID,
ChainHeight: config.Applied,
Logger: config.Logger,
StoragePath: config.StoragePath,
GetAccountNonce: config.GetAccountNonce,
BatchSize: raftConfig.RAFT.MempoolConfig.BatchSize,
PoolSize: raftConfig.RAFT.MempoolConfig.PoolSize,

View File

@ -55,6 +55,9 @@ func TestNode_Start(t *testing.T) {
order.WithStoragePath(repo.GetStoragePath(repoRoot, "order")),
order.WithLogger(log.NewWithModule("consensus")),
order.WithApplied(1),
order.WithGetAccountNonceFunc(func(address *types.Address) uint64 {
return 0
}),
)
require.Nil(t, err)
@ -111,6 +114,9 @@ func TestMulti_Node_Start(t *testing.T) {
order.WithLogger(log.NewWithModule("consensus")),
order.WithGetBlockByHeightFunc(nil),
order.WithApplied(1),
order.WithGetAccountNonceFunc(func(address *types.Address) uint64 {
return 0
}),
)
require.Nil(t, err)
err = order.Start()
@ -166,6 +172,9 @@ func TestMulti_Node_Start_Without_Cert_Verification(t *testing.T) {
order.WithLogger(log.NewWithModule("consensus")),
order.WithGetBlockByHeightFunc(nil),
order.WithApplied(1),
order.WithGetAccountNonceFunc(func(address *types.Address) uint64 {
return 0
}),
)
require.Nil(t, err)
err = order.Start()

View File

@ -69,6 +69,9 @@ func mockRaftNode(t *testing.T) (*Node, error) {
TxSliceSize: raftConfig.RAFT.MempoolConfig.TxSliceSize,
TxSliceTimeout: raftConfig.RAFT.MempoolConfig.TxSliceTimeout,
StoragePath: filepath.Join(repoRoot, "storage"),
GetAccountNonce: func(address *types.Address) uint64 {
return 0
},
}
mempoolInst, err := mempool.NewMempool(mempoolConf)
if err != nil {

View File

@ -1,10 +1,8 @@
package mempool
import (
"fmt"
"math"
"os"
"path/filepath"
"sync"
"time"
@ -29,17 +27,13 @@ type mempoolImpl struct {
}
func newMempoolImpl(config *Config) (*mempoolImpl, error) {
db, err := loadOrCreateStorage(filepath.Join(config.StoragePath, "mempool"))
if err != nil {
return nil, fmt.Errorf("load or create mem pool storage :%w", err)
}
mpi := &mempoolImpl{
localID: config.ID,
batchSeqNo: config.ChainHeight,
logger: config.Logger,
txSliceSize: config.TxSliceSize,
}
mpi.txStore = newTransactionStore(db, config.Logger)
mpi.txStore = newTransactionStore(config.GetAccountNonce, config.Logger)
if config.BatchSize == 0 {
mpi.batchSize = DefaultBatchSize
} else {
@ -162,7 +156,7 @@ func (mpi *mempoolImpl) generateBlock() (*raftproto.RequestBatch, error) {
}
// include transaction if it's "next" for given account or
// we've already sent its ancestor to Consensus
if seenPrevious || (txSeq == commitNonce) {
if seenPrevious || (txSeq == commitNonce+1) {
ptr := orderedIndexKey{account: tx.account, nonce: txSeq}
mpi.txStore.batchedTxs[ptr] = true
result = append(result, ptr)
@ -229,7 +223,7 @@ func (mpi *mempoolImpl) processCommitTransactions(state *ChainState) {
continue
}
preCommitNonce := mpi.txStore.nonceCache.getCommitNonce(txPointer.account)
newCommitNonce := txPointer.nonce + 1
newCommitNonce := txPointer.nonce
if updateAccounts[txPointer.account] < newCommitNonce && preCommitNonce < newCommitNonce {
updateAccounts[txPointer.account] = newCommitNonce
}
@ -245,7 +239,7 @@ func (mpi *mempoolImpl) processCommitTransactions(state *ChainState) {
commitNonce := mpi.txStore.nonceCache.getCommitNonce(account)
if list, ok := mpi.txStore.allTxs[account]; ok {
// remove all previous seq number txs for this account.
removedTxs := list.forward(commitNonce)
removedTxs := list.forward(commitNonce + 1)
// remove index smaller than commitNonce delete index.
var wg sync.WaitGroup
wg.Add(4)

View File

@ -153,9 +153,9 @@ func TestProcessTransactions(t *testing.T) {
ast.Equal(5, len(mpi.txStore.txHashMap))
ast.Equal(2, mpi.txStore.allTxs[account1.String()].index.size())
ast.Equal(3, mpi.txStore.allTxs[account2.String()].index.size())
ast.Equal(uint64(1), mpi.txStore.nonceCache.getCommitNonce(account1.String()))
ast.Equal(uint64(0), mpi.txStore.nonceCache.getCommitNonce(account1.String()))
ast.Equal(uint64(3), mpi.txStore.nonceCache.getPendingNonce(account1.String()))
ast.Equal(uint64(1), mpi.txStore.nonceCache.getCommitNonce(account2.String()))
ast.Equal(uint64(0), mpi.txStore.nonceCache.getCommitNonce(account2.String()))
ast.Equal(uint64(3), mpi.txStore.nonceCache.getPendingNonce(account2.String()))
mpi.batchSize = 4
@ -231,9 +231,9 @@ func TestUnorderedIncomingTxs(t *testing.T) {
ast.Equal(6, len(mpi.txStore.txHashMap))
ast.Equal(3, mpi.txStore.allTxs[account1.String()].index.size())
ast.Equal(3, mpi.txStore.allTxs[account2.String()].index.size())
ast.Equal(uint64(1), mpi.txStore.nonceCache.getCommitNonce(account1.String()))
ast.Equal(uint64(0), mpi.txStore.nonceCache.getCommitNonce(account1.String()))
ast.Equal(uint64(3), mpi.txStore.nonceCache.getPendingNonce(account1.String()))
ast.Equal(uint64(1), mpi.txStore.nonceCache.getCommitNonce(account2.String()))
ast.Equal(uint64(0), mpi.txStore.nonceCache.getCommitNonce(account2.String()))
ast.Equal(uint64(3), mpi.txStore.nonceCache.getPendingNonce(account2.String()))
tx7 := constructTx(uint64(3), &privKey1)
@ -275,7 +275,7 @@ func TestUnorderedIncomingTxs(t *testing.T) {
ast.Equal(1, mpi.txStore.allTxs[account1.String()].index.size())
ast.Equal(2, mpi.txStore.allTxs[account2.String()].index.size())
ast.Equal(uint64(5), mpi.txStore.nonceCache.getPendingNonce(account1.String()))
ast.Equal(uint64(3), mpi.txStore.nonceCache.getCommitNonce(account2.String()))
ast.Equal(uint64(2), mpi.txStore.nonceCache.getCommitNonce(account2.String()))
ast.Equal(uint64(3), mpi.txStore.nonceCache.getPendingNonce(account2.String()))
// generate block3
@ -298,9 +298,9 @@ func TestUnorderedIncomingTxs(t *testing.T) {
ast.Equal(7, len(mpi.txStore.txHashMap))
ast.Equal(3, mpi.txStore.allTxs[account1.String()].index.size())
ast.Equal(4, mpi.txStore.allTxs[account2.String()].index.size())
ast.Equal(uint64(4), mpi.txStore.nonceCache.getCommitNonce(account1.String()))
ast.Equal(uint64(3), mpi.txStore.nonceCache.getCommitNonce(account1.String()))
ast.Equal(uint64(7), mpi.txStore.nonceCache.getPendingNonce(account1.String()))
ast.Equal(uint64(3), mpi.txStore.nonceCache.getCommitNonce(account2.String()))
ast.Equal(uint64(2), mpi.txStore.nonceCache.getCommitNonce(account2.String()))
ast.Equal(uint64(7), mpi.txStore.nonceCache.getPendingNonce(account2.String()))
}
@ -348,9 +348,9 @@ func TestGetTimeoutTransaction(t *testing.T) {
ast.Equal(8, len(mpi.txStore.txHashMap))
ast.Equal(4, mpi.txStore.allTxs[account1.String()].index.size())
ast.Equal(4, mpi.txStore.allTxs[account2.String()].index.size())
ast.Equal(uint64(1), mpi.txStore.nonceCache.getCommitNonce(account1.String()))
ast.Equal(uint64(0), mpi.txStore.nonceCache.getCommitNonce(account1.String()))
ast.Equal(uint64(3), mpi.txStore.nonceCache.getPendingNonce(account1.String()))
ast.Equal(uint64(1), mpi.txStore.nonceCache.getCommitNonce(account2.String()))
ast.Equal(uint64(0), mpi.txStore.nonceCache.getCommitNonce(account2.String()))
ast.Equal(uint64(3), mpi.txStore.nonceCache.getPendingNonce(account2.String()))
tx1 := constructTx(3, &privKey1)
@ -407,7 +407,7 @@ func TestRestore(t *testing.T) {
nonce := mpi.GetPendingNonceByAccount(account1.String())
ast.Equal(uint64(1), nonce)
privKey2 := genPrivKey()
account2, _ := privKey1.PublicKey().Address()
//account2, _ := privKey1.PublicKey().Address()
tx1 := constructTx(uint64(1), &privKey1)
tx2 := constructTx(uint64(2), &privKey1)
tx3 := constructTx(uint64(1), &privKey2)
@ -443,10 +443,10 @@ func TestRestore(t *testing.T) {
ast.Equal(1, mpi.txStore.parkingLotIndex.size())
// stop and restore
ast.Nil(mpi.txStore.nonceCache.fallback.Close())
newMpi, _ := mockMempoolImpl(storePath)
ast.Equal(uint64(3), newMpi.txStore.nonceCache.getCommitNonce(account1.String()))
ast.Equal(uint64(3), newMpi.txStore.nonceCache.getCommitNonce(account2.String()))
ast.Equal(uint64(3), newMpi.txStore.nonceCache.getPendingNonce(account1.String()))
ast.Equal(uint64(3), newMpi.txStore.nonceCache.getPendingNonce(account2.String()))
//ast.Nil(mpi.txStore.nonceCache.fallback.Close())
//newMpi, _ := mockMempoolImpl(storePath)
//ast.Equal(uint64(3), newMpi.txStore.nonceCache.getCommitNonce(account1.String()))
//ast.Equal(uint64(3), newMpi.txStore.nonceCache.getCommitNonce(account2.String()))
//ast.Equal(uint64(3), newMpi.txStore.nonceCache.getPendingNonce(account1.String()))
//ast.Equal(uint64(3), newMpi.txStore.nonceCache.getPendingNonce(account2.String()))
}

View File

@ -21,16 +21,21 @@ const (
DefaultTestTxSetSize = uint64(1)
)
func mockGetAccountNonce(address *types.Address) uint64 {
return 0
}
func mockMempoolImpl(path string) (*mempoolImpl, chan *raftproto.Ready) {
config := &Config{
ID: 1,
ChainHeight: DefaultTestChainHeight,
BatchSize: DefaultTestBatchSize,
PoolSize: DefaultPoolSize,
TxSliceSize: DefaultTestTxSetSize,
TxSliceTimeout: DefaultTxSetTick,
Logger: log.NewWithModule("consensus"),
StoragePath: path,
ID: 1,
ChainHeight: DefaultTestChainHeight,
BatchSize: DefaultTestBatchSize,
PoolSize: DefaultPoolSize,
TxSliceSize: DefaultTestTxSetSize,
TxSliceTimeout: DefaultTxSetTick,
Logger: log.NewWithModule("consensus"),
StoragePath: path,
GetAccountNonce: mockGetAccountNonce,
}
proposalC := make(chan *raftproto.Ready)
mempool, _ := newMempoolImpl(config)

View File

@ -1,16 +1,13 @@
package mempool
import (
"fmt"
"math"
"strconv"
"sync"
"github.com/sirupsen/logrus"
"github.com/google/btree"
"github.com/meshplus/bitxhub-kit/storage"
"github.com/meshplus/bitxhub-kit/types"
"github.com/meshplus/bitxhub-model/pb"
"github.com/sirupsen/logrus"
)
type transactionStore struct {
@ -35,7 +32,7 @@ type transactionStore struct {
priorityNonBatchSize uint64
}
func newTransactionStore(db storage.Storage, logger logrus.FieldLogger) *transactionStore {
func newTransactionStore(f GetAccountNonceFunc, logger logrus.FieldLogger) *transactionStore {
return &transactionStore{
txHashMap: make(map[string]*orderedIndexKey, 0),
allTxs: make(map[string]*txSortedMap),
@ -43,7 +40,7 @@ func newTransactionStore(db storage.Storage, logger logrus.FieldLogger) *transac
parkingLotIndex: newBtreeIndex(),
priorityIndex: newBtreeIndex(),
ttlIndex: newTxLiveTimeMap(),
nonceCache: newNonceCache(db, logger),
nonceCache: newNonceCache(f, logger),
}
}
@ -159,26 +156,27 @@ type nonceCache struct {
commitNonces map[string]uint64
// pendingNonces records each account's latest nonce which has been included in
// priority queue. Invariant: pendingNonces[account] >= commitNonces[account]
pendingNonces map[string]uint64
pendingMu sync.RWMutex
// falling back to reading from local database if an account is unknown.
fallback storage.Storage
logger logrus.FieldLogger
pendingNonces map[string]uint64
pendingMu sync.RWMutex
getAccountNonce GetAccountNonceFunc
logger logrus.FieldLogger
}
func newNonceCache(store storage.Storage, logger logrus.FieldLogger) *nonceCache {
func newNonceCache(f GetAccountNonceFunc, logger logrus.FieldLogger) *nonceCache {
return &nonceCache{
commitNonces: make(map[string]uint64),
pendingNonces: make(map[string]uint64),
fallback: store,
logger: logger,
commitNonces: make(map[string]uint64),
pendingNonces: make(map[string]uint64),
getAccountNonce: f,
logger: logger,
}
}
func (nc *nonceCache) getCommitNonce(account string) uint64 {
nonce, ok := nc.commitNonces[account]
if !ok {
return nc.getNonceFromDB(committedNonceKey(account))
cn := nc.getAccountNonce(types.NewAddressByStr(account))
nc.commitNonces[account] = cn
return cn
}
return nonce
}
@ -194,7 +192,7 @@ func (nc *nonceCache) getPendingNonce(account string) uint64 {
if !ok {
// if nonce is unknown, check if there is committed nonce persisted in db
// cause there aer no pending txs in mempool now, pending nonce is equal to committed nonce
return nc.getNonceFromDB(committedNonceKey(account))
return nc.getCommitNonce(account) + 1
}
return nonce
}
@ -212,31 +210,11 @@ func (nc *nonceCache) updatePendingNonce(newPending map[string]uint64) {
}
func (nc *nonceCache) updateCommittedNonce(newCommitted map[string]uint64) {
storageBatch := nc.fallback.NewBatch()
defer storageBatch.Commit()
for account, committedNonce := range newCommitted {
nc.setCommitNonce(account, committedNonce)
storageBatch.Put(committedNonceKey(account), []byte(strconv.FormatUint(committedNonce, 10)))
}
}
func (nc *nonceCache) getNonceFromDB(key []byte) uint64 {
var value []byte
if value = nc.fallback.Get(key); value == nil {
return 1
}
nonce, err := strconv.ParseUint(string(value), 10, 64)
if err != nil {
nc.logger.Errorf("Parse invalid nonce from db %s", err.Error())
return 1
}
return nonce
}
func committedNonceKey(account string) []byte {
return []byte(fmt.Sprintf("committed-%s", account))
}
// since the live time field in sortedTtlKey may vary during process
// we need to track the latest live time since its latest broadcast.
type txLiveTimeMap struct {

View File

@ -20,6 +20,8 @@ const (
DefaultTxSetTick = 100 * time.Millisecond
)
type GetAccountNonceFunc func(address *types.Address) uint64
type Config struct {
ID uint64
BatchSize uint64
@ -30,6 +32,7 @@ type Config struct {
ChainHeight uint64
Logger logrus.FieldLogger
StoragePath string // db for persist mem pool meta data
GetAccountNonce GetAccountNonceFunc
}
type txItem struct {

View File

@ -98,10 +98,11 @@ func NewNode(opts ...order.Option) (order.Order, error) {
batchTimeout, memConfig, err := generateSoloConfig(config.RepoRoot)
mempoolConf := &mempool.Config{
ID: config.ID,
ChainHeight: config.Applied,
Logger: config.Logger,
StoragePath: config.StoragePath,
ID: config.ID,
ChainHeight: config.Applied,
Logger: config.Logger,
StoragePath: config.StoragePath,
GetAccountNonce: config.GetAccountNonce,
BatchSize: memConfig.BatchSize,
PoolSize: memConfig.PoolSize,

View File

@ -54,6 +54,9 @@ func TestNode_Start(t *testing.T) {
order.WithID(1),
order.WithNodes(nodes),
order.WithApplied(1),
order.WithGetAccountNonceFunc(func(address *types.Address) uint64 {
return 0
}),
)
require.Nil(t, err)

View File

@ -5,6 +5,8 @@
package mock_peermgr
import (
reflect "reflect"
event "github.com/ethereum/go-ethereum/event"
gomock "github.com/golang/mock/gomock"
peer "github.com/libp2p/go-libp2p-core/peer"
@ -12,61 +14,44 @@ import (
events "github.com/meshplus/bitxhub/internal/model/events"
peermgr "github.com/meshplus/bitxhub/pkg/peermgr"
network "github.com/meshplus/go-lightp2p"
reflect "reflect"
)
// MockPeerManager is a mock of PeerManager interface
// MockPeerManager is a mock of PeerManager interface.
type MockPeerManager struct {
ctrl *gomock.Controller
recorder *MockPeerManagerMockRecorder
}
// MockPeerManagerMockRecorder is the mock recorder for MockPeerManager
// MockPeerManagerMockRecorder is the mock recorder for MockPeerManager.
type MockPeerManagerMockRecorder struct {
mock *MockPeerManager
}
// NewMockPeerManager creates a new mock instance
// NewMockPeerManager creates a new mock instance.
func NewMockPeerManager(ctrl *gomock.Controller) *MockPeerManager {
mock := &MockPeerManager{ctrl: ctrl}
mock.recorder = &MockPeerManagerMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockPeerManager) EXPECT() *MockPeerManagerMockRecorder {
return m.recorder
}
// Start mocks base method
func (m *MockPeerManager) Start() error {
// AddNode mocks base method.
func (m *MockPeerManager) AddNode(newNodeID uint64, vpInfo *pb.VpInfo) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Start")
ret0, _ := ret[0].(error)
return ret0
m.ctrl.Call(m, "AddNode", newNodeID, vpInfo)
}
// Start indicates an expected call of Start
func (mr *MockPeerManagerMockRecorder) Start() *gomock.Call {
// AddNode indicates an expected call of AddNode.
func (mr *MockPeerManagerMockRecorder) AddNode(newNodeID, vpInfo interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockPeerManager)(nil).Start))
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddNode", reflect.TypeOf((*MockPeerManager)(nil).AddNode), newNodeID, vpInfo)
}
// Stop mocks base method
func (m *MockPeerManager) Stop() error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Stop")
ret0, _ := ret[0].(error)
return ret0
}
// Stop indicates an expected call of Stop
func (mr *MockPeerManagerMockRecorder) Stop() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockPeerManager)(nil).Stop))
}
// AsyncSend mocks base method
// AsyncSend mocks base method.
func (m *MockPeerManager) AsyncSend(arg0 uint64, arg1 *pb.Message) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "AsyncSend", arg0, arg1)
@ -74,27 +59,107 @@ func (m *MockPeerManager) AsyncSend(arg0 uint64, arg1 *pb.Message) error {
return ret0
}
// AsyncSend indicates an expected call of AsyncSend
// AsyncSend indicates an expected call of AsyncSend.
func (mr *MockPeerManagerMockRecorder) AsyncSend(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AsyncSend", reflect.TypeOf((*MockPeerManager)(nil).AsyncSend), arg0, arg1)
}
// SendWithStream mocks base method
func (m *MockPeerManager) SendWithStream(arg0 network.Stream, arg1 *pb.Message) error {
// Broadcast mocks base method.
func (m *MockPeerManager) Broadcast(arg0 *pb.Message) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SendWithStream", arg0, arg1)
ret := m.ctrl.Call(m, "Broadcast", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// SendWithStream indicates an expected call of SendWithStream
func (mr *MockPeerManagerMockRecorder) SendWithStream(arg0, arg1 interface{}) *gomock.Call {
// Broadcast indicates an expected call of Broadcast.
func (mr *MockPeerManagerMockRecorder) Broadcast(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendWithStream", reflect.TypeOf((*MockPeerManager)(nil).SendWithStream), arg0, arg1)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Broadcast", reflect.TypeOf((*MockPeerManager)(nil).Broadcast), arg0)
}
// Send mocks base method
// CountConnectedPeers mocks base method.
func (m *MockPeerManager) CountConnectedPeers() uint64 {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CountConnectedPeers")
ret0, _ := ret[0].(uint64)
return ret0
}
// CountConnectedPeers indicates an expected call of CountConnectedPeers.
func (mr *MockPeerManagerMockRecorder) CountConnectedPeers() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CountConnectedPeers", reflect.TypeOf((*MockPeerManager)(nil).CountConnectedPeers))
}
// DelNode mocks base method.
func (m *MockPeerManager) DelNode(delID uint64) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "DelNode", delID)
}
// DelNode indicates an expected call of DelNode.
func (mr *MockPeerManagerMockRecorder) DelNode(delID interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DelNode", reflect.TypeOf((*MockPeerManager)(nil).DelNode), delID)
}
// Disconnect mocks base method.
func (m *MockPeerManager) Disconnect(vpInfos map[uint64]*pb.VpInfo) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "Disconnect", vpInfos)
}
// Disconnect indicates an expected call of Disconnect.
func (mr *MockPeerManagerMockRecorder) Disconnect(vpInfos interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Disconnect", reflect.TypeOf((*MockPeerManager)(nil).Disconnect), vpInfos)
}
// OtherPeers mocks base method.
func (m *MockPeerManager) OtherPeers() map[uint64]*peer.AddrInfo {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "OtherPeers")
ret0, _ := ret[0].(map[uint64]*peer.AddrInfo)
return ret0
}
// OtherPeers indicates an expected call of OtherPeers.
func (mr *MockPeerManagerMockRecorder) OtherPeers() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OtherPeers", reflect.TypeOf((*MockPeerManager)(nil).OtherPeers))
}
// Peers mocks base method.
func (m *MockPeerManager) Peers() map[uint64]*pb.VpInfo {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Peers")
ret0, _ := ret[0].(map[uint64]*pb.VpInfo)
return ret0
}
// Peers indicates an expected call of Peers.
func (mr *MockPeerManagerMockRecorder) Peers() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Peers", reflect.TypeOf((*MockPeerManager)(nil).Peers))
}
// PierManager mocks base method.
func (m *MockPeerManager) PierManager() peermgr.PierManager {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "PierManager")
ret0, _ := ret[0].(peermgr.PierManager)
return ret0
}
// PierManager indicates an expected call of PierManager.
func (mr *MockPeerManagerMockRecorder) PierManager() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PierManager", reflect.TypeOf((*MockPeerManager)(nil).PierManager))
}
// Send mocks base method.
func (m *MockPeerManager) Send(arg0 uint64, arg1 *pb.Message) (*pb.Message, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Send", arg0, arg1)
@ -103,69 +168,55 @@ func (m *MockPeerManager) Send(arg0 uint64, arg1 *pb.Message) (*pb.Message, erro
return ret0, ret1
}
// Send indicates an expected call of Send
// Send indicates an expected call of Send.
func (mr *MockPeerManagerMockRecorder) Send(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Send", reflect.TypeOf((*MockPeerManager)(nil).Send), arg0, arg1)
}
// Broadcast mocks base method
func (m *MockPeerManager) Broadcast(arg0 *pb.Message) error {
// SendWithStream mocks base method.
func (m *MockPeerManager) SendWithStream(arg0 network.Stream, arg1 *pb.Message) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Broadcast", arg0)
ret := m.ctrl.Call(m, "SendWithStream", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// Broadcast indicates an expected call of Broadcast
func (mr *MockPeerManagerMockRecorder) Broadcast(arg0 interface{}) *gomock.Call {
// SendWithStream indicates an expected call of SendWithStream.
func (mr *MockPeerManagerMockRecorder) SendWithStream(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Broadcast", reflect.TypeOf((*MockPeerManager)(nil).Broadcast), arg0)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendWithStream", reflect.TypeOf((*MockPeerManager)(nil).SendWithStream), arg0, arg1)
}
// CountConnectedPeers mocks base method
func (m *MockPeerManager) CountConnectedPeers() uint64 {
// Start mocks base method.
func (m *MockPeerManager) Start() error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CountConnectedPeers")
ret0, _ := ret[0].(uint64)
ret := m.ctrl.Call(m, "Start")
ret0, _ := ret[0].(error)
return ret0
}
// CountConnectedPeers indicates an expected call of CountConnectedPeers
func (mr *MockPeerManagerMockRecorder) CountConnectedPeers() *gomock.Call {
// Start indicates an expected call of Start.
func (mr *MockPeerManagerMockRecorder) Start() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CountConnectedPeers", reflect.TypeOf((*MockPeerManager)(nil).CountConnectedPeers))
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockPeerManager)(nil).Start))
}
// Peers mocks base method
func (m *MockPeerManager) Peers() map[uint64]*pb.VpInfo {
// Stop mocks base method.
func (m *MockPeerManager) Stop() error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Peers")
ret0, _ := ret[0].(map[uint64]*pb.VpInfo)
ret := m.ctrl.Call(m, "Stop")
ret0, _ := ret[0].(error)
return ret0
}
// Peers indicates an expected call of Peers
func (mr *MockPeerManagerMockRecorder) Peers() *gomock.Call {
// Stop indicates an expected call of Stop.
func (mr *MockPeerManagerMockRecorder) Stop() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Peers", reflect.TypeOf((*MockPeerManager)(nil).Peers))
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockPeerManager)(nil).Stop))
}
// OtherPeers mocks base method
func (m *MockPeerManager) OtherPeers() map[uint64]*peer.AddrInfo {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "OtherPeers")
ret0, _ := ret[0].(map[uint64]*peer.AddrInfo)
return ret0
}
// OtherPeers indicates an expected call of OtherPeers
func (mr *MockPeerManagerMockRecorder) OtherPeers() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OtherPeers", reflect.TypeOf((*MockPeerManager)(nil).OtherPeers))
}
// SubscribeOrderMessage mocks base method
// SubscribeOrderMessage mocks base method.
func (m *MockPeerManager) SubscribeOrderMessage(ch chan<- events.OrderMessageEvent) event.Subscription {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SubscribeOrderMessage", ch)
@ -173,37 +224,13 @@ func (m *MockPeerManager) SubscribeOrderMessage(ch chan<- events.OrderMessageEve
return ret0
}
// SubscribeOrderMessage indicates an expected call of SubscribeOrderMessage
// SubscribeOrderMessage indicates an expected call of SubscribeOrderMessage.
func (mr *MockPeerManagerMockRecorder) SubscribeOrderMessage(ch interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubscribeOrderMessage", reflect.TypeOf((*MockPeerManager)(nil).SubscribeOrderMessage), ch)
}
// AddNode mocks base method
func (m *MockPeerManager) AddNode(newNodeID uint64, vpInfo *pb.VpInfo) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "AddNode", newNodeID, vpInfo)
}
// AddNode indicates an expected call of AddNode
func (mr *MockPeerManagerMockRecorder) AddNode(newNodeID, vpInfo interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddNode", reflect.TypeOf((*MockPeerManager)(nil).AddNode), newNodeID, vpInfo)
}
// DelNode mocks base method
func (m *MockPeerManager) DelNode(delID uint64) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "DelNode", delID)
}
// DelNode indicates an expected call of DelNode
func (mr *MockPeerManagerMockRecorder) DelNode(delID interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DelNode", reflect.TypeOf((*MockPeerManager)(nil).DelNode), delID)
}
// UpdateRouter mocks base method
// UpdateRouter mocks base method.
func (m *MockPeerManager) UpdateRouter(vpInfos map[uint64]*pb.VpInfo, isNew bool) bool {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpdateRouter", vpInfos, isNew)
@ -211,76 +238,36 @@ func (m *MockPeerManager) UpdateRouter(vpInfos map[uint64]*pb.VpInfo, isNew bool
return ret0
}
// UpdateRouter indicates an expected call of UpdateRouter
// UpdateRouter indicates an expected call of UpdateRouter.
func (mr *MockPeerManagerMockRecorder) UpdateRouter(vpInfos, isNew interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateRouter", reflect.TypeOf((*MockPeerManager)(nil).UpdateRouter), vpInfos, isNew)
}
// Disconnect mocks base method
func (m *MockPeerManager) Disconnect(vpInfos map[uint64]*pb.VpInfo) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "Disconnect", vpInfos)
}
// Disconnect indicates an expected call of Disconnect
func (mr *MockPeerManagerMockRecorder) Disconnect(vpInfos interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Disconnect", reflect.TypeOf((*MockPeerManager)(nil).Disconnect), vpInfos)
}
// PierManager mocks base method
func (m *MockPeerManager) PierManager() peermgr.PierManager {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "PierManager")
ret0, _ := ret[0].(peermgr.PierManager)
return ret0
}
// PierManager indicates an expected call of PierManager
func (mr *MockPeerManagerMockRecorder) PierManager() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PierManager", reflect.TypeOf((*MockPeerManager)(nil).PierManager))
}
// MockPierManager is a mock of PierManager interface
// MockPierManager is a mock of PierManager interface.
type MockPierManager struct {
ctrl *gomock.Controller
recorder *MockPierManagerMockRecorder
}
// MockPierManagerMockRecorder is the mock recorder for MockPierManager
// MockPierManagerMockRecorder is the mock recorder for MockPierManager.
type MockPierManagerMockRecorder struct {
mock *MockPierManager
}
// NewMockPierManager creates a new mock instance
// NewMockPierManager creates a new mock instance.
func NewMockPierManager(ctrl *gomock.Controller) *MockPierManager {
mock := &MockPierManager{ctrl: ctrl}
mock.recorder = &MockPierManagerMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockPierManager) EXPECT() *MockPierManagerMockRecorder {
return m.recorder
}
// Piers mocks base method
func (m *MockPierManager) Piers() *peermgr.Piers {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Piers")
ret0, _ := ret[0].(*peermgr.Piers)
return ret0
}
// Piers indicates an expected call of Piers
func (mr *MockPierManagerMockRecorder) Piers() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Piers", reflect.TypeOf((*MockPierManager)(nil).Piers))
}
// AskPierMaster mocks base method
// AskPierMaster mocks base method.
func (m *MockPierManager) AskPierMaster(arg0 string) (bool, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "AskPierMaster", arg0)
@ -289,8 +276,22 @@ func (m *MockPierManager) AskPierMaster(arg0 string) (bool, error) {
return ret0, ret1
}
// AskPierMaster indicates an expected call of AskPierMaster
// AskPierMaster indicates an expected call of AskPierMaster.
func (mr *MockPierManagerMockRecorder) AskPierMaster(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AskPierMaster", reflect.TypeOf((*MockPierManager)(nil).AskPierMaster), arg0)
}
// Piers mocks base method.
func (m *MockPierManager) Piers() *peermgr.Piers {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Piers")
ret0, _ := ret[0].(*peermgr.Piers)
return ret0
}
// Piers indicates an expected call of Piers.
func (mr *MockPierManagerMockRecorder) Piers() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Piers", reflect.TypeOf((*MockPierManager)(nil).Piers))
}

View File

@ -35,7 +35,7 @@ func TestVerifyPool_CheckProof(t *testing.T) {
ID: from,
Name: "appchain A",
Validators: "",
ConsensusType: 0,
ConsensusType: "rbft",
ChainType: "fabric",
Desc: "",
Version: "",
@ -129,7 +129,7 @@ func TestVerifyPool_CheckProof2(t *testing.T) {
ID: from,
Name: "appchain" + from,
Validators: "",
ConsensusType: int32(1),
ConsensusType: "rbft",
ChainType: "fabric",
Desc: "",
Version: "",

View File

@ -27,6 +27,10 @@ func (b *BoltStubImpl) Callee() string {
return b.ctx.Callee.String()
}
func (b *BoltStubImpl) CurrentCaller() string {
return b.ctx.CurrentCaller.String()
}
func (b *BoltStubImpl) Logger() logrus.FieldLogger {
return b.ctx.Logger
}
@ -126,6 +130,7 @@ func (b *BoltStubImpl) CrossInvoke(address, method string, args ...*pb.Arg) *bol
ctx := &vm.Context{
Caller: b.bvm.ctx.Caller,
Callee: addr,
CurrentCaller: b.bvm.ctx.Callee,
Ledger: b.bvm.ctx.Ledger,
TransactionIndex: b.bvm.ctx.TransactionIndex,
TransactionHash: b.bvm.ctx.TransactionHash,

View File

@ -94,6 +94,11 @@ func (bvm *BoltVM) HandleIBTP(ibtp *pb.IBTP) (ret []byte, err error) {
ve: bvm.ve,
}
_, err = GetBoltContract(bvm.ctx.Callee.String(), bvm.contracts)
if err != nil {
return nil, fmt.Errorf("get bolt contract: %w", err)
}
res := con.HandleIBTP(ibtp)
if !res.Ok {
return nil, fmt.Errorf("call error: %s", res.Result)

View File

@ -7,23 +7,28 @@ import (
"time"
"github.com/golang/mock/gomock"
"github.com/meshplus/bitxhub-core/agency"
appchain_mgr "github.com/meshplus/bitxhub-core/appchain-mgr"
"github.com/meshplus/bitxhub-core/validator/mock_validator"
"github.com/meshplus/bitxhub-kit/log"
"github.com/meshplus/bitxhub/internal/ledger/mock_ledger"
"github.com/meshplus/bitxhub/internal/repo"
"github.com/meshplus/bitxhub/pkg/vm"
"github.com/meshplus/bitxhub-core/agency"
"github.com/meshplus/bitxhub-kit/types"
"github.com/meshplus/bitxhub-model/constant"
"github.com/meshplus/bitxhub-model/pb"
"github.com/meshplus/bitxhub/internal/executor/contracts"
"github.com/meshplus/bitxhub/internal/ledger/mock_ledger"
"github.com/meshplus/bitxhub/pkg/vm"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
const (
from = "0x3f9d18f7C3a6E5E4C0B877FE3E688aB08840b997"
to = "0x000018f7C3A6E5E4c0b877fe3E688ab08840b997"
from = "0x3f9d18f7C3a6E5E4C0B877FE3E688aB08840b997"
to = "0x000018f7C3A6E5E4c0b877fe3E688ab08840b997"
admin1 = "0xc7F999b83Af6DF9e67d0a37Ee7e900bF38b3D013"
admin2 = "0x79a1215469FaB6f9c63c1816b45183AD3624bE34"
admin3 = "0x97c8B516D19edBf575D72a172Af7F418BE498C37"
)
func GetBoltContracts() map[string]agency.Contract {
@ -128,23 +133,96 @@ func TestBoltVM_Run(t *testing.T) {
require.Nil(t, err)
proposals := make([][]byte, 0)
proposals = append(proposals, proposalData)
admins := make([]*repo.Admin, 0)
admins = append(admins, &repo.Admin{
Address: admin1,
Weight: uint64(1),
})
admins = append(admins, &repo.Admin{
Address: admin2,
Weight: uint64(1),
})
admins = append(admins, &repo.Admin{
Address: admin3,
Weight: uint64(1),
})
adminsData, err := json.Marshal(admins)
require.Nil(t, err)
chainRegisting := &appchain_mgr.Appchain{
Status: appchain_mgr.AppchainRegisting,
ID: from,
Name: "appchain A",
Validators: "",
ConsensusType: "rbft",
ChainType: "fabric",
Desc: "",
Version: "",
PublicKey: "11111",
}
chainRegistingData, err := json.Marshal(chainRegisting)
require.Nil(t, err)
chainAvailable := &appchain_mgr.Appchain{
Status: appchain_mgr.AppchainAvailable,
ID: from,
Name: "appchain A",
Validators: "",
ConsensusType: "rbft",
ChainType: "fabric",
Desc: "",
Version: "",
PublicKey: "11111",
}
chainAvailableData, err := json.Marshal(chainAvailable)
require.Nil(t, err)
interchain := &pb.Interchain{
ID: from,
InterchainCounter: make(map[string]uint64),
ReceiptCounter: make(map[string]uint64),
SourceReceiptCounter: make(map[string]uint64),
}
interchainData, err := interchain.Marshal()
require.Nil(t, err)
mockLedger.EXPECT().QueryByPrefix(gomock.Any(), contracts.PROPOSAL_PREFIX).Return(true, proposals).AnyTimes()
mockLedger.EXPECT().QueryByPrefix(gomock.Any(), appchain_mgr.PREFIX).Return(true, data).AnyTimes()
mockLedger.EXPECT().GetState(gomock.Any(), gomock.Any()).DoAndReturn(func(addr *types.Address, key []byte) (bool, []byte) {
switch addr.String() {
case constant.AppchainMgrContractAddr.String():
case constant.AppchainMgrContractAddr.Address().String():
return false, nil
case constant.InterchainContractAddr.String():
case constant.InterchainContractAddr.Address().String():
return false, nil
case constant.RoleContractAddr.Address().String():
return true, adminsData
}
return false, nil
}).Times(1)
mockLedger.EXPECT().GetState(gomock.Any(), gomock.Any()).DoAndReturn(func(addr *types.Address, key []byte) (bool, []byte) {
switch addr.String() {
case constant.AppchainMgrContractAddr.String():
return true, nil
case constant.InterchainContractAddr.String():
case constant.AppchainMgrContractAddr.Address().String():
return true, chainRegistingData
case constant.InterchainContractAddr.Address().String():
return false, nil
case constant.RoleContractAddr.Address().String():
return true, adminsData
case constant.GovernanceContractAddr.Address().String():
return true, nil
}
return true, nil
}).Times(5)
mockLedger.EXPECT().GetState(gomock.Any(), gomock.Any()).DoAndReturn(func(addr *types.Address, key []byte) (bool, []byte) {
switch addr.String() {
case constant.AppchainMgrContractAddr.Address().String():
return true, chainAvailableData
case constant.InterchainContractAddr.Address().String():
return true, interchainData
case constant.RoleContractAddr.Address().String():
return true, adminsData
case constant.GovernanceContractAddr.Address().String():
return true, nil
}
return true, nil
}).AnyTimes()
@ -160,6 +238,24 @@ func TestBoltVM_Run(t *testing.T) {
ctx := vm.NewContext(tx, 1, nil, mockLedger, log.NewWithModule("vm"))
boltVM := New(ctx, mockEngine, GetBoltContracts())
// create Governance boltVM
txGovernance := &pb.Transaction{
From: types.NewAddressByStr(constant.GovernanceContractAddr.String()),
To: constant.AppchainMgrContractAddr.Address(),
}
txGovernance.TransactionHash = txGovernance.Hash()
ctxGovernance := vm.NewContext(txGovernance, 1, nil, mockLedger, log.NewWithModule("vm"))
boltVMGovernance := New(ctxGovernance, mockEngine, GetBoltContracts())
// create Interchain boltVM
txInterchain := &pb.Transaction{
From: types.NewAddressByStr(from),
To: constant.InterchainContractAddr.Address(),
}
txInterchain.TransactionHash = txInterchain.Hash()
ctxInterchain := vm.NewContext(txInterchain, 1, nil, mockLedger, log.NewWithModule("vm"))
boltVMInterchain := New(ctxInterchain, mockEngine, GetBoltContracts())
ip := &pb.InvokePayload{
Method: "CountAppchains",
}
@ -197,8 +293,8 @@ func TestBoltVM_Run(t *testing.T) {
Value: []byte(from),
},
{
Type: pb.Arg_I32,
Value: []byte(strconv.Itoa(1)),
Type: pb.Arg_String,
Value: []byte("rbft"),
},
{
Type: pb.Arg_String,
@ -227,6 +323,28 @@ func TestBoltVM_Run(t *testing.T) {
ret, err = boltVM.Run(input)
require.Nil(t, err)
ip = &pb.InvokePayload{
Method: "Manager",
Args: []*pb.Arg{
{
Type: pb.Arg_String,
Value: []byte(appchain_mgr.EventRegister),
},
{
Type: pb.Arg_String,
Value: []byte(contracts.APPOVED),
},
{
Type: pb.Arg_Bytes,
Value: chainRegistingData,
},
},
}
input, err = ip.Marshal()
require.Nil(t, err)
ret, err = boltVMGovernance.Run(input)
require.Nil(t, err, string(ret))
ip = &pb.InvokePayload{
Method: "DeleteAppchain",
Args: []*pb.Arg{
@ -244,7 +362,7 @@ func TestBoltVM_Run(t *testing.T) {
require.Contains(t, err.Error(), "caller is not an admin account")
ibtp := mockIBTP(t, 1, pb.IBTP_INTERCHAIN)
_, err = boltVM.HandleIBTP(ibtp)
_, err = boltVMInterchain.HandleIBTP(ibtp)
require.Nil(t, err)
}

View File

@ -11,6 +11,7 @@ import (
type Context struct {
Caller *types.Address
Callee *types.Address
CurrentCaller *types.Address
Ledger ledger.Ledger
TransactionIndex uint64
TransactionHash *types.Hash
@ -24,6 +25,7 @@ func NewContext(tx *pb.Transaction, txIndex uint64, data *pb.TransactionData, le
return &Context{
Caller: tx.From,
Callee: tx.To,
CurrentCaller: tx.From,
Ledger: ledger,
TransactionIndex: txIndex,
TransactionHash: tx.TransactionHash,

BIN
pkg/vm/wasm/testdata/ledger_test_gc.wasm vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,21 @@
package vmledger
import (
"github.com/wasmerio/go-ext-wasm/wasmer"
)
type Imports struct {
imports *wasmer.Imports
}
func New() (*wasmer.Imports, error) {
imports := &Imports{
imports: wasmer.NewImports(),
}
err := imports.importLedger()
if err != nil {
return nil, err
}
return imports.imports, nil
}

View File

@ -0,0 +1,115 @@
package vmledger
// #include <stdlib.h>
//
// extern long long get_balance(void *context);
// extern void set_balance(void *context, long long value);
// extern int32_t get_state(void *context, long long key_ptr);
// extern void set_state(void *context, long long key_ptr, long long value_ptr);
// extern void add_state(void *context, long long key_ptr, long long value_ptr);
import "C"
import (
"unsafe"
"github.com/meshplus/bitxhub/internal/ledger"
"github.com/wasmerio/go-ext-wasm/wasmer"
)
//export get_balance
func get_balance(context unsafe.Pointer) int64 {
ctx := wasmer.IntoInstanceContext(context)
ctxMap := ctx.Data().(map[string]interface{})
account := ctxMap["account"].(*ledger.Account)
return int64(account.GetBalance())
}
//export set_balance
func set_balance(context unsafe.Pointer, value int64) {
ctx := wasmer.IntoInstanceContext(context)
ctxMap := ctx.Data().(map[string]interface{})
account := ctxMap["account"].(*ledger.Account)
account.SetBalance(uint64(value))
}
//export get_state
func get_state(context unsafe.Pointer, key_ptr int64) int32 {
ctx := wasmer.IntoInstanceContext(context)
ctxMap := ctx.Data().(map[string]interface{})
data := ctxMap["argmap"].(map[int]int)
alloc := ctxMap["allocate"].(func(...interface{}) (wasmer.Value, error))
account := ctxMap["account"].(*ledger.Account)
memory := ctx.Memory()
key := memory.Data()[key_ptr : key_ptr+int64(data[int(key_ptr)])]
ok, value := account.GetState(key)
if !ok {
return -1
}
lengthOfBytes := len(value)
allocResult, err := alloc(lengthOfBytes)
if err != nil {
return -1
}
inputPointer := allocResult.ToI32()
mem := memory.Data()[inputPointer:]
var i int
for i = 0; i < lengthOfBytes; i++ {
mem[i] = value[i]
}
mem[i] = 0
data[int(inputPointer)] = len(value)
return inputPointer
}
//export set_state
func set_state(context unsafe.Pointer, key_ptr int64, value_ptr int64) {
ctx := wasmer.IntoInstanceContext(context)
ctxMap := ctx.Data().(map[string]interface{})
data := ctxMap["argmap"].(map[int]int)
account := ctxMap["account"].(*ledger.Account)
memory := ctx.Memory()
key := memory.Data()[key_ptr : key_ptr+int64(data[int(key_ptr)])]
value := memory.Data()[value_ptr : value_ptr+int64(data[int(value_ptr)])]
account.SetState(key, value)
}
//export add_state
func add_state(context unsafe.Pointer, key_ptr int64, value_ptr int64) {
ctx := wasmer.IntoInstanceContext(context)
ctxMap := ctx.Data().(map[string]interface{})
data := ctxMap["argmap"].(map[int]int)
account := ctxMap["account"].(*ledger.Account)
memory := ctx.Memory()
key := memory.Data()[key_ptr : key_ptr+int64(data[int(key_ptr)])]
value := memory.Data()[value_ptr : value_ptr+int64(data[int(value_ptr)])]
account.AddState(key, value)
}
func (im *Imports) importLedger() error {
var err error
im.imports, err = im.imports.Append("get_balance", get_balance, C.get_balance)
if err != nil {
return err
}
im.imports, err = im.imports.Append("set_balance", set_balance, C.set_balance)
if err != nil {
return err
}
im.imports, err = im.imports.Append("get_state", get_state, C.get_state)
if err != nil {
return err
}
im.imports, err = im.imports.Append("set_state", set_state, C.set_state)
if err != nil {
return err
}
im.imports, err = im.imports.Append("add_state", add_state, C.add_state)
if err != nil {
return err
}
return nil
}

View File

@ -60,6 +60,13 @@ func New(ctx *vm.Context, imports *wasmer.Imports, instances map[string]wasmer.I
return nil, err
}
w.SetContext(wasm.ACCOUNT, ctx.Ledger.GetOrCreateAccount(ctx.Callee))
_, ok := w.Instance.Exports["allocate"]
if !ok {
return nil, fmt.Errorf("no allocate method")
}
w.SetContext(wasm.ALLOC_MEM, w.Instance.Exports["allocate"])
wasmVM.w = w
return wasmVM, nil

View File

@ -19,6 +19,7 @@ import (
"github.com/meshplus/bitxhub/internal/ledger"
"github.com/meshplus/bitxhub/internal/repo"
"github.com/meshplus/bitxhub/pkg/vm"
"github.com/meshplus/bitxhub/pkg/vm/wasm/vmledger"
libp2pcert "github.com/meshplus/go-libp2p-cert"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -45,7 +46,7 @@ func initCreateContext(t *testing.T, name string) *vm.Context {
assert.Nil(t, err)
dir := filepath.Join(os.TempDir(), "wasm", name)
bytes, err := ioutil.ReadFile("./testdata/wasm_test.wasm")
bytes, err := ioutil.ReadFile("./testdata/ledger_test_gc.wasm")
assert.Nil(t, err)
data := &pb.TransactionData{
@ -171,10 +172,10 @@ func TestExecute(t *testing.T) {
require.Nil(t, err)
invokePayload := &pb.InvokePayload{
Method: "a",
Method: "state_test_set",
Args: []*pb.Arg{
{Type: pb.Arg_I32, Value: []byte(fmt.Sprintf("%d", 1))},
{Type: pb.Arg_I32, Value: []byte(fmt.Sprintf("%d", 2))},
{Type: pb.Arg_Bytes, Value: []byte("alice")},
{Type: pb.Arg_Bytes, Value: []byte("111")},
},
}
payload, err := invokePayload.Marshal()
@ -188,14 +189,39 @@ func TestExecute(t *testing.T) {
TransactionData: data,
Ledger: ctx.Ledger,
}
imports1, err := validatorlib.New()
imports1, err := vmledger.New()
require.Nil(t, err)
fmt.Println(imports1)
wasm1, err := New(ctx1, imports1, instances)
require.Nil(t, err)
fmt.Println(wasm1.w.Instance.Exports)
result, err := wasm1.Run(payload)
require.Nil(t, err)
require.Equal(t, "336", string(result))
require.Equal(t, "1", string(result))
invokePayload1 := &pb.InvokePayload{
Method: "state_test_get",
Args: []*pb.Arg{
{Type: pb.Arg_Bytes, Value: []byte("alice")},
{Type: pb.Arg_Bytes, Value: []byte("111")},
},
}
payload1, err := invokePayload1.Marshal()
require.Nil(t, err)
// data1 := &pb.TransactionData{
// Payload: payload1,
// }
// ctx2 := &vm.Context{
// Caller: ctx.Caller,
// Callee: types.NewAddress(ret),
// TransactionData: data1,
// Ledger: ctx.Ledger,
// }
result1, err := wasm1.Run(payload1)
require.Nil(t, err)
require.Equal(t, "1", string(result1))
}
func TestWasm_RunFabValidation(t *testing.T) {
@ -352,7 +378,7 @@ func TestWasm_RunWithoutMethod(t *testing.T) {
TransactionData: data,
Ledger: ctx.Ledger,
}
imports1, err := validatorlib.New()
imports1, err := vmledger.New()
require.Nil(t, err)
wasm1, err := New(ctx1, imports1, instances)
require.Nil(t, err)

View File

@ -24,3 +24,48 @@ fi
if ! type mockgen >/dev/null 2>&1; then
go get github.com/golang/mock/mockgen
fi
function Get_PM_Name()
{
PM=''
if [ "$(uname)" == "Darwin" ]; then
DISTRO='MacOS'
PM='brew'
elif grep -Eqii "CentOS" /etc/issue || grep -Eq "CentOS" /etc/*-release; then
DISTRO='CentOS'
PM='yum'
elif grep -Eqi "Red Hat Enterprise Linux Server" /etc/issue || grep -Eq "Red Hat Enterprise Linux Server" /etc/*-release; then
DISTRO='RHEL'
PM='yum'
elif grep -Eqi "Aliyun" /etc/issue || grep -Eq "Aliyun" /etc/*-release; then
DISTRO='Aliyun'
PM='yum'
elif grep -Eqi "Fedora" /etc/issue || grep -Eq "Fedora" /etc/*-release; then
DISTRO='Fedora'
PM='yum'
elif grep -Eqi "Debian" /etc/issue || grep -Eq "Debian" /etc/*-release; then
DISTRO='Debian'
PM='apt-get'
elif grep -Eqi "Ubuntu" /etc/issue || grep -Eq "Ubuntu" /etc/*-release; then
DISTRO='Ubuntu'
PM='apt-get'
elif grep -Eqi "Raspbian" /etc/issue || grep -Eq "Raspbian" /etc/*-release; then
DISTRO='Raspbian'
PM='apt-get'
else
DISTRO='unknow'
fi
print_blue "Your OS distribution is detected as: "$DISTRO;
eval "$1=$PM"
}
print_blue "===> 4. Install tmux with package manager"
PM_NAME=''
Get_PM_Name PM_NAME
if [ -n "$PM_NAME" ]; then
if [ "$PM_NAME" == "brew" ]; then
$PM_NAME install tmux
else
sudo $PM_NAME install -y tmux
fi
fi

31
scripts/release_binary.sh Normal file
View File

@ -0,0 +1,31 @@
#!/usr/bin/env bash
source x.sh
CURRENT_PATH=$(pwd)
PROJECT_PATH=$(dirname "${CURRENT_PATH}")
BUILD_PATH=${PROJECT_PATH}/build
APP_VERSION=${1:-'1.6.0'}
print_blue "===> 1. Install packr"
if ! type packr >/dev/null 2>&1; then
go get -u github.com/gobuffalo/packr/packr
fi
print_blue "===> 2. build bitxhub"
cd "${PROJECT_PATH}" && make build
print_blue "===> 3. build plugins: raft, solo"
cd "${PROJECT_PATH}"/internal/plugins && make plugins
print_blue "===> 4. pack binarys"
cd "${PROJECT_PATH}"
cp ./bin/bitxhub ./build/bitxhub
cp ./internal/plugins/build/*.so ./build/
if [ "$(uname)" == "Darwin" ]; then
cd "${BUILD_PATH}" && tar zcvf bitxhub_v"${APP_VERSION}"_Darwin_x86_64.tar.gz ./bitxhub ./raft.so ./solo.so ./libwasmer.dylib
mv ./*.tar.gz ../dist/
else
cd "${BUILD_PATH}" && tar zcvf bitxhub_v"${APP_VERSION}"_Linux_x86_64.tar.gz ./bitxhub ./*.so
mv ./*.tar.gz ../dist/
fi

View File

@ -43,7 +43,7 @@ func (suite *RegisterAppchain) TestRegisterAppchain() {
args := []*pb.Arg{
pb.String("validators"),
pb.Int32(0),
pb.String(""),
pb.String("hyperchain"),
pb.String("税务链"),
pb.String("趣链税务链"),
@ -82,7 +82,7 @@ func (suite *RegisterAppchain) TestFetchAppchains() {
args := []*pb.Arg{
pb.String(""),
pb.Int32(0),
pb.String(""),
pb.String("hyperchain"),
pb.String("税务链"),
pb.String("趣链税务链"),
@ -97,7 +97,7 @@ func (suite *RegisterAppchain) TestFetchAppchains() {
args = []*pb.Arg{
pb.String(""),
pb.Int32(0),
pb.String(""),
pb.String("fabric"),
pb.String("政务链"),
pb.String("fabric政务"),
@ -160,7 +160,7 @@ func (suite *RegisterAppchain) TestGetPubKeyByChainID() {
args := []*pb.Arg{
pb.String(""),
pb.Int32(0),
pb.String(""),
pb.String("hyperchain"),
pb.String("税务链"),
pb.String("趣链税务链"),
@ -174,7 +174,7 @@ func (suite *RegisterAppchain) TestGetPubKeyByChainID() {
args = []*pb.Arg{
pb.String(""),
pb.Int32(0),
pb.String(""),
pb.String("fabric"),
pb.String("政务链"),
pb.String("fabric政务"),
@ -212,7 +212,7 @@ func (suite *RegisterAppchain) TestUpdateAppchains() {
args := []*pb.Arg{
pb.String(""),
pb.Int32(0),
pb.String(""),
pb.String("hyperchain"),
pb.String("税务链"),
pb.String("趣链税务链"),
@ -235,7 +235,7 @@ func (suite *RegisterAppchain) TestUpdateAppchains() {
args = []*pb.Arg{
pb.String(""),
pb.Int32(0),
pb.String(""),
pb.String("hyperchain"),
pb.String("管理链"),
pb.String("趣链管理链"),
@ -250,7 +250,7 @@ func (suite *RegisterAppchain) TestUpdateAppchains() {
//UpdateAppchain
args = []*pb.Arg{
pb.String(""),
pb.Int32(0),
pb.String(""),
pb.String("hyperchain"),
pb.String("税务链"),
pb.String("趣链税务链"),

View File

@ -3,20 +3,17 @@ package tester
import (
"crypto/sha256"
"io/ioutil"
"path/filepath"
"time"
"github.com/meshplus/bitxhub/internal/executor/contracts"
"github.com/tidwall/gjson"
appchain_mgr "github.com/meshplus/bitxhub-core/appchain-mgr"
"github.com/meshplus/bitxhub-kit/crypto"
"github.com/meshplus/bitxhub-kit/crypto/asym"
"github.com/meshplus/bitxhub-model/constant"
"github.com/meshplus/bitxhub-model/pb"
"github.com/meshplus/bitxhub/internal/coreapi/api"
"github.com/meshplus/bitxhub/internal/executor/contracts"
"github.com/stretchr/testify/suite"
"github.com/tidwall/gjson"
)
type Interchain struct {
@ -28,6 +25,28 @@ func (suite *Interchain) SetupSuite() {
}
func (suite *Interchain) TestHandleIBTP() {
path1 := "./test_data/config/node1/key.json"
path2 := "./test_data/config/node2/key.json"
path3 := "./test_data/config/node3/key.json"
keyPath1 := filepath.Join(path1)
keyPath2 := filepath.Join(path2)
keyPath3 := filepath.Join(path3)
priAdmin1, err := asym.RestorePrivateKey(keyPath1, "bitxhub")
suite.Require().Nil(err)
priAdmin2, err := asym.RestorePrivateKey(keyPath2, "bitxhub")
suite.Require().Nil(err)
priAdmin3, err := asym.RestorePrivateKey(keyPath3, "bitxhub")
suite.Require().Nil(err)
fromAdmin1, err := priAdmin1.PublicKey().Address()
suite.Require().Nil(err)
fromAdmin2, err := priAdmin2.PublicKey().Address()
suite.Require().Nil(err)
fromAdmin3, err := priAdmin3.PublicKey().Address()
suite.Require().Nil(err)
adminNonce1 := suite.api.Broker().GetPendingNonceByAccount(fromAdmin1.String())
adminNonce2 := suite.api.Broker().GetPendingNonceByAccount(fromAdmin2.String())
adminNonce3 := suite.api.Broker().GetPendingNonceByAccount(fromAdmin3.String())
k1, err := asym.GenerateKeyPair(crypto.Secp256k1)
suite.Require().Nil(err)
k2, err := asym.GenerateKeyPair(crypto.Secp256k1)
@ -47,7 +66,7 @@ func (suite *Interchain) TestHandleIBTP() {
ret, err := invokeBVMContract(suite.api, k1, k1Nonce, constant.AppchainMgrContractAddr.Address(), "Register",
pb.String(""),
pb.Int32(0),
pb.String("rbft"),
pb.String("hyperchain"),
pb.String("婚姻链"),
pb.String("趣链婚姻链"),
@ -58,24 +77,43 @@ func (suite *Interchain) TestHandleIBTP() {
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
k1Nonce++
id1 := gjson.Get(string(ret.Ret), "chain_id").String()
proposalId1 := gjson.Get(string(ret.Ret), "proposal_id").String()
ret, err = invokeBVMContract(suite.api, k1, k1Nonce, constant.AppchainMgrContractAddr.Address(), "GetAppchain", pb.String(id1))
suite.Require().Nil(err)
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
k1Nonce++
ret, err = invokeBVMContract(suite.api, k1, k1Nonce, constant.AppchainMgrContractAddr.Address(), "Manager",
pb.String(string(appchain_mgr.EventRegister)),
ret, err = invokeBVMContract(suite.api, priAdmin1, adminNonce1, constant.GovernanceContractAddr.Address(), "Vote",
pb.String(proposalId1),
pb.String(string(contracts.APPOVED)),
pb.Bytes(ret.Ret),
pb.String("reason"),
)
suite.Require().Nil(err)
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
k1Nonce++
adminNonce1++
ret, err = invokeBVMContract(suite.api, priAdmin2, adminNonce2, constant.GovernanceContractAddr.Address(), "Vote",
pb.String(proposalId1),
pb.String(string(contracts.APPOVED)),
pb.String("reason"),
)
suite.Require().Nil(err)
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
adminNonce2++
ret, err = invokeBVMContract(suite.api, priAdmin3, adminNonce3, constant.GovernanceContractAddr.Address(), "Vote",
pb.String(proposalId1),
pb.String(string(contracts.APPOVED)),
pb.String("reason"),
)
suite.Require().Nil(err)
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
adminNonce3++
ret, err = invokeBVMContract(suite.api, k2, k2Nonce, constant.AppchainMgrContractAddr.Address(), "Register",
pb.String(""),
pb.Int32(0),
pb.String("rbft"),
pb.String("fabric"),
pb.String("税务链"),
pb.String("fabric婚姻链"),
@ -86,20 +124,39 @@ func (suite *Interchain) TestHandleIBTP() {
suite.Require().True(ret.IsSuccess())
k2Nonce++
id2 := gjson.Get(string(ret.Ret), "chain_id").String()
proposalId2 := gjson.Get(string(ret.Ret), "proposal_id").String()
ret, err = invokeBVMContract(suite.api, k2, k2Nonce, constant.AppchainMgrContractAddr.Address(), "GetAppchain", pb.String(id2))
suite.Require().Nil(err)
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
k2Nonce++
ret, err = invokeBVMContract(suite.api, k2, k2Nonce, constant.AppchainMgrContractAddr.Address(), "Manager",
pb.String(string(appchain_mgr.EventRegister)),
ret, err = invokeBVMContract(suite.api, priAdmin1, adminNonce1, constant.GovernanceContractAddr.Address(), "Vote",
pb.String(proposalId2),
pb.String(string(contracts.APPOVED)),
pb.Bytes(ret.Ret),
pb.String("reason"),
)
suite.Require().Nil(err)
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
k2Nonce++
adminNonce1++
ret, err = invokeBVMContract(suite.api, priAdmin2, adminNonce2, constant.GovernanceContractAddr.Address(), "Vote",
pb.String(proposalId2),
pb.String(string(contracts.APPOVED)),
pb.String("reason"),
)
suite.Require().Nil(err)
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
adminNonce2++
ret, err = invokeBVMContract(suite.api, priAdmin3, adminNonce3, constant.GovernanceContractAddr.Address(), "Vote",
pb.String(proposalId2),
pb.String(string(contracts.APPOVED)),
pb.String("reason"),
)
suite.Require().Nil(err)
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
adminNonce3++
// deploy rule
bytes, err := ioutil.ReadFile("./test_data/hpc_rule.wasm")
@ -117,8 +174,9 @@ func (suite *Interchain) TestHandleIBTP() {
proof := []byte("true")
proofHash := sha256.Sum256(proof)
ib := &pb.IBTP{From: f.String(), To: t.String(), Index: ibtpNonce, Timestamp: time.Now().UnixNano(), Proof: proofHash[:]}
tx, err := genIBTPTransaction(k1, ib)
tx, err := genIBTPTransaction(k1, ib, k1Nonce)
suite.Require().Nil(err)
k1Nonce++
tx.Extra = proof
ret, err = sendTransactionWithReceipt(suite.api, tx)
@ -128,6 +186,28 @@ func (suite *Interchain) TestHandleIBTP() {
}
func (suite *Interchain) TestGetIBTPByID() {
path1 := "./test_data/config/node1/key.json"
path2 := "./test_data/config/node2/key.json"
path3 := "./test_data/config/node3/key.json"
keyPath1 := filepath.Join(path1)
keyPath2 := filepath.Join(path2)
keyPath3 := filepath.Join(path3)
priAdmin1, err := asym.RestorePrivateKey(keyPath1, "bitxhub")
suite.Require().Nil(err)
priAdmin2, err := asym.RestorePrivateKey(keyPath2, "bitxhub")
suite.Require().Nil(err)
priAdmin3, err := asym.RestorePrivateKey(keyPath3, "bitxhub")
suite.Require().Nil(err)
fromAdmin1, err := priAdmin1.PublicKey().Address()
suite.Require().Nil(err)
fromAdmin2, err := priAdmin2.PublicKey().Address()
suite.Require().Nil(err)
fromAdmin3, err := priAdmin3.PublicKey().Address()
suite.Require().Nil(err)
adminNonce1 := suite.api.Broker().GetPendingNonceByAccount(fromAdmin1.String())
adminNonce2 := suite.api.Broker().GetPendingNonceByAccount(fromAdmin2.String())
adminNonce3 := suite.api.Broker().GetPendingNonceByAccount(fromAdmin3.String())
k1, err := asym.GenerateKeyPair(crypto.Secp256k1)
suite.Require().Nil(err)
k2, err := asym.GenerateKeyPair(crypto.Secp256k1)
@ -150,7 +230,7 @@ func (suite *Interchain) TestGetIBTPByID() {
ret, err := invokeBVMContract(suite.api, k1, k1Nonce, constant.AppchainMgrContractAddr.Address(), "Register",
pb.String(string(confByte)),
pb.Int32(0),
pb.String("rbft"),
pb.String("hyperchain"),
pb.String("婚姻链"),
pb.String("趣链婚姻链"),
@ -161,24 +241,43 @@ func (suite *Interchain) TestGetIBTPByID() {
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
k1Nonce++
id1 := gjson.Get(string(ret.Ret), "chain_id").String()
proposalId1 := gjson.Get(string(ret.Ret), "proposal_id").String()
ret, err = invokeBVMContract(suite.api, k1, k1Nonce, constant.AppchainMgrContractAddr.Address(), "GetAppchain", pb.String(id1))
suite.Require().Nil(err)
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
k1Nonce++
ret, err = invokeBVMContract(suite.api, k1, k1Nonce, constant.AppchainMgrContractAddr.Address(), "Manager",
pb.String(string(appchain_mgr.EventRegister)),
ret, err = invokeBVMContract(suite.api, priAdmin1, adminNonce1, constant.GovernanceContractAddr.Address(), "Vote",
pb.String(proposalId1),
pb.String(string(contracts.APPOVED)),
pb.Bytes(ret.Ret),
pb.String("reason"),
)
suite.Require().Nil(err)
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
k1Nonce++
adminNonce1++
ret, err = invokeBVMContract(suite.api, priAdmin2, adminNonce2, constant.GovernanceContractAddr.Address(), "Vote",
pb.String(proposalId1),
pb.String(string(contracts.APPOVED)),
pb.String("reason"),
)
suite.Require().Nil(err)
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
adminNonce2++
ret, err = invokeBVMContract(suite.api, priAdmin3, adminNonce3, constant.GovernanceContractAddr.Address(), "Vote",
pb.String(proposalId1),
pb.String(string(contracts.APPOVED)),
pb.String("reason"),
)
suite.Require().Nil(err)
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
adminNonce3++
ret, err = invokeBVMContract(suite.api, k2, k2Nonce, constant.AppchainMgrContractAddr.Address(), "Register",
pb.String(""),
pb.Int32(0),
pb.String("rbft"),
pb.String("fabric"),
pb.String("税务链"),
pb.String("fabric税务链"),
@ -189,20 +288,39 @@ func (suite *Interchain) TestGetIBTPByID() {
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
k2Nonce++
id2 := gjson.Get(string(ret.Ret), "chain_id").String()
proposalId2 := gjson.Get(string(ret.Ret), "proposal_id").String()
ret, err = invokeBVMContract(suite.api, k2, k2Nonce, constant.AppchainMgrContractAddr.Address(), "GetAppchain", pb.String(id2))
suite.Require().Nil(err)
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
k2Nonce++
ret, err = invokeBVMContract(suite.api, k2, k2Nonce, constant.AppchainMgrContractAddr.Address(), "Manager",
pb.String(string(appchain_mgr.EventRegister)),
ret, err = invokeBVMContract(suite.api, priAdmin1, adminNonce1, constant.GovernanceContractAddr.Address(), "Vote",
pb.String(proposalId2),
pb.String(string(contracts.APPOVED)),
pb.Bytes(ret.Ret),
pb.String("reason"),
)
suite.Require().Nil(err)
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
k2Nonce++
adminNonce1++
ret, err = invokeBVMContract(suite.api, priAdmin2, adminNonce2, constant.GovernanceContractAddr.Address(), "Vote",
pb.String(proposalId2),
pb.String(string(contracts.APPOVED)),
pb.String("reason"),
)
suite.Require().Nil(err)
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
adminNonce2++
ret, err = invokeBVMContract(suite.api, priAdmin3, adminNonce3, constant.GovernanceContractAddr.Address(), "Vote",
pb.String(proposalId2),
pb.String(string(contracts.APPOVED)),
pb.String("reason"),
)
suite.Require().Nil(err)
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
adminNonce3++
contractByte, err := ioutil.ReadFile("./test_data/fabric_policy.wasm")
suite.Require().Nil(err)
@ -220,30 +338,33 @@ func (suite *Interchain) TestGetIBTPByID() {
proofHash := sha256.Sum256(proof)
ib := &pb.IBTP{From: f.String(), To: t.String(), Index: ibtpNonce, Payload: []byte("111"), Timestamp: time.Now().UnixNano(), Proof: proofHash[:]}
tx, err := genIBTPTransaction(k1, ib)
tx, err := genIBTPTransaction(k1, ib, k1Nonce)
suite.Require().Nil(err)
tx.Extra = proof
receipt, err := sendTransactionWithReceipt(suite.api, tx)
suite.Require().Nil(err)
suite.Require().EqualValues(true, receipt.IsSuccess(), string(receipt.Ret))
ibtpNonce++
k1Nonce++
ib2 := &pb.IBTP{From: f.String(), To: t.String(), Index: ibtpNonce, Payload: []byte("111"), Timestamp: time.Now().UnixNano(), Proof: proofHash[:]}
tx, err = genIBTPTransaction(k1, ib2)
tx, err = genIBTPTransaction(k1, ib2, k1Nonce)
suite.Require().Nil(err)
tx.Extra = proof
receipt, err = sendTransactionWithReceipt(suite.api, tx)
suite.Require().Nil(err)
suite.Require().EqualValues(true, receipt.IsSuccess(), string(receipt.Ret))
ibtpNonce++
k1Nonce++
ib3 := &pb.IBTP{From: f.String(), To: t.String(), Index: ibtpNonce, Payload: []byte("111"), Timestamp: time.Now().UnixNano(), Proof: proofHash[:]}
tx, err = genIBTPTransaction(k1, ib3)
tx, err = genIBTPTransaction(k1, ib3, k1Nonce)
suite.Require().Nil(err)
tx.Extra = proof
receipt, err = sendTransactionWithReceipt(suite.api, tx)
suite.Assert().Nil(err)
ibtpNonce++
k1Nonce++
ib.Index = 2
ret, err = invokeBVMContract(suite.api, k1, k1Nonce, constant.InterchainContractAddr.Address(), "GetIBTPByID", pb.String(ib.ID()))
@ -253,6 +374,28 @@ func (suite *Interchain) TestGetIBTPByID() {
}
func (suite *Interchain) TestInterchain() {
path1 := "./test_data/config/node1/key.json"
path2 := "./test_data/config/node2/key.json"
path3 := "./test_data/config/node3/key.json"
keyPath1 := filepath.Join(path1)
keyPath2 := filepath.Join(path2)
keyPath3 := filepath.Join(path3)
priAdmin1, err := asym.RestorePrivateKey(keyPath1, "bitxhub")
suite.Require().Nil(err)
priAdmin2, err := asym.RestorePrivateKey(keyPath2, "bitxhub")
suite.Require().Nil(err)
priAdmin3, err := asym.RestorePrivateKey(keyPath3, "bitxhub")
suite.Require().Nil(err)
fromAdmin1, err := priAdmin1.PublicKey().Address()
suite.Require().Nil(err)
fromAdmin2, err := priAdmin2.PublicKey().Address()
suite.Require().Nil(err)
fromAdmin3, err := priAdmin3.PublicKey().Address()
suite.Require().Nil(err)
adminNonce1 := suite.api.Broker().GetPendingNonceByAccount(fromAdmin1.String())
adminNonce2 := suite.api.Broker().GetPendingNonceByAccount(fromAdmin2.String())
adminNonce3 := suite.api.Broker().GetPendingNonceByAccount(fromAdmin3.String())
k1, err := asym.GenerateKeyPair(crypto.Secp256k1)
suite.Require().Nil(err)
k1Nonce := uint64(1)
@ -262,7 +405,7 @@ func (suite *Interchain) TestInterchain() {
ret, err := invokeBVMContract(suite.api, k1, k1Nonce, constant.AppchainMgrContractAddr.Address(), "Register",
pb.String(""),
pb.Int32(0),
pb.String("rbft"),
pb.String("hyperchain"),
pb.String("婚姻链"),
pb.String("趣链婚姻链"),
@ -273,20 +416,39 @@ func (suite *Interchain) TestInterchain() {
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
k1Nonce++
id1 := gjson.Get(string(ret.Ret), "chain_id").String()
proposalId1 := gjson.Get(string(ret.Ret), "proposal_id").String()
ret, err = invokeBVMContract(suite.api, k1, k1Nonce, constant.AppchainMgrContractAddr.Address(), "GetAppchain", pb.String(id1))
suite.Require().Nil(err)
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
k1Nonce++
ret, err = invokeBVMContract(suite.api, k1, k1Nonce, constant.AppchainMgrContractAddr.Address(), "Manager",
pb.String(string(appchain_mgr.EventRegister)),
ret, err = invokeBVMContract(suite.api, priAdmin1, adminNonce1, constant.GovernanceContractAddr.Address(), "Vote",
pb.String(proposalId1),
pb.String(string(contracts.APPOVED)),
pb.Bytes(ret.Ret),
pb.String("reason"),
)
suite.Require().Nil(err)
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
k1Nonce++
adminNonce1++
ret, err = invokeBVMContract(suite.api, priAdmin2, adminNonce2, constant.GovernanceContractAddr.Address(), "Vote",
pb.String(proposalId1),
pb.String(string(contracts.APPOVED)),
pb.String("reason"),
)
suite.Require().Nil(err)
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
adminNonce2++
ret, err = invokeBVMContract(suite.api, priAdmin3, adminNonce3, constant.GovernanceContractAddr.Address(), "Vote",
pb.String(proposalId1),
pb.String(string(contracts.APPOVED)),
pb.String("reason"),
)
suite.Require().Nil(err)
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
adminNonce3++
ret, err = invokeBVMContract(suite.api, k1, k1Nonce, constant.InterchainContractAddr.Address(), "Interchain")
suite.Require().Nil(err)

View File

@ -6,6 +6,8 @@ import (
"path/filepath"
"strconv"
"github.com/meshplus/bitxhub/internal/executor/contracts"
"github.com/meshplus/bitxhub-kit/crypto"
"github.com/meshplus/bitxhub-kit/crypto/asym"
"github.com/meshplus/bitxhub-model/constant"
@ -37,7 +39,7 @@ func (suite *Role) TestGetRole() {
suite.Assert().Nil(err)
_, err = invokeBVMContract(suite.api, suite.privKey, suite.normalNonce, constant.AppchainMgrContractAddr.Address(), "Register",
pb.String(""),
pb.Int32(0),
pb.String("rbft"),
pb.String("hyperchain"),
pb.String("婚姻链"),
pb.String("趣链婚姻链"),
@ -106,6 +108,28 @@ func (suite *Role) TestIsAdmin() {
}
func (suite *Role) TestGetRuleAddress() {
path1 := "./test_data/config/node1/key.json"
path2 := "./test_data/config/node2/key.json"
path3 := "./test_data/config/node3/key.json"
keyPath1 := filepath.Join(path1)
keyPath2 := filepath.Join(path2)
keyPath3 := filepath.Join(path3)
priAdmin1, err := asym.RestorePrivateKey(keyPath1, "bitxhub")
suite.Require().Nil(err)
priAdmin2, err := asym.RestorePrivateKey(keyPath2, "bitxhub")
suite.Require().Nil(err)
priAdmin3, err := asym.RestorePrivateKey(keyPath3, "bitxhub")
suite.Require().Nil(err)
fromAdmin1, err := priAdmin1.PublicKey().Address()
suite.Require().Nil(err)
fromAdmin2, err := priAdmin2.PublicKey().Address()
suite.Require().Nil(err)
fromAdmin3, err := priAdmin3.PublicKey().Address()
suite.Require().Nil(err)
adminNonce1 := suite.api.Broker().GetPendingNonceByAccount(fromAdmin1.String())
adminNonce2 := suite.api.Broker().GetPendingNonceByAccount(fromAdmin2.String())
adminNonce3 := suite.api.Broker().GetPendingNonceByAccount(fromAdmin3.String())
k1, err := asym.GenerateKeyPair(crypto.Secp256k1)
suite.Require().Nil(err)
k2, err := asym.GenerateKeyPair(crypto.Secp256k1)
@ -126,7 +150,7 @@ func (suite *Role) TestGetRuleAddress() {
// Register
ret, err := invokeBVMContract(suite.api, k1, k1Nonce, constant.AppchainMgrContractAddr.Address(), "Register",
pb.String(""),
pb.Int32(0),
pb.String("rbft"),
pb.String("hyperchain"),
pb.String("婚姻链"),
pb.String("趣链婚姻链"),
@ -137,10 +161,38 @@ func (suite *Role) TestGetRuleAddress() {
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
k1Nonce++
id1 := gjson.Get(string(ret.Ret), "chain_id").String()
proposalId1 := gjson.Get(string(ret.Ret), "proposal_id").String()
ret, err = invokeBVMContract(suite.api, priAdmin1, adminNonce1, constant.GovernanceContractAddr.Address(), "Vote",
pb.String(proposalId1),
pb.String(string(contracts.APPOVED)),
pb.String("reason"),
)
suite.Require().Nil(err)
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
adminNonce1++
ret, err = invokeBVMContract(suite.api, priAdmin2, adminNonce2, constant.GovernanceContractAddr.Address(), "Vote",
pb.String(proposalId1),
pb.String(string(contracts.APPOVED)),
pb.String("reason"),
)
suite.Require().Nil(err)
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
adminNonce2++
ret, err = invokeBVMContract(suite.api, priAdmin3, adminNonce3, constant.GovernanceContractAddr.Address(), "Vote",
pb.String(proposalId1),
pb.String(string(contracts.APPOVED)),
pb.String("reason"),
)
suite.Require().Nil(err)
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
adminNonce3++
ret, err = invokeBVMContract(suite.api, k2, k2Nonce, constant.AppchainMgrContractAddr.Address(), "Register",
pb.String(""),
pb.Int32(0),
pb.String("rbft"),
pb.String("fabric"),
pb.String("政务链"),
pb.String("fabric政务"),
@ -151,6 +203,34 @@ func (suite *Role) TestGetRuleAddress() {
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
k2Nonce++
id2 := gjson.Get(string(ret.Ret), "chain_id").String()
proposalId2 := gjson.Get(string(ret.Ret), "proposal_id").String()
ret, err = invokeBVMContract(suite.api, priAdmin1, adminNonce1, constant.GovernanceContractAddr.Address(), "Vote",
pb.String(proposalId2),
pb.String(string(contracts.APPOVED)),
pb.String("reason"),
)
suite.Require().Nil(err)
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
adminNonce1++
ret, err = invokeBVMContract(suite.api, priAdmin2, adminNonce2, constant.GovernanceContractAddr.Address(), "Vote",
pb.String(proposalId2),
pb.String(string(contracts.APPOVED)),
pb.String("reason"),
)
suite.Require().Nil(err)
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
adminNonce2++
ret, err = invokeBVMContract(suite.api, priAdmin3, adminNonce3, constant.GovernanceContractAddr.Address(), "Vote",
pb.String(proposalId2),
pb.String(string(contracts.APPOVED)),
pb.String("reason"),
)
suite.Require().Nil(err)
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
adminNonce3++
// deploy rule
bytes, err := ioutil.ReadFile("./test_data/hpc_rule.wasm")
@ -207,7 +287,7 @@ func (suite *Role) TestSetAdminRoles() {
// register
retReg, err := invokeBVMContract(suite.api, priAdmin, adminNonce, constant.AppchainMgrContractAddr.Address(), "Register",
pb.String(""),
pb.Int32(0),
pb.String("rbft"),
pb.String("hyperchain"),
pb.String("管理链"),
pb.String("趣链管理链"),

View File

@ -64,7 +64,7 @@ func (suite *Governance) TestGovernance() {
// 1. Register ==============================================
ret, err := invokeBVMContract(suite.api, appchainPri, appchainNonce, constant.AppchainMgrContractAddr.Address(), "Register",
pb.String("validators"),
pb.Int32(0),
pb.String("rbft"),
pb.String("hyperchain"),
pb.String("税务链"),
pb.String("趣链税务链"),
@ -80,7 +80,7 @@ func (suite *Governance) TestGovernance() {
// repeated registration
ret, err = invokeBVMContract(suite.api, appchainPri, appchainNonce, constant.AppchainMgrContractAddr.Address(), "Register",
pb.String("validators"),
pb.Int32(0),
pb.String("rbft"),
pb.String("hyperchain"),
pb.String("税务链"),
pb.String("趣链税务链"),

View File

@ -21,7 +21,7 @@ func genXVMContractTransaction(privateKey crypto.PrivateKey, nonce uint64, addre
return genContractTransaction(pb.TransactionData_XVM, privateKey, nonce, address, method, args...)
}
func genIBTPTransaction(privateKey crypto.PrivateKey, ibtp *pb.IBTP) (*pb.Transaction, error) {
func genIBTPTransaction(privateKey crypto.PrivateKey, ibtp *pb.IBTP, nonce uint64) (*pb.Transaction, error) {
from, err := privateKey.PublicKey().Address()
if err != nil {
return nil, err
@ -58,7 +58,7 @@ func genIBTPTransaction(privateKey crypto.PrivateKey, ibtp *pb.IBTP) (*pb.Transa
To: constant.InterchainContractAddr.Address(),
Payload: payload,
Timestamp: time.Now().UnixNano(),
Nonce: ibtp.Index,
Nonce: nonce,
IBTP: ibtp,
}

View File

@ -95,6 +95,7 @@ func newTesterBitXHub(rep *repo.Repo) (*app.BitXHub, error) {
order.WithDigest(chainMeta.BlockHash.String()),
order.WithGetChainMetaFunc(bxh.Ledger.GetChainMeta),
order.WithGetBlockByHeightFunc(bxh.Ledger.GetBlock),
order.WithGetAccountNonceFunc(bxh.Ledger.GetNonce),
)
if err != nil {