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:
commit
8b2b9fe426
|
@ -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 }}
|
|
@ -27,4 +27,6 @@ testdata/storage
|
||||||
|
|
||||||
imports/imports.go
|
imports/imports.go
|
||||||
goent.mod
|
goent.mod
|
||||||
goent.sum
|
goent.sum
|
||||||
|
dist
|
||||||
|
build
|
||||||
|
|
|
@ -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:'
|
6
Makefile
6
Makefile
|
@ -2,7 +2,7 @@
|
||||||
SHELL := /bin/bash
|
SHELL := /bin/bash
|
||||||
CURRENT_PATH = $(shell pwd)
|
CURRENT_PATH = $(shell pwd)
|
||||||
APP_NAME = bitxhub
|
APP_NAME = bitxhub
|
||||||
APP_VERSION = 1.5.0
|
APP_VERSION = 1.6.0
|
||||||
export GODEBUG=x509ignoreCN=0
|
export GODEBUG=x509ignoreCN=0
|
||||||
|
|
||||||
# build with verison infos
|
# build with verison infos
|
||||||
|
@ -81,6 +81,10 @@ buildent:
|
||||||
@mv ./bitxhub bin
|
@mv ./bitxhub bin
|
||||||
@printf "${GREEN}Build bitxhub ent successfully!${NC}\n"
|
@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:
|
mod:
|
||||||
sed "s?)?$(MODS)\n)?" go.mod
|
sed "s?)?$(MODS)\n)?" go.mod
|
||||||
|
|
||||||
|
|
|
@ -76,15 +76,45 @@ func governanceCMD() cli.Command {
|
||||||
},
|
},
|
||||||
cli.Command{
|
cli.Command{
|
||||||
Name: "chain",
|
Name: "chain",
|
||||||
Usage: "query chain status by chain id",
|
Usage: "appchain manage command",
|
||||||
Flags: []cli.Flag{
|
Subcommands: cli.Commands{
|
||||||
cli.StringFlag{
|
cli.Command{
|
||||||
Name: "id",
|
Name: "status",
|
||||||
Usage: "chain id",
|
Usage: "query chain status by chain id",
|
||||||
Required: true,
|
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\"")
|
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 {
|
if err != nil {
|
||||||
return err
|
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() {
|
if receipt.IsSuccess() {
|
||||||
color.Green("vote successfully!\n")
|
color.Green("vote successfully!\n")
|
||||||
|
@ -155,7 +148,7 @@ func getProposals(ctx *cli.Context) error {
|
||||||
status := ctx.String("status")
|
status := ctx.String("status")
|
||||||
from := ctx.String("from")
|
from := ctx.String("from")
|
||||||
|
|
||||||
if err := checkArgs(id, typ, status, from); err != nil {
|
if err := checkProposalArgs(id, typ, status, from); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,7 +199,7 @@ func getProposals(ctx *cli.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkArgs(id, typ, status, from string) error {
|
func checkProposalArgs(id, typ, status, from string) error {
|
||||||
if id == "" &&
|
if id == "" &&
|
||||||
typ == "" &&
|
typ == "" &&
|
||||||
status == "" &&
|
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) {
|
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,
|
receipt, err := invokeBVMContract(ctx, constant.GovernanceContractAddr.String(), menthod, pb.String(arg))
|
||||||
pb.String(arg))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("send transaction error: %w", err)
|
return nil, 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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if receipt.IsSuccess() {
|
if receipt.IsSuccess() {
|
||||||
|
@ -356,22 +318,72 @@ func addRow(t *tabby.Tabby, rawLine []string, header bool) {
|
||||||
func getChainStatusById(ctx *cli.Context) error {
|
func getChainStatusById(ctx *cli.Context) error {
|
||||||
id := ctx.String("id")
|
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 {
|
if err != nil {
|
||||||
return err
|
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)
|
keyPath := repo.GetKeyPath(repoRoot)
|
||||||
|
|
||||||
resp, err := sendTx(ctx, constant.AppchainMgrContractAddr.String(), 0, uint64(pb.TransactionData_INVOKE), keyPath, uint64(pb.TransactionData_BVM), "GetAppchain",
|
resp, err := sendTx(ctx, contractAddr, 0, uint64(pb.TransactionData_INVOKE), keyPath, uint64(pb.TransactionData_BVM), method, args...)
|
||||||
pb.String(id))
|
|
||||||
if err != nil {
|
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()
|
hash := gjson.Get(string(resp), "tx_hash").String()
|
||||||
|
|
||||||
var data []byte
|
var data []byte
|
||||||
if err = retry.Retry(func(attempt uint) error {
|
if err = retry.Retry(func(attempt uint) error {
|
||||||
|
time.Sleep(1000 * time.Millisecond)
|
||||||
data, err = getTxReceipt(ctx, hash)
|
data, err = getTxReceipt(ctx, hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("get transaction receipt error: " + err.Error() + "... retry later")
|
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}
|
m := &runtime.JSONPb{OrigName: true, EmitDefaults: true, EnumsAsInts: true}
|
||||||
receipt := &pb.Receipt{}
|
receipt := &pb.Receipt{}
|
||||||
if err = m.Unmarshal(data, receipt); err != nil {
|
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() {
|
return receipt, nil
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
Binary file not shown.
After Width: | Height: | Size: 344 KiB |
Binary file not shown.
After Width: | Height: | Size: 190 KiB |
Binary file not shown.
After Width: | Height: | Size: 411 KiB |
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 |
|
@ -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值为来源链ID,value对应该来源链已发送的最新的跨链请求的序号,如{B:3, C:5}。
|
||||||
|
getInnerMeta() Response
|
||||||
|
|
||||||
|
// getOuterMeta 是获取跨链请求相关的Meta信息的接口。以Broker所在的区块链为来源链的一系列跨链请求的序号信息。如果以Broker在A链,则A可能和多条链进行跨链,如A->B:3; A->C:5。返回的map中,key值为目的链ID,value对应已发送到该目的链的最新跨链请求的序号,如{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值为目的链ID,value对应到该目的链最新的带回调跨链请求的序号,如{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)
|
||||||
|
|
|
@ -0,0 +1,388 @@
|
||||||
|
# 应用链插件编写
|
||||||
|
|
||||||
|
在本教程中,你将构建一个完整功能的 Plugin。过程中能学习基本的概念和具体使用细节。该示例将展示如何快速、轻松地**接入自己的区块链到跨链平台中来**。
|
||||||
|
|
||||||
|
如果你需要接入自己的开发的区块链到BitXHub跨链平台来的话,可以根据你们自己的区块链来定制开发Plugin,通过我们跨链网关加入到跨链平台来。
|
||||||
|
|
||||||
|
## 开发要求
|
||||||
|
|
||||||
|
- 安装 [__go1.13+__](https://golang.org/doc/install)
|
||||||
|
|
||||||
|
- 设置好$GOPATH等环境
|
||||||
|
|
||||||
|
## 教程章节
|
||||||
|
|
||||||
|
1. 重要概念
|
||||||
|
|
||||||
|
2. Plugin接口
|
||||||
|
|
||||||
|
3. 程序目标
|
||||||
|
|
||||||
|
4. 开始编写程序
|
||||||
|
|
||||||
|
5. 编译你的Plugin
|
||||||
|
|
||||||
|
## 重要概念
|
||||||
|
|
||||||
|
在解释具体的接口之前,先明确几个概念:
|
||||||
|
|
||||||
|
**跨链请求:**如果有两条区块链A和B,A链需要向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值为来源链ID,value对应该来源链已发送的最新的跨链请求的序号,如{B:3, C:5}。
|
||||||
|
GetInMeta() (map[string]uint64, error)
|
||||||
|
|
||||||
|
// GetOutMeta 是获取跨链请求相关的Meta信息的接口。以Plugin负责的区块链为来源链的一系列跨链请求的序号信息。如果Plugin负责A链,则A可能和多条链进行跨链,如A->B:3; A->C:5。返回的map中,key值为目的链ID,value对应已发送到该目的链的最新跨链请求的序号,如{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值为目的链ID,value对应到该目的链最新的带回调跨链请求的序号,如{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"`
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- meta:Plugin直接和跨链合约交互,需要保存你的合约的一些基础信息。由于我们需要连接一个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
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- eventClient:fabric gosdk提供的事件Client
|
||||||
|
|
||||||
|
- meta Fabric:相关的参数信息
|
||||||
|
|
||||||
|
- msgH:事件handler,在监听到指定事件之后负责处理的函数
|
||||||
|
|
||||||
|
- channelProvider:fabric gosdk提供的和chaincode交互
|
||||||
|
|
||||||
|
- ChannelClient:fabric gosdk 提供的和调用chaincode的对象
|
||||||
|
|
||||||
|
- registeration:fabric 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`,目标应用链上智能合约的地址或者ID(Fabric上的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,就能接入到跨链平台来。
|
|
@ -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
|
||||||
|
```
|
|
@ -0,0 +1,57 @@
|
||||||
|
# 环境准备
|
||||||
|
|
||||||
|
环境准备是部署和使用BitXHub跨链平台的第一步,主要是说明BitXHub及相关组件运行的硬件配置和软件依赖,您需要在部署BitXHub平台之前确认机器满足下述的要求。
|
||||||
|
|
||||||
|
## 硬件
|
||||||
|
|
||||||
|
配置| 推荐配置 | 最低配置
|
||||||
|
---|---|---
|
||||||
|
CPU | 2.4GHz *8核或以上 |1.5GHz *4核
|
||||||
|
内存 | 16GB或以上 | 8GB
|
||||||
|
存储 | 500G或以上(需要支持扩容) |100G
|
||||||
|
带宽 | 10Mb |2Mb
|
||||||
|
|
||||||
|
## 操作系统支持
|
||||||
|
|
||||||
|
目前BitXHub支持的操作系统以及对应版本号如下:
|
||||||
|
|
||||||
|
操作系统| 发行版本 | 系统架构
|
||||||
|
---|---|---
|
||||||
|
RHEL | 6或更新 |amd64,386
|
||||||
|
CentOS | 6或更新| amd64,386
|
||||||
|
SUSE |11SP3或更新|amd64,386
|
||||||
|
Ubuntu |14.04或更新|amd64,386
|
||||||
|
MacOS |10.8或更新|amd64,386
|
||||||
|
|
||||||
|
**说明:为了更好的部署安装体验,我们建议您选用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平台啦!!!
|
|
@ -1,8 +1,8 @@
|
||||||
.md-header-nav__button.md-logo {
|
.md-header .md-logo {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.md-header-nav__button.md-logo img {
|
.md-header .md-logo img {
|
||||||
width: auto;
|
width: auto;
|
||||||
height: 2rem;
|
height: 2rem;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
```
|
||||||
|
|
|
@ -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启动成功
|
||||||
|
|
||||||
|
**说明:因为跨链合约和验证规则的部署涉及到不同应用链的细节,且需依赖应用链的安装部署,具体操作请见快速开始手册或使用文档,这里不再赘述**
|
||||||
|
|
|
@ -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.dylib,Linux使用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)
|
||||||
|
|
|
@ -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.dylib,Linux使用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启动成功
|
||||||
|
|
||||||
|
|
|
@ -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-cli(fabric官方提供)
|
||||||
|
|
||||||
|
```
|
||||||
|
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.dylib,Linux使用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启动成功
|
||||||
|
|
||||||
|
|
|
@ -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”即正常启动。
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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. 本文是以一方的跨链网关为例进行部署,而另一方的跨链网关的部署与之基本一样,这里不再赘述。**
|
||||||
|
|
||||||
|
|
|
@ -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 合约接口
|
||||||
|
|
||||||
|
合约类型:
|
||||||
|
|
||||||
|
- BVM:BitXHub内置合约。
|
||||||
|
|
||||||
|
- XVM:WebAssembly合约。
|
||||||
|
|
||||||
|
### 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)
|
||||||
|
```
|
|
@ -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 合约接口
|
||||||
|
|
||||||
|
合约类型:
|
||||||
|
|
||||||
|
- BVM:BitXHub内置合约
|
||||||
|
|
||||||
|
- XVM:WebAssembly合约
|
||||||
|
|
||||||
|
### 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);
|
||||||
|
```
|
|
@ -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 合约接口
|
||||||
|
|
||||||
|
合约类型:
|
||||||
|
|
||||||
|
- BVM:BitXHub内置合约。
|
||||||
|
|
||||||
|
- XVM:WebAssembly合约。
|
||||||
|
|
||||||
|
### 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)
|
||||||
|
```
|
|
@ -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`
|
|
@ -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`中继链,就能接入到跨链平台来。
|
|
@ -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)
|
|
@ -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)
|
||||||
|
```
|
|
@ -0,0 +1,211 @@
|
||||||
|
# 跨链网关设计方案
|
||||||
|
## 整体架构
|
||||||
|
|
||||||
|
在中继链的设计中,对于跨链网关的主要功能作了简要的介绍。本文主要详细介绍跨链网关的主要设计架构思想。
|
||||||
|
|
||||||
|
从跨链网关的功能上来说,设计上需要解决的难点包括以下几点:
|
||||||
|
|
||||||
|
1. 跨链网关需要对接不同架构的区块链,如何简化跨链网关接入不同区块链的跨链网关设计上需要考虑的问题。
|
||||||
|
|
||||||
|
2. 跨链网关需要支持中继模式(直接和中继链连接)和直连模式(直接和其他的Pier进行连接),如何在不同模式间切换时设计上需要考虑的问题。
|
||||||
|
|
||||||
|
从总体架构来说,跨链网关根据不同的功能采取了模块划分的方式,主要的功能模块有Monitor,Executor,Exchanger,Validate Engine,Appchain Manager,Network等。
|
||||||
|
|
||||||
|
![](../../../assets/pier.png)
|
||||||
|
|
||||||
|
## 处理流程
|
||||||
|
|
||||||
|
一次完整的跨链交易的处理过程如下:
|
||||||
|
|
||||||
|
A.Monitor监听
|
||||||
|
|
||||||
|
跨链网关PA启动之后,Appchain A发起一笔跨链交易,Monitor模块监听到该跨链交易,跨链网关对于该跨链交易做出检查之后,保存相应的跨链交易。
|
||||||
|
|
||||||
|
B.Exchanger转发
|
||||||
|
|
||||||
|
Exchanger获取Monitor收到的跨链交易,作相应的检查后,进行转发。转发过程中,根据跨链交易的目的链ID以及连接的是中继链还是直连的其他跨链网关等信息,转发到正确的路由路径。
|
||||||
|
|
||||||
|
1. 中继链模式
|
||||||
|
|
||||||
|
通过中继链的SDK,提交跨链交易到中继链的内置合约上,中继链记录并执行验证,转发等操作。
|
||||||
|
|
||||||
|
2. 直连模式
|
||||||
|
|
||||||
|
通过P2P网络连接其他跨链网关,通过跨链交易的目的链ID来转发到相应的跨链网关。
|
||||||
|
|
||||||
|
C. Exchanger接受外部跨链交易
|
||||||
|
|
||||||
|
1. 中继链模式
|
||||||
|
|
||||||
|
Exchanger 的子模块Lite和Syncer负责同步中继链的区块头和跨链交易的信息,对于验证通过的跨链交易,Exchanger进行转送到Executor中。
|
||||||
|
|
||||||
|
2. 直连模式
|
||||||
|
|
||||||
|
Exchanger通过P2P网络收到对方跨链网关发送的跨链交易,并作出相应的验证操作。验证通过的跨链交易转送到Executor中。
|
||||||
|
|
||||||
|
Executor提交跨链交易到应用链上,并根据执行的结果,构造返回的回执类型的IBTP包,转送到Exchanger进行下一步的转发工作。
|
||||||
|
|
||||||
|
D.Exchanger 接收外部回执
|
||||||
|
|
||||||
|
来源链发送的跨链交易在目的链执行之后,返回回执信息又回到了来源链的跨链网关之后,进行如下的处理。
|
||||||
|
|
||||||
|
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能够做到无状态启动,并且能解决整体架构中提出的两个问题。
|
|
@ -0,0 +1,25 @@
|
||||||
|
# 隐私保护
|
||||||
|
## 前提
|
||||||
|
|
||||||
|
在跨链场景中,中继链需要记录不同的应用链的跨链请求,所有的跨链交易对于加入了中继链跨链系统的应用链来说都是可见的。这种情况下,如果应用链用户发起的跨链交易带有隐私数据,隐私数据非常容易泄露。
|
||||||
|
|
||||||
|
![](/assets/privacy1.png)
|
||||||
|
|
||||||
|
所以我们在中继链的设计中需要隐私保护相关的机制,并且能够保持比较高的灵活度。在跨链系统中,我们提出了两种可行的隐私保护机制。
|
||||||
|
|
||||||
|
## 隐私交易
|
||||||
|
|
||||||
|
跨链交易在提交到中继链的共识节点之后,会通过共识发送到所有的其他节点去,所以跨链交易对于整个中继链来说没有隐私性可言。而隐私交易这种机制下,收到跨链交易的节点并不会将跨链交易的内容直接进行共识,而是计算跨链交易的哈希值进行共识。跨链交易在共识完成之后,参与方的共识节点将完整的跨链交易直接发送到参与方负责的中继链节点。
|
||||||
|
|
||||||
|
![](/assets/privacy2.png)
|
||||||
|
这样不相关的节点就无法直接获取到跨链交易,能够保护用户的交易隐私信息。
|
||||||
|
|
||||||
|
## 端到端加密
|
||||||
|
|
||||||
|
另一种进行隐私保护的思路是在跨链交易提交之前,就对跨链交易进行加密操作。为了提升加解密的效率,采用对称加密和秘钥协商的机制来实现。具体的一次加密交易过程如下图:
|
||||||
|
|
||||||
|
![](/assets/privacy3.png)
|
||||||
|
|
||||||
|
|
||||||
|
要进行加密交易的双方需要在中继链上注册自己的私钥衍生的公钥,这样双方通过自己拥有的私钥和对方注册的公钥,能够计算出一个相同的对称加密的秘钥,用于提交跨链交易之前的加密和获取跨链交易之后的解密。这个秘钥只有公钥的话无法解出用于加密的对称秘钥,所以能够保证跨链交易的隐私性。
|
||||||
|
|
|
@ -4,8 +4,71 @@ repo_name: meshplus/bitxhub
|
||||||
extra_css:
|
extra_css:
|
||||||
- stylesheets/extra.css
|
- stylesheets/extra.css
|
||||||
theme:
|
theme:
|
||||||
logo: /assets/logo.png
|
logo: assets/logo.png
|
||||||
name: material
|
name: material
|
||||||
|
features:
|
||||||
|
- navigation.tabs
|
||||||
|
font:
|
||||||
|
text: Roboto
|
||||||
|
code: Roboto Mono
|
||||||
palette:
|
palette:
|
||||||
scheme: blue
|
primary: red
|
||||||
accent: blue
|
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
18
go.mod
|
@ -22,33 +22,26 @@ require (
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0
|
github.com/grpc-ecosystem/grpc-gateway v1.16.0
|
||||||
github.com/hashicorp/golang-lru v0.5.4
|
github.com/hashicorp/golang-lru v0.5.4
|
||||||
github.com/hokaccha/go-prettyjson v0.0.0-20190818114111-108c894c2c0e
|
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/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/libp2p/go-libp2p-core v0.5.6
|
||||||
github.com/magiconair/properties v1.8.4
|
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-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-libp2p-cert v0.0.0-20210125063330-7c25fd5b7a49
|
||||||
github.com/meshplus/go-lightp2p v0.0.0-20210120082108-df5a536a6192
|
github.com/meshplus/go-lightp2p v0.0.0-20210120082108-df5a536a6192
|
||||||
github.com/mitchellh/go-homedir v1.1.0
|
github.com/mitchellh/go-homedir v1.1.0
|
||||||
github.com/multiformats/go-multiaddr v0.3.0
|
github.com/multiformats/go-multiaddr v0.3.0
|
||||||
github.com/orcaman/concurrent-map v0.0.0-20190826125027-8c72a8bb44f6
|
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/pkg/errors v0.9.1
|
||||||
github.com/prometheus/client_golang v1.5.0
|
github.com/prometheus/client_golang v1.5.0
|
||||||
github.com/rogpeppe/go-internal v1.5.2 // indirect
|
github.com/rogpeppe/go-internal v1.5.2 // indirect
|
||||||
github.com/rs/cors v1.7.0
|
github.com/rs/cors v1.7.0
|
||||||
github.com/sirupsen/logrus v1.7.0
|
github.com/sirupsen/logrus v1.7.0
|
||||||
github.com/spf13/cast v1.3.1
|
github.com/spf13/cast v1.3.1
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
|
||||||
github.com/spf13/viper v1.7.1
|
github.com/spf13/viper v1.7.1
|
||||||
github.com/stretchr/objx v0.2.0 // indirect
|
github.com/stretchr/testify v1.6.1
|
||||||
github.com/stretchr/testify v1.6.0
|
|
||||||
github.com/sykesm/zap-logfmt v0.0.4 // indirect
|
|
||||||
github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d
|
github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d
|
||||||
github.com/tidwall/gjson v1.6.8
|
github.com/tidwall/gjson v1.6.8
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5
|
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/bitset v1.1.11 // indirect
|
||||||
github.com/willf/bloom v2.0.3+incompatible
|
github.com/willf/bloom v2.0.3+incompatible
|
||||||
go.uber.org/atomic v1.7.0
|
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
|
google.golang.org/grpc v1.33.2
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
148
go.sum
148
go.sum
|
@ -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/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/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/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 h1:FqK94z34ly8Baa6K+G8Mmza9rYWTKOJk+yckIBB5qVk=
|
||||||
github.com/Rican7/retry v0.1.0/go.mod h1:FgOROf8P5bebcC1DS0PdOQiqGUridaZvikzUmkFW6gg=
|
github.com/Rican7/retry v0.1.0/go.mod h1:FgOROf8P5bebcC1DS0PdOQiqGUridaZvikzUmkFW6gg=
|
||||||
github.com/Shopify/sarama v1.26.1/go.mod h1:NbSGBSSndYaIhRcBtY9V0U7AyH+x71bG668AuWys/yU=
|
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/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
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/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-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-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8=
|
||||||
github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI=
|
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-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 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
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/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 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=
|
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/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 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
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/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 h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI=
|
||||||
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
|
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/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-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 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-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.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 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk=
|
||||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
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.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 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA=
|
||||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
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-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-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 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
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.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
|
||||||
github.com/gobuffalo/envy v1.9.0 h1:eZR0DuEgVLfeIb1zIKt3bT4YovIMf9O9LXQeCZLXpqE=
|
github.com/gobuffalo/envy v1.9.0 h1:eZR0DuEgVLfeIb1zIKt3bT4YovIMf9O9LXQeCZLXpqE=
|
||||||
github.com/gobuffalo/envy v1.9.0/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w=
|
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/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 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg=
|
||||||
github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=
|
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/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.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.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||||
github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
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.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 h1:lMm2hD9Fy0ynom5+85/pbdkiYcBqM1JWmhpAXLmy0fw=
|
||||||
github.com/golang/snappy v0.0.2-0.20200707131729-196ae77b8a26/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
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 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 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
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-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/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/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 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
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=
|
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/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 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
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-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.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
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/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/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.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 h1:FlFbCRLd5Jr4iYXZufAvgWN6Ao0JrI5chLINnUXDDr0=
|
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/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 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/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/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 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||||
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
|
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/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 h1:I/yrLt2WilKxlQKCM52clh5rGzTKpVctGT1lH4Dc8Jw=
|
||||||
github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU=
|
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/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.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
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 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
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/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.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.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 h1:a/y8CglcM7gLGYmlbP/stPE5sR3hbhFRUjCBfd/0B3I=
|
||||||
github.com/klauspost/compress v1.10.10/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
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/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/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=
|
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/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 h1:68u9r4wEvL3gYg2jvAOgROwZ3H+Y3hIDk4tbbmIjcYQ=
|
||||||
github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk=
|
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 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
|
||||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
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.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
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/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.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.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 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
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.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 h1:qqOPU7y+TM8Y803I8fG9c/DyKG3xH/xkng6keC1015Q=
|
||||||
github.com/lestrrat-go/strftime v1.0.3/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR76fd03sz+Qz4g=
|
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.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 h1:7cWK5cdA5x72jX0g8iLrQWm5TRJZ6CzGdPEhWj7plWU=
|
||||||
github.com/libp2p/go-addr-util v0.0.2/go.mod h1:Ecd6Fb3yIuLzq4bD7VcywcVSBtefcAwnUISBM3WG15E=
|
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.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 h1:O5qcBXRcfqecvQ/My9NqDNHB3/5t58yuJYqthcKhhgE=
|
||||||
github.com/libp2p/go-yamux v1.3.6/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=
|
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 h1:M8hf5EF4AYLcT1FNKVUX8nu7D0xfp291iGeuigSxfrw=
|
||||||
github.com/looplab/fsm v0.2.0/go.mod h1:p+IElwgCnAByqr2DWMuNbPjgMwqcHvTRZZn3dvKEke0=
|
github.com/looplab/fsm v0.2.0/go.mod h1:p+IElwgCnAByqr2DWMuNbPjgMwqcHvTRZZn3dvKEke0=
|
||||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
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 h1:8KGKTcQQGm0Kv7vEbKFErAoAOFyyacLStRtQSeYtvkY=
|
||||||
github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
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/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.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.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
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.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 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
|
||||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
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 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
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.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 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.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=
|
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.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 h1:GdgS14bnCF4b/a5zhQi2wlu92pHc9cfl6A1HcbO7zmE=
|
||||||
github.com/meshplus/bitxhub-kit v1.1.2-0.20210112075018-319e668d6359/go.mod h1:KR7ZlXhII9n0Bu8viaZTScvXCYn0MCQnYlsTvHPp0XA=
|
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.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 h1:ziae0L0cbCMKp66OYzjZuU1WtNoB2TgfFhNIVWOTod4=
|
||||||
github.com/meshplus/bitxhub-model v1.1.2-0.20210309053945-afaea82e9fe1/go.mod h1:x3H+TL24wcByzHegenLfs+5PQkQGNsk8eCm31QJMa+Q=
|
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 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-20210120021632-1578cf63e06a/go.mod h1:rS4AYMqKypLn2IPEnHICP//V2v16SZo4CWUbwMdihl0=
|
||||||
github.com/meshplus/go-libp2p-cert v0.0.0-20210125063330-7c25fd5b7a49 h1:F8dpLJZW6FxqinAQcZKTkoymZgxnqlNvTebNqWVMEYI=
|
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.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 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
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/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/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
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 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 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
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-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/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 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/modern-go/reflect2 v1.0.1/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.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.1/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
|
||||||
github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
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/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/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/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 h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
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/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 h1:goeTyGkArOZIVOMA0dQbyuPWGNQJZGPwPu/QS9GlpnA=
|
||||||
github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34=
|
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 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
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 h1:oYW+YCJ1pachXTQmzR3rNLYGGz4g/UgFcjb28p/viDM=
|
||||||
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
|
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.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 h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI=
|
||||||
github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
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.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.8.1/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 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
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=
|
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.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 h1:If5rVCMTp6W2SiRAQFlbpJNgVlgMEd+U2GZckwK38ic=
|
||||||
github.com/prometheus/tsdb v0.10.0/go.mod h1:oi49uRhEe9dPUTlS3JRZOwJuVi6tmh10QSgwXEyGCt4=
|
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-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 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ=
|
||||||
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
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.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.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.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||||
github.com/rogpeppe/go-internal v1.5.2 h1:qLvObTrvO/XRCqmkKxUlOBc48bI3efyDuAZe25QiF0w=
|
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
||||||
github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
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 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 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
|
||||||
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
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/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/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/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 h1:tYH07UPoQt0OCQdgWWMgYHy3/a9bcxNpBIysykNIP7I=
|
||||||
github.com/shirou/gopsutil v2.20.5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
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 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
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=
|
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.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 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
|
||||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
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.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 h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
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 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
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/smola/gocompat v0.2.0/go.mod h1:1B0MlxbmoZNo3h8guHp8HztB3BSYR5itql9qtVc0ypY=
|
||||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
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/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 h1:RC6RW7j+1+HkWaX/Yh71Ee5ZHaHYt7ZP4sQgUrm6cDU=
|
||||||
github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc=
|
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/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 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
|
||||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
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 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
|
||||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
|
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/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 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
|
||||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
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 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
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 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
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.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 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
|
||||||
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
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=
|
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.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 h1:jlIyCplCJFULU/01vCkhKuTyc3OorI3bJFuw6obfgho=
|
||||||
github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
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 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
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=
|
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/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||||
github.com/tidwall/pretty v1.0.2 h1:Z7S3cePv9Jwm1KwS0513MRaoUe3S01WPbLNV40pwWZU=
|
github.com/tidwall/pretty v1.0.2 h1:Z7S3cePv9Jwm1KwS0513MRaoUe3S01WPbLNV40pwWZU=
|
||||||
github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
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/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 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
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 h1:wHSqTBrZW24CsNJDfeh9Ex6Pm0Rcpc7qrgKBiL44vF4=
|
||||||
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs=
|
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/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 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY=
|
||||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
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/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/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 h1:G95XP3fE2FszQSwIU+fHPBYzD0Csmd2ef33snQXNA5Q=
|
||||||
github.com/wasmerio/go-ext-wasm v0.3.1/go.mod h1:VGyarTzasuS7k5KhSIGpM3tciSZlkP31Mp9VJTHMMeI=
|
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-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-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-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-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-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72/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-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 h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
|
||||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
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-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-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
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/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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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=
|
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-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 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
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-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-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/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-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 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-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-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-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/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-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-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-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-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-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/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-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 h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
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-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.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.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/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.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 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
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-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-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/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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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 h1:hHkvGJK23seRCflePJnVa9IMv8fsuavSCWKd11kDQFs=
|
||||||
golang.org/x/tools v0.0.0-20200221224223-e1da425f72fd/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
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-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 h1:CB3a9Nez8M13wwlr/E2YtwoU+qYHKfC+JrDa45RXXoQ=
|
||||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
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=
|
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/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 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
|
||||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
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 h1:cVVZBK2b1zY26haWB4vbBiZrfFQnfbTVrE3xZq6hrEw=
|
||||||
gopkg.in/jcmturner/aescts.v1 v1.0.1/go.mod h1:nsR8qBOg+OucoIW+WMhB3GspUQXq9XorLnQb9XtvcOo=
|
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=
|
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.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.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.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 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
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-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 h1:VEmvx0P+GVTgkNu2EdTN988YCZPcD3lo9AoczZpucwc=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200601152816-913338de1bd2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
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.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 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
|
||||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
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/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/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||||
|
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
|
||||||
|
|
|
@ -64,6 +64,7 @@ func NewBitXHub(rep *repo.Repo) (*BitXHub, error) {
|
||||||
order.WithDigest(chainMeta.BlockHash.String()),
|
order.WithDigest(chainMeta.BlockHash.String()),
|
||||||
order.WithGetChainMetaFunc(bxh.Ledger.GetChainMeta),
|
order.WithGetChainMetaFunc(bxh.Ledger.GetChainMeta),
|
||||||
order.WithGetBlockByHeightFunc(bxh.Ledger.GetBlock),
|
order.WithGetBlockByHeightFunc(bxh.Ledger.GetBlock),
|
||||||
|
order.WithGetAccountNonceFunc(bxh.Ledger.GetNonce),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -22,15 +22,29 @@ type RegisterResult struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (am *AppchainManager) Manager(des string, proposalResult string, extra []byte) *boltvm.Response {
|
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
|
am.AppchainManager.Persister = am.Stub
|
||||||
chain := &appchainMgr.Appchain{}
|
chain := &appchainMgr.Appchain{}
|
||||||
if err := json.Unmarshal(extra, chain); err != nil {
|
if err := json.Unmarshal(extra, chain); err != nil {
|
||||||
return boltvm.Error("unmarshal json error:" + err.Error())
|
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 {
|
if !ok {
|
||||||
return boltvm.Error(string(err))
|
return boltvm.Error(string(errData))
|
||||||
}
|
}
|
||||||
|
|
||||||
if proposalResult == string(APPOVED) {
|
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
|
// Register appchain managers registers appchain info caller is the appchain
|
||||||
// manager address return appchain id and error
|
// 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
|
am.AppchainManager.Persister = am.Stub
|
||||||
ok, idData := am.AppchainManager.Register(am.Caller(), validators, consensusType, chainType, name, desc, version, pubkey)
|
ok, idData := am.AppchainManager.Register(am.Caller(), validators, consensusType, chainType, name, desc, version, pubkey)
|
||||||
if ok {
|
if ok {
|
||||||
|
@ -81,10 +95,122 @@ func (am *AppchainManager) Register(validators string, consensusType int32, chai
|
||||||
return boltvm.Success(resData)
|
return boltvm.Success(resData)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateAppchain updates approved appchain
|
// UpdateAppchain updates available appchain
|
||||||
func (am *AppchainManager) UpdateAppchain(validators string, consensusType int32, chainType, name, desc, version, pubkey string) *boltvm.Response {
|
func (am *AppchainManager) UpdateAppchain(validators string, consensusType, chainType, name, desc, version, pubkey string) *boltvm.Response {
|
||||||
am.AppchainManager.Persister = am.Stub
|
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
|
// CountApprovedAppchains counts all approved appchains
|
||||||
|
@ -154,3 +280,23 @@ func responseWrapper(ok bool, data []byte) *boltvm.Response {
|
||||||
}
|
}
|
||||||
return boltvm.Error(string(data))
|
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)
|
||||||
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ func TestAppchainManager_Appchain(t *testing.T) {
|
||||||
ID: addr0,
|
ID: addr0,
|
||||||
Name: "appchain A",
|
Name: "appchain A",
|
||||||
Validators: "",
|
Validators: "",
|
||||||
ConsensusType: 0,
|
ConsensusType: "",
|
||||||
ChainType: "fabric",
|
ChainType: "fabric",
|
||||||
Desc: "",
|
Desc: "",
|
||||||
Version: "",
|
Version: "",
|
||||||
|
@ -79,7 +79,7 @@ func TestAppchainManager_Appchains(t *testing.T) {
|
||||||
ID: addr,
|
ID: addr,
|
||||||
Name: "appchain" + addr,
|
Name: "appchain" + addr,
|
||||||
Validators: "",
|
Validators: "",
|
||||||
ConsensusType: int32(i),
|
ConsensusType: "",
|
||||||
ChainType: "fabric",
|
ChainType: "fabric",
|
||||||
Desc: "",
|
Desc: "",
|
||||||
Version: "",
|
Version: "",
|
||||||
|
@ -178,7 +178,7 @@ func TestAppchainManager_Manager(t *testing.T) {
|
||||||
ID: "addr",
|
ID: "addr",
|
||||||
Name: "appchain A",
|
Name: "appchain A",
|
||||||
Validators: "",
|
Validators: "",
|
||||||
ConsensusType: int32(1),
|
ConsensusType: "",
|
||||||
ChainType: "fabric",
|
ChainType: "fabric",
|
||||||
Desc: "",
|
Desc: "",
|
||||||
Version: "",
|
Version: "",
|
||||||
|
@ -192,7 +192,7 @@ func TestAppchainManager_Manager(t *testing.T) {
|
||||||
ID: "addr1",
|
ID: "addr1",
|
||||||
Name: "appchain A",
|
Name: "appchain A",
|
||||||
Validators: "",
|
Validators: "",
|
||||||
ConsensusType: int32(1),
|
ConsensusType: "",
|
||||||
ChainType: "fabric",
|
ChainType: "fabric",
|
||||||
Desc: "",
|
Desc: "",
|
||||||
Version: "",
|
Version: "",
|
||||||
|
@ -206,59 +206,124 @@ func TestAppchainManager_Manager(t *testing.T) {
|
||||||
mockStub.EXPECT().Has(AppchainKey("addr")).Return(true).AnyTimes()
|
mockStub.EXPECT().Has(AppchainKey("addr")).Return(true).AnyTimes()
|
||||||
mockStub.EXPECT().Has(AppchainKey("addr1")).Return(false).AnyTimes()
|
mockStub.EXPECT().Has(AppchainKey("addr1")).Return(false).AnyTimes()
|
||||||
mockStub.EXPECT().SetObject(gomock.Any(), gomock.Any()).Return().AnyTimes()
|
mockStub.EXPECT().SetObject(gomock.Any(), gomock.Any()).Return().AnyTimes()
|
||||||
mockStub.EXPECT().GetObject(gomock.Any(), gomock.Any()).Do(
|
mockStub.EXPECT().CurrentCaller().Return("addrNotAdmin").Times(1)
|
||||||
func(key string, ret interface{}) bool {
|
mockStub.EXPECT().CurrentCaller().Return(constant.GovernanceContractAddr.String()).AnyTimes()
|
||||||
chain := ret.(*appchainMgr.Appchain)
|
mockStub.EXPECT().CrossInvoke(constant.RoleContractAddr.String(), "CheckPermission", gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(boltvm.Error("")).Times(1)
|
||||||
chain.Status = appchainMgr.AppchainAvailable
|
mockStub.EXPECT().CrossInvoke(constant.RoleContractAddr.String(), "CheckPermission", gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(boltvm.Success(nil)).AnyTimes()
|
||||||
assert.Equal(t, key, AppchainKey("addr"))
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
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)
|
assert.False(t, res.Ok)
|
||||||
res = am.Manager(appchainMgr.EventUpdate, string(REJECTED), data1)
|
res = am.Manager(appchainMgr.EventUpdate, string(REJECTED), data1)
|
||||||
assert.False(t, res.Ok)
|
assert.False(t, res.Ok)
|
||||||
res = am.Manager(appchainMgr.EventUpdate, string(APPOVED), data)
|
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)
|
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.Error("")).Times(1)
|
||||||
mockStub.EXPECT().CrossInvoke(constant.InterchainContractAddr.String(), "Register", gomock.Any()).Return(boltvm.Success(nil)).AnyTimes()
|
mockStub.EXPECT().CrossInvoke(constant.InterchainContractAddr.String(), "Register", gomock.Any()).Return(boltvm.Success(nil)).AnyTimes()
|
||||||
res = am.Manager(appchainMgr.EventRegister, string(APPOVED), data)
|
res = am.Manager(appchainMgr.EventRegister, string(APPOVED), data)
|
||||||
assert.False(t, res.Ok)
|
assert.False(t, res.Ok)
|
||||||
res = am.Manager(appchainMgr.EventRegister, string(APPOVED), data)
|
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) {
|
func TestAppchainManager_IsAvailable(t *testing.T) {
|
||||||
am, mockStub, chains, _ := prepare(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")
|
logger := log.NewWithModule("contracts")
|
||||||
// test for DeleteAppchain
|
|
||||||
mockStub.EXPECT().Caller().Return(caller).AnyTimes()
|
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().SetObject(gomock.Any(), gomock.Any()).Return().AnyTimes()
|
||||||
mockStub.EXPECT().Logger().Return(logger).AnyTimes()
|
mockStub.EXPECT().Logger().Return(logger).AnyTimes()
|
||||||
// TODO: test UpdateAppchain without register (false)
|
mockStub.EXPECT().Get(AppchainKey(caller)).Return(true, chainsData[0]).AnyTimes()
|
||||||
// test UpdateAppchain with register
|
mockStub.EXPECT().Get(AppchainKey("freezingChain")).Return(true, chainsData[1]).AnyTimes()
|
||||||
mockStub.EXPECT().GetObject(gomock.Any(), gomock.Any()).Do(
|
mockStub.EXPECT().CrossInvoke(constant.GovernanceContractAddr.String(), "SubmitProposal", gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(boltvm.Success(nil)).AnyTimes()
|
||||||
func(key string, ret interface{}) bool {
|
mockStub.EXPECT().CrossInvoke(constant.RoleContractAddr.String(), "CheckPermission", gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(boltvm.Success(nil)).AnyTimes()
|
||||||
chain := ret.(*appchainMgr.Appchain)
|
|
||||||
chain.Status = appchainMgr.AppchainAvailable
|
// test UpdateAppchain
|
||||||
chain.PublicKey = chains[0].PublicKey
|
|
||||||
assert.Equal(t, key, AppchainKey(caller))
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
res := am.UpdateAppchain(chains[0].Validators, chains[0].ConsensusType, chains[0].ChainType,
|
res := am.UpdateAppchain(chains[0].Validators, chains[0].ConsensusType, chains[0].ChainType,
|
||||||
chains[0].Name, chains[0].Desc, chains[0].Version, chains[0].PublicKey)
|
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) {
|
func TestCountApprovedAppchains(t *testing.T) {
|
||||||
am, mockStub, _, chainsData := prepare(t)
|
am, mockStub, _, chainsData := prepare(t)
|
||||||
|
|
||||||
logger := log.NewWithModule("contracts")
|
logger := log.NewWithModule("contracts")
|
||||||
// test for DeleteAppchain
|
|
||||||
mockStub.EXPECT().Caller().Return(caller).AnyTimes()
|
mockStub.EXPECT().Caller().Return(caller).AnyTimes()
|
||||||
mockStub.EXPECT().SetObject(gomock.Any(), gomock.Any()).Return().AnyTimes()
|
mockStub.EXPECT().SetObject(gomock.Any(), gomock.Any()).Return().AnyTimes()
|
||||||
mockStub.EXPECT().Logger().Return(logger).AnyTimes()
|
mockStub.EXPECT().Logger().Return(logger).AnyTimes()
|
||||||
|
@ -266,7 +331,7 @@ func TestCountApprovedAppchains(t *testing.T) {
|
||||||
mockStub.EXPECT().Query(appchainMgr.PREFIX).Return(true, chainsData)
|
mockStub.EXPECT().Query(appchainMgr.PREFIX).Return(true, chainsData)
|
||||||
res := am.CountAvailableAppchains()
|
res := am.CountAvailableAppchains()
|
||||||
assert.Equal(t, true, res.Ok)
|
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) {
|
func TestDeleteAppchain(t *testing.T) {
|
||||||
|
@ -281,12 +346,26 @@ func TestDeleteAppchain(t *testing.T) {
|
||||||
mockStub.EXPECT().Caller().Return(caller).AnyTimes()
|
mockStub.EXPECT().Caller().Return(caller).AnyTimes()
|
||||||
mockStub.EXPECT().SetObject(gomock.Any(), gomock.Any()).Return().AnyTimes()
|
mockStub.EXPECT().SetObject(gomock.Any(), gomock.Any()).Return().AnyTimes()
|
||||||
mockStub.EXPECT().Logger().Return(logger).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",
|
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()
|
mockStub.EXPECT().Delete(AppchainKey(caller)).Return()
|
||||||
|
|
||||||
|
// judge caller type error
|
||||||
res := am.DeleteAppchain(caller)
|
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)
|
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 chains []*appchainMgr.Appchain
|
||||||
var chainsData [][]byte
|
var chainsData [][]byte
|
||||||
|
chainType := []string{string(appchainMgr.AppchainAvailable), string(appchainMgr.AppchainFrozen)}
|
||||||
for i := 0; i < 2; i++ {
|
for i := 0; i < 2; i++ {
|
||||||
addr := types.NewAddress([]byte{byte(i)}).String()
|
addr := types.NewAddress([]byte{byte(i)}).String()
|
||||||
|
|
||||||
chain := &appchainMgr.Appchain{
|
chain := &appchainMgr.Appchain{
|
||||||
Status: appchainMgr.AppchainAvailable,
|
Status: appchainMgr.AppchainStatus(chainType[i]),
|
||||||
ID: addr,
|
ID: addr,
|
||||||
Name: "appchain" + addr,
|
Name: "appchain" + addr,
|
||||||
Validators: "",
|
Validators: "",
|
||||||
ConsensusType: int32(i),
|
ConsensusType: "",
|
||||||
ChainType: "fabric",
|
ChainType: "fabric",
|
||||||
Desc: "",
|
Desc: "",
|
||||||
Version: "",
|
Version: "",
|
||||||
|
@ -491,7 +571,8 @@ func TestInterchainManager_HandleIBTP(t *testing.T) {
|
||||||
ID: "",
|
ID: "",
|
||||||
Name: "Relay1",
|
Name: "Relay1",
|
||||||
Validators: "",
|
Validators: "",
|
||||||
ConsensusType: 0,
|
ConsensusType: "",
|
||||||
|
Status: appchainMgr.AppchainAvailable,
|
||||||
ChainType: "appchain",
|
ChainType: "appchain",
|
||||||
Desc: "Relay1",
|
Desc: "Relay1",
|
||||||
Version: "1",
|
Version: "1",
|
||||||
|
@ -515,12 +596,6 @@ func TestInterchainManager_HandleIBTP(t *testing.T) {
|
||||||
From: from,
|
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)
|
res := im.HandleIBTP(ibtp)
|
||||||
assert.False(t, res.Ok)
|
assert.False(t, res.Ok)
|
||||||
assert.Equal(t, "this appchain does not exist", string(res.Result))
|
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()
|
data0, err := interchain.Marshal()
|
||||||
assert.Nil(t, err)
|
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+caller).Return(true, data0).AnyTimes()
|
||||||
mockStub.EXPECT().Get(appchainMgr.PREFIX+to).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().AddObject(gomock.Any(), gomock.Any()).AnyTimes()
|
||||||
mockStub.EXPECT().GetTxIndex().Return(uint64(1)).AnyTimes()
|
mockStub.EXPECT().GetTxIndex().Return(uint64(1)).AnyTimes()
|
||||||
mockStub.EXPECT().PostInterchainEvent(gomock.Any()).AnyTimes()
|
mockStub.EXPECT().PostInterchainEvent(gomock.Any()).AnyTimes()
|
||||||
|
@ -685,7 +775,7 @@ func TestInterchainManager_HandleIBTPs(t *testing.T) {
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
res := im.HandleIBTPs(data)
|
res := im.HandleIBTPs(data)
|
||||||
fmt.Printf("result is %v", string(res.Result))
|
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) {
|
func TestInterchainManager_HandleUnionIBTP(t *testing.T) {
|
||||||
|
@ -715,7 +805,7 @@ func TestInterchainManager_HandleUnionIBTP(t *testing.T) {
|
||||||
ID: from,
|
ID: from,
|
||||||
Name: "appchain" + from,
|
Name: "appchain" + from,
|
||||||
Validators: "",
|
Validators: "",
|
||||||
ConsensusType: int32(1),
|
ConsensusType: "",
|
||||||
ChainType: "fabric",
|
ChainType: "fabric",
|
||||||
Desc: "",
|
Desc: "",
|
||||||
Version: "",
|
Version: "",
|
||||||
|
@ -742,6 +832,7 @@ func TestInterchainManager_HandleUnionIBTP(t *testing.T) {
|
||||||
data, err := json.Marshal(relayChain)
|
data, err := json.Marshal(relayChain)
|
||||||
assert.Nil(t, err)
|
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().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().CrossInvoke(gomock.Any(), gomock.Any(), gomock.Any()).Return(boltvm.Success(data)).AnyTimes()
|
||||||
mockStub.EXPECT().AddObject(gomock.Any(), gomock.Any()).AnyTimes()
|
mockStub.EXPECT().AddObject(gomock.Any(), gomock.Any()).AnyTimes()
|
||||||
|
@ -990,6 +1081,38 @@ func TestRole_GetRoleWeight(t *testing.T) {
|
||||||
assert.False(t, res.Ok)
|
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) {
|
func TestRuleManager_RegisterRule(t *testing.T) {
|
||||||
mockCtl := gomock.NewController(t)
|
mockCtl := gomock.NewController(t)
|
||||||
mockStub := mock_stub.NewMockStub(mockCtl)
|
mockStub := mock_stub.NewMockStub(mockCtl)
|
||||||
|
@ -997,8 +1120,8 @@ func TestRuleManager_RegisterRule(t *testing.T) {
|
||||||
id0 := types.NewAddress([]byte{0}).String()
|
id0 := types.NewAddress([]byte{0}).String()
|
||||||
id1 := types.NewAddress([]byte{1}).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(), "IsAvailable", 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(id1)).Return(boltvm.Error(""))
|
||||||
mockStub.EXPECT().SetObject(gomock.Any(), gomock.Any()).AnyTimes()
|
mockStub.EXPECT().SetObject(gomock.Any(), gomock.Any()).AnyTimes()
|
||||||
|
|
||||||
im := &RuleManager{mockStub}
|
im := &RuleManager{mockStub}
|
||||||
|
@ -1009,7 +1132,6 @@ func TestRuleManager_RegisterRule(t *testing.T) {
|
||||||
|
|
||||||
res = im.RegisterRule(id1, addr)
|
res = im.RegisterRule(id1, addr)
|
||||||
assert.False(t, res.Ok)
|
assert.False(t, res.Ok)
|
||||||
assert.Equal(t, "this appchain does not exist", string(res.Result))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRuleManager_GetRuleAddress(t *testing.T) {
|
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().CrossInvoke(constant.RoleContractAddr.String(), "GetAdminRoles").Return(boltvm.Success(adminsData)).AnyTimes()
|
||||||
mockStub.EXPECT().GetObject(gomock.Any(), gomock.Any()).Return(false).AnyTimes()
|
mockStub.EXPECT().GetObject(gomock.Any(), gomock.Any()).Return(false).AnyTimes()
|
||||||
mockStub.EXPECT().AddObject(gomock.Any(), gomock.Any()).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{})
|
res := g.SubmitProposal("", "des", string(AppchainMgr), []byte{})
|
||||||
assert.False(t, res.Ok, string(res.Result))
|
assert.False(t, res.Ok, string(res.Result))
|
||||||
// GetAdminRoles error
|
// GetAdminRoles error
|
||||||
|
|
|
@ -76,6 +76,20 @@ type Proposal struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Governance) SubmitProposal(from, des string, typ string, extra []byte) *boltvm.Response {
|
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)
|
ret, err := g.getProposalsByFrom(from)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return boltvm.Error(err.Error())
|
return boltvm.Error(err.Error())
|
||||||
|
@ -103,9 +117,6 @@ func (g *Governance) SubmitProposal(from, des string, typ string, extra []byte)
|
||||||
ThresholdNum: tn,
|
ThresholdNum: tn,
|
||||||
Extra: extra,
|
Extra: extra,
|
||||||
}
|
}
|
||||||
if err := checkProposalInfo(p); err != nil {
|
|
||||||
return boltvm.Error(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
g.AddObject(ProposalKey(p.Id), *p)
|
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) {
|
func (g *Governance) getElectorateNum() (uint64, error) {
|
||||||
res := g.CrossInvoke(constant.RoleContractAddr.String(), "GetAdminRoles")
|
res := g.CrossInvoke(constant.RoleContractAddr.String(), "GetAdminRoles")
|
||||||
if !res.Ok {
|
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
|
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)
|
p.ApproveNum = p.ApproveNum + uint64(num)
|
||||||
case BallotReject:
|
case BallotReject:
|
||||||
p.AgainstNum = p.AgainstNum + uint64(num)
|
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)
|
g.SetObject(ProposalKey(p.Id), *p)
|
||||||
|
|
|
@ -108,9 +108,9 @@ func (x *InterchainManager) HandleIBTP(ibtp *pb.IBTP) *boltvm.Response {
|
||||||
return x.handleUnionIBTP(ibtp)
|
return x.handleUnionIBTP(ibtp)
|
||||||
}
|
}
|
||||||
|
|
||||||
interchain, ok := x.getInterchain(ibtp.From)
|
interchain, _, err := x.checkAppchain(ibtp.From)
|
||||||
if !ok {
|
if err != nil {
|
||||||
return boltvm.Error("this appchain does not exist")
|
return boltvm.Error(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := x.checkIBTP(ibtp, interchain); err != nil {
|
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 {
|
func (x *InterchainManager) HandleIBTPs(data []byte) *boltvm.Response {
|
||||||
ok := x.Has(AppchainKey(x.Caller()))
|
interchain, _, err := x.checkAppchain(x.Caller())
|
||||||
if !ok {
|
if err != nil {
|
||||||
return boltvm.Error("this appchain does not exist")
|
return boltvm.Error(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
ibtps := &pb.IBTPs{}
|
ibtps := &pb.IBTPs{}
|
||||||
|
@ -149,9 +149,6 @@ func (x *InterchainManager) HandleIBTPs(data []byte) *boltvm.Response {
|
||||||
return boltvm.Error(err.Error())
|
return boltvm.Error(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
interchain := &pb.Interchain{}
|
|
||||||
x.GetObject(AppchainKey(x.Caller()), interchain)
|
|
||||||
|
|
||||||
for _, ibtp := range ibtps.Ibtps {
|
for _, ibtp := range ibtps.Ibtps {
|
||||||
if err := x.checkIBTP(ibtp, interchain); err != nil {
|
if err := x.checkIBTP(ibtp, interchain); err != nil {
|
||||||
return boltvm.Error(err.Error())
|
return boltvm.Error(err.Error())
|
||||||
|
@ -217,6 +214,29 @@ func (x *InterchainManager) checkIBTP(ibtp *pb.IBTP, interchain *pb.Interchain)
|
||||||
return nil
|
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
|
// isRelayIBTP returns whether ibtp.from is relaychain type
|
||||||
func (x *InterchainManager) isRelayIBTP(from string) bool {
|
func (x *InterchainManager) isRelayIBTP(from string) bool {
|
||||||
srcChain := &appchainMgr.Appchain{}
|
srcChain := &appchainMgr.Appchain{}
|
||||||
|
@ -328,9 +348,10 @@ func (x *InterchainManager) GetIBTPByID(id string) *boltvm.Response {
|
||||||
|
|
||||||
func (x *InterchainManager) handleUnionIBTP(ibtp *pb.IBTP) *boltvm.Response {
|
func (x *InterchainManager) handleUnionIBTP(ibtp *pb.IBTP) *boltvm.Response {
|
||||||
srcRelayChainID := strings.Split(ibtp.From, "-")[0]
|
srcRelayChainID := strings.Split(ibtp.From, "-")[0]
|
||||||
ok := x.Has(AppchainKey(srcRelayChainID))
|
|
||||||
if !ok {
|
_, app, err := x.checkAppchain(srcRelayChainID)
|
||||||
return boltvm.Error("this relay chain does not exist")
|
if err != nil {
|
||||||
|
return boltvm.Error(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if ibtp.To == "" {
|
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))
|
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)
|
interchain, ok := x.getInterchain(ibtp.From)
|
||||||
if !ok {
|
if !ok {
|
||||||
interchain = &pb.Interchain{
|
interchain = &pb.Interchain{
|
||||||
|
|
|
@ -36,16 +36,20 @@ func (r *Role) GetRole() *boltvm.Response {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Role) IsAdmin(address string) *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
|
var admins []*repo.Admin
|
||||||
r.GetObject(adminRolesKey, &admins)
|
r.GetObject(adminRolesKey, &admins)
|
||||||
|
|
||||||
for _, admin := range admins {
|
for _, admin := range admins {
|
||||||
if admin.Address == address {
|
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 {
|
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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,9 +32,8 @@ type ruleRecord struct {
|
||||||
|
|
||||||
// SetRule can map the validation rule address with the chain id
|
// SetRule can map the validation rule address with the chain id
|
||||||
func (r *RuleManager) RegisterRule(id string, address string) *boltvm.Response {
|
func (r *RuleManager) RegisterRule(id string, address string) *boltvm.Response {
|
||||||
|
if res := r.CrossInvoke(constant.AppchainMgrContractAddr.String(), "IsAvailable", pb.String(id)); !res.Ok {
|
||||||
if res := r.CrossInvoke(constant.AppchainMgrContractAddr.String(), "GetAppchain", pb.String(id)); !res.Ok {
|
return boltvm.Error(string(res.Result))
|
||||||
return boltvm.Error("this appchain does not exist")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rl := &Rule{
|
rl := &Rule{
|
||||||
|
|
|
@ -200,6 +200,8 @@ func TestBlockExecutor_ApplyReadonlyTransactions(t *testing.T) {
|
||||||
mockLedger.EXPECT().PersistExecutionResult(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
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().FlushDirtyDataAndComputeJournal().Return(make(map[string]*ledger.Account), &ledger.BlockJournal{}).AnyTimes()
|
||||||
mockLedger.EXPECT().PersistBlockData(gomock.Any()).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")
|
logger := log.NewWithModule("executor")
|
||||||
|
|
||||||
exec, err := New(mockLedger, logger, executorType)
|
exec, err := New(mockLedger, logger, executorType)
|
||||||
|
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"github.com/meshplus/bitxhub/pkg/vm"
|
"github.com/meshplus/bitxhub/pkg/vm"
|
||||||
"github.com/meshplus/bitxhub/pkg/vm/boltvm"
|
"github.com/meshplus/bitxhub/pkg/vm/boltvm"
|
||||||
"github.com/meshplus/bitxhub/pkg/vm/wasm"
|
"github.com/meshplus/bitxhub/pkg/vm/wasm"
|
||||||
|
"github.com/meshplus/bitxhub/pkg/vm/wasm/vmledger"
|
||||||
"github.com/sirupsen/logrus"
|
"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) {
|
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() {
|
if tx.IsIBTP() {
|
||||||
ctx := vm.NewContext(tx, uint64(i), nil, exec.ledger, exec.logger)
|
ctx := vm.NewContext(tx, uint64(i), nil, exec.ledger, exec.logger)
|
||||||
instance := boltvm.New(ctx, exec.validationEngine, exec.getContracts(opt))
|
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))
|
instance = boltvm.New(ctx, exec.validationEngine, exec.getContracts(opt))
|
||||||
case pb.TransactionData_XVM:
|
case pb.TransactionData_XVM:
|
||||||
ctx := vm.NewContext(tx, uint64(i), data, exec.ledger, exec.logger)
|
ctx := vm.NewContext(tx, uint64(i), data, exec.ledger, exec.logger)
|
||||||
imports, err := wasm.EmptyImports()
|
imports, err := vmledger.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,9 @@ func (ac *AccountCache) addToReadCache(accounts map[string]*Account) error {
|
||||||
for addr, account := range accounts {
|
for addr, account := range accounts {
|
||||||
var stateCache *lru.Cache
|
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)
|
value, ok := ac.stateCache.Get(addr)
|
||||||
if ok {
|
if ok {
|
||||||
stateCache = value.(*lru.Cache)
|
stateCache = value.(*lru.Cache)
|
||||||
|
@ -91,7 +93,9 @@ func (ac *AccountCache) addToWriteBuffer(accounts map[string]*Account) {
|
||||||
defer ac.rwLock.Unlock()
|
defer ac.rwLock.Unlock()
|
||||||
|
|
||||||
for addr, account := range accounts {
|
for addr, account := range accounts {
|
||||||
ac.innerAccounts[addr] = account.dirtyAccount
|
if account.dirtyAccount != nil {
|
||||||
|
ac.innerAccounts[addr] = account.dirtyAccount
|
||||||
|
}
|
||||||
stateMap, ok := ac.states[addr]
|
stateMap, ok := ac.states[addr]
|
||||||
if !ok {
|
if !ok {
|
||||||
stateMap = make(map[string][]byte)
|
stateMap = make(map[string][]byte)
|
||||||
|
|
|
@ -4,25 +4,27 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/meshplus/bitxhub-kit/crypto"
|
"github.com/meshplus/bitxhub-kit/crypto"
|
||||||
|
"github.com/meshplus/bitxhub-kit/types"
|
||||||
"github.com/meshplus/bitxhub-model/pb"
|
"github.com/meshplus/bitxhub-model/pb"
|
||||||
"github.com/meshplus/bitxhub/pkg/peermgr"
|
"github.com/meshplus/bitxhub/pkg/peermgr"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
ID uint64
|
ID uint64
|
||||||
IsNew bool
|
IsNew bool
|
||||||
RepoRoot string
|
RepoRoot string
|
||||||
StoragePath string
|
StoragePath string
|
||||||
PluginPath string
|
PluginPath string
|
||||||
PeerMgr peermgr.PeerManager
|
PeerMgr peermgr.PeerManager
|
||||||
PrivKey crypto.PrivateKey
|
PrivKey crypto.PrivateKey
|
||||||
Logger logrus.FieldLogger
|
Logger logrus.FieldLogger
|
||||||
Nodes map[uint64]*pb.VpInfo
|
Nodes map[uint64]*pb.VpInfo
|
||||||
Applied uint64
|
Applied uint64
|
||||||
Digest string
|
Digest string
|
||||||
GetChainMetaFunc func() *pb.ChainMeta
|
GetChainMetaFunc func() *pb.ChainMeta
|
||||||
GetBlockByHeight func(height uint64) (*pb.Block, error)
|
GetBlockByHeight func(height uint64) (*pb.Block, error)
|
||||||
|
GetAccountNonce func(address *types.Address) uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
type Option func(*Config)
|
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 {
|
func checkConfig(config *Config) error {
|
||||||
if config.Logger == nil {
|
if config.Logger == nil {
|
||||||
return fmt.Errorf("logger is nil")
|
return fmt.Errorf("logger is nil")
|
||||||
|
|
|
@ -93,10 +93,11 @@ func NewNode(opts ...order.Option) (order.Order, error) {
|
||||||
return nil, fmt.Errorf("generate raft txpool config: %w", err)
|
return nil, fmt.Errorf("generate raft txpool config: %w", err)
|
||||||
}
|
}
|
||||||
mempoolConf := &mempool.Config{
|
mempoolConf := &mempool.Config{
|
||||||
ID: config.ID,
|
ID: config.ID,
|
||||||
ChainHeight: config.Applied,
|
ChainHeight: config.Applied,
|
||||||
Logger: config.Logger,
|
Logger: config.Logger,
|
||||||
StoragePath: config.StoragePath,
|
StoragePath: config.StoragePath,
|
||||||
|
GetAccountNonce: config.GetAccountNonce,
|
||||||
|
|
||||||
BatchSize: raftConfig.RAFT.MempoolConfig.BatchSize,
|
BatchSize: raftConfig.RAFT.MempoolConfig.BatchSize,
|
||||||
PoolSize: raftConfig.RAFT.MempoolConfig.PoolSize,
|
PoolSize: raftConfig.RAFT.MempoolConfig.PoolSize,
|
||||||
|
|
|
@ -55,6 +55,9 @@ func TestNode_Start(t *testing.T) {
|
||||||
order.WithStoragePath(repo.GetStoragePath(repoRoot, "order")),
|
order.WithStoragePath(repo.GetStoragePath(repoRoot, "order")),
|
||||||
order.WithLogger(log.NewWithModule("consensus")),
|
order.WithLogger(log.NewWithModule("consensus")),
|
||||||
order.WithApplied(1),
|
order.WithApplied(1),
|
||||||
|
order.WithGetAccountNonceFunc(func(address *types.Address) uint64 {
|
||||||
|
return 0
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
@ -111,6 +114,9 @@ func TestMulti_Node_Start(t *testing.T) {
|
||||||
order.WithLogger(log.NewWithModule("consensus")),
|
order.WithLogger(log.NewWithModule("consensus")),
|
||||||
order.WithGetBlockByHeightFunc(nil),
|
order.WithGetBlockByHeightFunc(nil),
|
||||||
order.WithApplied(1),
|
order.WithApplied(1),
|
||||||
|
order.WithGetAccountNonceFunc(func(address *types.Address) uint64 {
|
||||||
|
return 0
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
err = order.Start()
|
err = order.Start()
|
||||||
|
@ -166,6 +172,9 @@ func TestMulti_Node_Start_Without_Cert_Verification(t *testing.T) {
|
||||||
order.WithLogger(log.NewWithModule("consensus")),
|
order.WithLogger(log.NewWithModule("consensus")),
|
||||||
order.WithGetBlockByHeightFunc(nil),
|
order.WithGetBlockByHeightFunc(nil),
|
||||||
order.WithApplied(1),
|
order.WithApplied(1),
|
||||||
|
order.WithGetAccountNonceFunc(func(address *types.Address) uint64 {
|
||||||
|
return 0
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
err = order.Start()
|
err = order.Start()
|
||||||
|
|
|
@ -69,6 +69,9 @@ func mockRaftNode(t *testing.T) (*Node, error) {
|
||||||
TxSliceSize: raftConfig.RAFT.MempoolConfig.TxSliceSize,
|
TxSliceSize: raftConfig.RAFT.MempoolConfig.TxSliceSize,
|
||||||
TxSliceTimeout: raftConfig.RAFT.MempoolConfig.TxSliceTimeout,
|
TxSliceTimeout: raftConfig.RAFT.MempoolConfig.TxSliceTimeout,
|
||||||
StoragePath: filepath.Join(repoRoot, "storage"),
|
StoragePath: filepath.Join(repoRoot, "storage"),
|
||||||
|
GetAccountNonce: func(address *types.Address) uint64 {
|
||||||
|
return 0
|
||||||
|
},
|
||||||
}
|
}
|
||||||
mempoolInst, err := mempool.NewMempool(mempoolConf)
|
mempoolInst, err := mempool.NewMempool(mempoolConf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
package mempool
|
package mempool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -29,17 +27,13 @@ type mempoolImpl struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMempoolImpl(config *Config) (*mempoolImpl, error) {
|
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{
|
mpi := &mempoolImpl{
|
||||||
localID: config.ID,
|
localID: config.ID,
|
||||||
batchSeqNo: config.ChainHeight,
|
batchSeqNo: config.ChainHeight,
|
||||||
logger: config.Logger,
|
logger: config.Logger,
|
||||||
txSliceSize: config.TxSliceSize,
|
txSliceSize: config.TxSliceSize,
|
||||||
}
|
}
|
||||||
mpi.txStore = newTransactionStore(db, config.Logger)
|
mpi.txStore = newTransactionStore(config.GetAccountNonce, config.Logger)
|
||||||
if config.BatchSize == 0 {
|
if config.BatchSize == 0 {
|
||||||
mpi.batchSize = DefaultBatchSize
|
mpi.batchSize = DefaultBatchSize
|
||||||
} else {
|
} else {
|
||||||
|
@ -162,7 +156,7 @@ func (mpi *mempoolImpl) generateBlock() (*raftproto.RequestBatch, error) {
|
||||||
}
|
}
|
||||||
// include transaction if it's "next" for given account or
|
// include transaction if it's "next" for given account or
|
||||||
// we've already sent its ancestor to Consensus
|
// we've already sent its ancestor to Consensus
|
||||||
if seenPrevious || (txSeq == commitNonce) {
|
if seenPrevious || (txSeq == commitNonce+1) {
|
||||||
ptr := orderedIndexKey{account: tx.account, nonce: txSeq}
|
ptr := orderedIndexKey{account: tx.account, nonce: txSeq}
|
||||||
mpi.txStore.batchedTxs[ptr] = true
|
mpi.txStore.batchedTxs[ptr] = true
|
||||||
result = append(result, ptr)
|
result = append(result, ptr)
|
||||||
|
@ -229,7 +223,7 @@ func (mpi *mempoolImpl) processCommitTransactions(state *ChainState) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
preCommitNonce := mpi.txStore.nonceCache.getCommitNonce(txPointer.account)
|
preCommitNonce := mpi.txStore.nonceCache.getCommitNonce(txPointer.account)
|
||||||
newCommitNonce := txPointer.nonce + 1
|
newCommitNonce := txPointer.nonce
|
||||||
if updateAccounts[txPointer.account] < newCommitNonce && preCommitNonce < newCommitNonce {
|
if updateAccounts[txPointer.account] < newCommitNonce && preCommitNonce < newCommitNonce {
|
||||||
updateAccounts[txPointer.account] = newCommitNonce
|
updateAccounts[txPointer.account] = newCommitNonce
|
||||||
}
|
}
|
||||||
|
@ -245,7 +239,7 @@ func (mpi *mempoolImpl) processCommitTransactions(state *ChainState) {
|
||||||
commitNonce := mpi.txStore.nonceCache.getCommitNonce(account)
|
commitNonce := mpi.txStore.nonceCache.getCommitNonce(account)
|
||||||
if list, ok := mpi.txStore.allTxs[account]; ok {
|
if list, ok := mpi.txStore.allTxs[account]; ok {
|
||||||
// remove all previous seq number txs for this account.
|
// 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.
|
// remove index smaller than commitNonce delete index.
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(4)
|
wg.Add(4)
|
||||||
|
|
|
@ -153,9 +153,9 @@ func TestProcessTransactions(t *testing.T) {
|
||||||
ast.Equal(5, len(mpi.txStore.txHashMap))
|
ast.Equal(5, len(mpi.txStore.txHashMap))
|
||||||
ast.Equal(2, mpi.txStore.allTxs[account1.String()].index.size())
|
ast.Equal(2, mpi.txStore.allTxs[account1.String()].index.size())
|
||||||
ast.Equal(3, mpi.txStore.allTxs[account2.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(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()))
|
ast.Equal(uint64(3), mpi.txStore.nonceCache.getPendingNonce(account2.String()))
|
||||||
|
|
||||||
mpi.batchSize = 4
|
mpi.batchSize = 4
|
||||||
|
@ -231,9 +231,9 @@ func TestUnorderedIncomingTxs(t *testing.T) {
|
||||||
ast.Equal(6, len(mpi.txStore.txHashMap))
|
ast.Equal(6, len(mpi.txStore.txHashMap))
|
||||||
ast.Equal(3, mpi.txStore.allTxs[account1.String()].index.size())
|
ast.Equal(3, mpi.txStore.allTxs[account1.String()].index.size())
|
||||||
ast.Equal(3, mpi.txStore.allTxs[account2.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(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()))
|
ast.Equal(uint64(3), mpi.txStore.nonceCache.getPendingNonce(account2.String()))
|
||||||
|
|
||||||
tx7 := constructTx(uint64(3), &privKey1)
|
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(1, mpi.txStore.allTxs[account1.String()].index.size())
|
||||||
ast.Equal(2, mpi.txStore.allTxs[account2.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(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()))
|
ast.Equal(uint64(3), mpi.txStore.nonceCache.getPendingNonce(account2.String()))
|
||||||
|
|
||||||
// generate block3
|
// generate block3
|
||||||
|
@ -298,9 +298,9 @@ func TestUnorderedIncomingTxs(t *testing.T) {
|
||||||
ast.Equal(7, len(mpi.txStore.txHashMap))
|
ast.Equal(7, len(mpi.txStore.txHashMap))
|
||||||
ast.Equal(3, mpi.txStore.allTxs[account1.String()].index.size())
|
ast.Equal(3, mpi.txStore.allTxs[account1.String()].index.size())
|
||||||
ast.Equal(4, mpi.txStore.allTxs[account2.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(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()))
|
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(8, len(mpi.txStore.txHashMap))
|
||||||
ast.Equal(4, mpi.txStore.allTxs[account1.String()].index.size())
|
ast.Equal(4, mpi.txStore.allTxs[account1.String()].index.size())
|
||||||
ast.Equal(4, mpi.txStore.allTxs[account2.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(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()))
|
ast.Equal(uint64(3), mpi.txStore.nonceCache.getPendingNonce(account2.String()))
|
||||||
|
|
||||||
tx1 := constructTx(3, &privKey1)
|
tx1 := constructTx(3, &privKey1)
|
||||||
|
@ -407,7 +407,7 @@ func TestRestore(t *testing.T) {
|
||||||
nonce := mpi.GetPendingNonceByAccount(account1.String())
|
nonce := mpi.GetPendingNonceByAccount(account1.String())
|
||||||
ast.Equal(uint64(1), nonce)
|
ast.Equal(uint64(1), nonce)
|
||||||
privKey2 := genPrivKey()
|
privKey2 := genPrivKey()
|
||||||
account2, _ := privKey1.PublicKey().Address()
|
//account2, _ := privKey1.PublicKey().Address()
|
||||||
tx1 := constructTx(uint64(1), &privKey1)
|
tx1 := constructTx(uint64(1), &privKey1)
|
||||||
tx2 := constructTx(uint64(2), &privKey1)
|
tx2 := constructTx(uint64(2), &privKey1)
|
||||||
tx3 := constructTx(uint64(1), &privKey2)
|
tx3 := constructTx(uint64(1), &privKey2)
|
||||||
|
@ -443,10 +443,10 @@ func TestRestore(t *testing.T) {
|
||||||
ast.Equal(1, mpi.txStore.parkingLotIndex.size())
|
ast.Equal(1, mpi.txStore.parkingLotIndex.size())
|
||||||
|
|
||||||
// stop and restore
|
// stop and restore
|
||||||
ast.Nil(mpi.txStore.nonceCache.fallback.Close())
|
//ast.Nil(mpi.txStore.nonceCache.fallback.Close())
|
||||||
newMpi, _ := mockMempoolImpl(storePath)
|
//newMpi, _ := mockMempoolImpl(storePath)
|
||||||
ast.Equal(uint64(3), newMpi.txStore.nonceCache.getCommitNonce(account1.String()))
|
//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.getCommitNonce(account2.String()))
|
||||||
ast.Equal(uint64(3), newMpi.txStore.nonceCache.getPendingNonce(account1.String()))
|
//ast.Equal(uint64(3), newMpi.txStore.nonceCache.getPendingNonce(account1.String()))
|
||||||
ast.Equal(uint64(3), newMpi.txStore.nonceCache.getPendingNonce(account2.String()))
|
//ast.Equal(uint64(3), newMpi.txStore.nonceCache.getPendingNonce(account2.String()))
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,16 +21,21 @@ const (
|
||||||
DefaultTestTxSetSize = uint64(1)
|
DefaultTestTxSetSize = uint64(1)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func mockGetAccountNonce(address *types.Address) uint64 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
func mockMempoolImpl(path string) (*mempoolImpl, chan *raftproto.Ready) {
|
func mockMempoolImpl(path string) (*mempoolImpl, chan *raftproto.Ready) {
|
||||||
config := &Config{
|
config := &Config{
|
||||||
ID: 1,
|
ID: 1,
|
||||||
ChainHeight: DefaultTestChainHeight,
|
ChainHeight: DefaultTestChainHeight,
|
||||||
BatchSize: DefaultTestBatchSize,
|
BatchSize: DefaultTestBatchSize,
|
||||||
PoolSize: DefaultPoolSize,
|
PoolSize: DefaultPoolSize,
|
||||||
TxSliceSize: DefaultTestTxSetSize,
|
TxSliceSize: DefaultTestTxSetSize,
|
||||||
TxSliceTimeout: DefaultTxSetTick,
|
TxSliceTimeout: DefaultTxSetTick,
|
||||||
Logger: log.NewWithModule("consensus"),
|
Logger: log.NewWithModule("consensus"),
|
||||||
StoragePath: path,
|
StoragePath: path,
|
||||||
|
GetAccountNonce: mockGetAccountNonce,
|
||||||
}
|
}
|
||||||
proposalC := make(chan *raftproto.Ready)
|
proposalC := make(chan *raftproto.Ready)
|
||||||
mempool, _ := newMempoolImpl(config)
|
mempool, _ := newMempoolImpl(config)
|
||||||
|
|
|
@ -1,16 +1,13 @@
|
||||||
package mempool
|
package mempool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"math"
|
"math"
|
||||||
"strconv"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
"github.com/google/btree"
|
"github.com/google/btree"
|
||||||
"github.com/meshplus/bitxhub-kit/storage"
|
"github.com/meshplus/bitxhub-kit/types"
|
||||||
"github.com/meshplus/bitxhub-model/pb"
|
"github.com/meshplus/bitxhub-model/pb"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
type transactionStore struct {
|
type transactionStore struct {
|
||||||
|
@ -35,7 +32,7 @@ type transactionStore struct {
|
||||||
priorityNonBatchSize uint64
|
priorityNonBatchSize uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTransactionStore(db storage.Storage, logger logrus.FieldLogger) *transactionStore {
|
func newTransactionStore(f GetAccountNonceFunc, logger logrus.FieldLogger) *transactionStore {
|
||||||
return &transactionStore{
|
return &transactionStore{
|
||||||
txHashMap: make(map[string]*orderedIndexKey, 0),
|
txHashMap: make(map[string]*orderedIndexKey, 0),
|
||||||
allTxs: make(map[string]*txSortedMap),
|
allTxs: make(map[string]*txSortedMap),
|
||||||
|
@ -43,7 +40,7 @@ func newTransactionStore(db storage.Storage, logger logrus.FieldLogger) *transac
|
||||||
parkingLotIndex: newBtreeIndex(),
|
parkingLotIndex: newBtreeIndex(),
|
||||||
priorityIndex: newBtreeIndex(),
|
priorityIndex: newBtreeIndex(),
|
||||||
ttlIndex: newTxLiveTimeMap(),
|
ttlIndex: newTxLiveTimeMap(),
|
||||||
nonceCache: newNonceCache(db, logger),
|
nonceCache: newNonceCache(f, logger),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,26 +156,27 @@ type nonceCache struct {
|
||||||
commitNonces map[string]uint64
|
commitNonces map[string]uint64
|
||||||
// pendingNonces records each account's latest nonce which has been included in
|
// pendingNonces records each account's latest nonce which has been included in
|
||||||
// priority queue. Invariant: pendingNonces[account] >= commitNonces[account]
|
// priority queue. Invariant: pendingNonces[account] >= commitNonces[account]
|
||||||
pendingNonces map[string]uint64
|
pendingNonces map[string]uint64
|
||||||
pendingMu sync.RWMutex
|
pendingMu sync.RWMutex
|
||||||
// falling back to reading from local database if an account is unknown.
|
getAccountNonce GetAccountNonceFunc
|
||||||
fallback storage.Storage
|
logger logrus.FieldLogger
|
||||||
logger logrus.FieldLogger
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newNonceCache(store storage.Storage, logger logrus.FieldLogger) *nonceCache {
|
func newNonceCache(f GetAccountNonceFunc, logger logrus.FieldLogger) *nonceCache {
|
||||||
return &nonceCache{
|
return &nonceCache{
|
||||||
commitNonces: make(map[string]uint64),
|
commitNonces: make(map[string]uint64),
|
||||||
pendingNonces: make(map[string]uint64),
|
pendingNonces: make(map[string]uint64),
|
||||||
fallback: store,
|
getAccountNonce: f,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (nc *nonceCache) getCommitNonce(account string) uint64 {
|
func (nc *nonceCache) getCommitNonce(account string) uint64 {
|
||||||
nonce, ok := nc.commitNonces[account]
|
nonce, ok := nc.commitNonces[account]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nc.getNonceFromDB(committedNonceKey(account))
|
cn := nc.getAccountNonce(types.NewAddressByStr(account))
|
||||||
|
nc.commitNonces[account] = cn
|
||||||
|
return cn
|
||||||
}
|
}
|
||||||
return nonce
|
return nonce
|
||||||
}
|
}
|
||||||
|
@ -194,7 +192,7 @@ func (nc *nonceCache) getPendingNonce(account string) uint64 {
|
||||||
if !ok {
|
if !ok {
|
||||||
// if nonce is unknown, check if there is committed nonce persisted in db
|
// 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
|
// 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
|
return nonce
|
||||||
}
|
}
|
||||||
|
@ -212,31 +210,11 @@ func (nc *nonceCache) updatePendingNonce(newPending map[string]uint64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (nc *nonceCache) updateCommittedNonce(newCommitted map[string]uint64) {
|
func (nc *nonceCache) updateCommittedNonce(newCommitted map[string]uint64) {
|
||||||
storageBatch := nc.fallback.NewBatch()
|
|
||||||
defer storageBatch.Commit()
|
|
||||||
for account, committedNonce := range newCommitted {
|
for account, committedNonce := range newCommitted {
|
||||||
nc.setCommitNonce(account, committedNonce)
|
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
|
// since the live time field in sortedTtlKey may vary during process
|
||||||
// we need to track the latest live time since its latest broadcast.
|
// we need to track the latest live time since its latest broadcast.
|
||||||
type txLiveTimeMap struct {
|
type txLiveTimeMap struct {
|
||||||
|
|
|
@ -20,6 +20,8 @@ const (
|
||||||
DefaultTxSetTick = 100 * time.Millisecond
|
DefaultTxSetTick = 100 * time.Millisecond
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type GetAccountNonceFunc func(address *types.Address) uint64
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
ID uint64
|
ID uint64
|
||||||
BatchSize uint64
|
BatchSize uint64
|
||||||
|
@ -30,6 +32,7 @@ type Config struct {
|
||||||
ChainHeight uint64
|
ChainHeight uint64
|
||||||
Logger logrus.FieldLogger
|
Logger logrus.FieldLogger
|
||||||
StoragePath string // db for persist mem pool meta data
|
StoragePath string // db for persist mem pool meta data
|
||||||
|
GetAccountNonce GetAccountNonceFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
type txItem struct {
|
type txItem struct {
|
||||||
|
|
|
@ -98,10 +98,11 @@ func NewNode(opts ...order.Option) (order.Order, error) {
|
||||||
|
|
||||||
batchTimeout, memConfig, err := generateSoloConfig(config.RepoRoot)
|
batchTimeout, memConfig, err := generateSoloConfig(config.RepoRoot)
|
||||||
mempoolConf := &mempool.Config{
|
mempoolConf := &mempool.Config{
|
||||||
ID: config.ID,
|
ID: config.ID,
|
||||||
ChainHeight: config.Applied,
|
ChainHeight: config.Applied,
|
||||||
Logger: config.Logger,
|
Logger: config.Logger,
|
||||||
StoragePath: config.StoragePath,
|
StoragePath: config.StoragePath,
|
||||||
|
GetAccountNonce: config.GetAccountNonce,
|
||||||
|
|
||||||
BatchSize: memConfig.BatchSize,
|
BatchSize: memConfig.BatchSize,
|
||||||
PoolSize: memConfig.PoolSize,
|
PoolSize: memConfig.PoolSize,
|
||||||
|
|
|
@ -54,6 +54,9 @@ func TestNode_Start(t *testing.T) {
|
||||||
order.WithID(1),
|
order.WithID(1),
|
||||||
order.WithNodes(nodes),
|
order.WithNodes(nodes),
|
||||||
order.WithApplied(1),
|
order.WithApplied(1),
|
||||||
|
order.WithGetAccountNonceFunc(func(address *types.Address) uint64 {
|
||||||
|
return 0
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
package mock_peermgr
|
package mock_peermgr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
reflect "reflect"
|
||||||
|
|
||||||
event "github.com/ethereum/go-ethereum/event"
|
event "github.com/ethereum/go-ethereum/event"
|
||||||
gomock "github.com/golang/mock/gomock"
|
gomock "github.com/golang/mock/gomock"
|
||||||
peer "github.com/libp2p/go-libp2p-core/peer"
|
peer "github.com/libp2p/go-libp2p-core/peer"
|
||||||
|
@ -12,61 +14,44 @@ import (
|
||||||
events "github.com/meshplus/bitxhub/internal/model/events"
|
events "github.com/meshplus/bitxhub/internal/model/events"
|
||||||
peermgr "github.com/meshplus/bitxhub/pkg/peermgr"
|
peermgr "github.com/meshplus/bitxhub/pkg/peermgr"
|
||||||
network "github.com/meshplus/go-lightp2p"
|
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 {
|
type MockPeerManager struct {
|
||||||
ctrl *gomock.Controller
|
ctrl *gomock.Controller
|
||||||
recorder *MockPeerManagerMockRecorder
|
recorder *MockPeerManagerMockRecorder
|
||||||
}
|
}
|
||||||
|
|
||||||
// MockPeerManagerMockRecorder is the mock recorder for MockPeerManager
|
// MockPeerManagerMockRecorder is the mock recorder for MockPeerManager.
|
||||||
type MockPeerManagerMockRecorder struct {
|
type MockPeerManagerMockRecorder struct {
|
||||||
mock *MockPeerManager
|
mock *MockPeerManager
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMockPeerManager creates a new mock instance
|
// NewMockPeerManager creates a new mock instance.
|
||||||
func NewMockPeerManager(ctrl *gomock.Controller) *MockPeerManager {
|
func NewMockPeerManager(ctrl *gomock.Controller) *MockPeerManager {
|
||||||
mock := &MockPeerManager{ctrl: ctrl}
|
mock := &MockPeerManager{ctrl: ctrl}
|
||||||
mock.recorder = &MockPeerManagerMockRecorder{mock}
|
mock.recorder = &MockPeerManagerMockRecorder{mock}
|
||||||
return 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 {
|
func (m *MockPeerManager) EXPECT() *MockPeerManagerMockRecorder {
|
||||||
return m.recorder
|
return m.recorder
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start mocks base method
|
// AddNode mocks base method.
|
||||||
func (m *MockPeerManager) Start() error {
|
func (m *MockPeerManager) AddNode(newNodeID uint64, vpInfo *pb.VpInfo) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "Start")
|
m.ctrl.Call(m, "AddNode", newNodeID, vpInfo)
|
||||||
ret0, _ := ret[0].(error)
|
|
||||||
return ret0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start indicates an expected call of Start
|
// AddNode indicates an expected call of AddNode.
|
||||||
func (mr *MockPeerManagerMockRecorder) Start() *gomock.Call {
|
func (mr *MockPeerManagerMockRecorder) AddNode(newNodeID, vpInfo interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
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
|
// AsyncSend 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
|
|
||||||
func (m *MockPeerManager) AsyncSend(arg0 uint64, arg1 *pb.Message) error {
|
func (m *MockPeerManager) AsyncSend(arg0 uint64, arg1 *pb.Message) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "AsyncSend", arg0, arg1)
|
ret := m.ctrl.Call(m, "AsyncSend", arg0, arg1)
|
||||||
|
@ -74,27 +59,107 @@ func (m *MockPeerManager) AsyncSend(arg0 uint64, arg1 *pb.Message) error {
|
||||||
return ret0
|
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 {
|
func (mr *MockPeerManagerMockRecorder) AsyncSend(arg0, arg1 interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AsyncSend", reflect.TypeOf((*MockPeerManager)(nil).AsyncSend), arg0, arg1)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AsyncSend", reflect.TypeOf((*MockPeerManager)(nil).AsyncSend), arg0, arg1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendWithStream mocks base method
|
// Broadcast mocks base method.
|
||||||
func (m *MockPeerManager) SendWithStream(arg0 network.Stream, arg1 *pb.Message) error {
|
func (m *MockPeerManager) Broadcast(arg0 *pb.Message) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "SendWithStream", arg0, arg1)
|
ret := m.ctrl.Call(m, "Broadcast", arg0)
|
||||||
ret0, _ := ret[0].(error)
|
ret0, _ := ret[0].(error)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendWithStream indicates an expected call of SendWithStream
|
// Broadcast indicates an expected call of Broadcast.
|
||||||
func (mr *MockPeerManagerMockRecorder) SendWithStream(arg0, arg1 interface{}) *gomock.Call {
|
func (mr *MockPeerManagerMockRecorder) Broadcast(arg0 interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
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) {
|
func (m *MockPeerManager) Send(arg0 uint64, arg1 *pb.Message) (*pb.Message, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "Send", arg0, arg1)
|
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
|
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 {
|
func (mr *MockPeerManagerMockRecorder) Send(arg0, arg1 interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Send", reflect.TypeOf((*MockPeerManager)(nil).Send), arg0, arg1)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Send", reflect.TypeOf((*MockPeerManager)(nil).Send), arg0, arg1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Broadcast mocks base method
|
// SendWithStream mocks base method.
|
||||||
func (m *MockPeerManager) Broadcast(arg0 *pb.Message) error {
|
func (m *MockPeerManager) SendWithStream(arg0 network.Stream, arg1 *pb.Message) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "Broadcast", arg0)
|
ret := m.ctrl.Call(m, "SendWithStream", arg0, arg1)
|
||||||
ret0, _ := ret[0].(error)
|
ret0, _ := ret[0].(error)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Broadcast indicates an expected call of Broadcast
|
// SendWithStream indicates an expected call of SendWithStream.
|
||||||
func (mr *MockPeerManagerMockRecorder) Broadcast(arg0 interface{}) *gomock.Call {
|
func (mr *MockPeerManagerMockRecorder) SendWithStream(arg0, arg1 interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
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
|
// Start mocks base method.
|
||||||
func (m *MockPeerManager) CountConnectedPeers() uint64 {
|
func (m *MockPeerManager) Start() error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "CountConnectedPeers")
|
ret := m.ctrl.Call(m, "Start")
|
||||||
ret0, _ := ret[0].(uint64)
|
ret0, _ := ret[0].(error)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// CountConnectedPeers indicates an expected call of CountConnectedPeers
|
// Start indicates an expected call of Start.
|
||||||
func (mr *MockPeerManagerMockRecorder) CountConnectedPeers() *gomock.Call {
|
func (mr *MockPeerManagerMockRecorder) Start() *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
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
|
// Stop mocks base method.
|
||||||
func (m *MockPeerManager) Peers() map[uint64]*pb.VpInfo {
|
func (m *MockPeerManager) Stop() error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "Peers")
|
ret := m.ctrl.Call(m, "Stop")
|
||||||
ret0, _ := ret[0].(map[uint64]*pb.VpInfo)
|
ret0, _ := ret[0].(error)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Peers indicates an expected call of Peers
|
// Stop indicates an expected call of Stop.
|
||||||
func (mr *MockPeerManagerMockRecorder) Peers() *gomock.Call {
|
func (mr *MockPeerManagerMockRecorder) Stop() *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
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
|
// SubscribeOrderMessage 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
|
|
||||||
func (m *MockPeerManager) SubscribeOrderMessage(ch chan<- events.OrderMessageEvent) event.Subscription {
|
func (m *MockPeerManager) SubscribeOrderMessage(ch chan<- events.OrderMessageEvent) event.Subscription {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "SubscribeOrderMessage", ch)
|
ret := m.ctrl.Call(m, "SubscribeOrderMessage", ch)
|
||||||
|
@ -173,37 +224,13 @@ func (m *MockPeerManager) SubscribeOrderMessage(ch chan<- events.OrderMessageEve
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubscribeOrderMessage indicates an expected call of SubscribeOrderMessage
|
// SubscribeOrderMessage indicates an expected call of SubscribeOrderMessage.
|
||||||
func (mr *MockPeerManagerMockRecorder) SubscribeOrderMessage(ch interface{}) *gomock.Call {
|
func (mr *MockPeerManagerMockRecorder) SubscribeOrderMessage(ch interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubscribeOrderMessage", reflect.TypeOf((*MockPeerManager)(nil).SubscribeOrderMessage), ch)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubscribeOrderMessage", reflect.TypeOf((*MockPeerManager)(nil).SubscribeOrderMessage), ch)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddNode mocks base method
|
// UpdateRouter 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
|
|
||||||
func (m *MockPeerManager) UpdateRouter(vpInfos map[uint64]*pb.VpInfo, isNew bool) bool {
|
func (m *MockPeerManager) UpdateRouter(vpInfos map[uint64]*pb.VpInfo, isNew bool) bool {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "UpdateRouter", vpInfos, isNew)
|
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
|
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 {
|
func (mr *MockPeerManagerMockRecorder) UpdateRouter(vpInfos, isNew interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateRouter", reflect.TypeOf((*MockPeerManager)(nil).UpdateRouter), vpInfos, isNew)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateRouter", reflect.TypeOf((*MockPeerManager)(nil).UpdateRouter), vpInfos, isNew)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disconnect mocks base method
|
// MockPierManager is a mock of PierManager interface.
|
||||||
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
|
|
||||||
type MockPierManager struct {
|
type MockPierManager struct {
|
||||||
ctrl *gomock.Controller
|
ctrl *gomock.Controller
|
||||||
recorder *MockPierManagerMockRecorder
|
recorder *MockPierManagerMockRecorder
|
||||||
}
|
}
|
||||||
|
|
||||||
// MockPierManagerMockRecorder is the mock recorder for MockPierManager
|
// MockPierManagerMockRecorder is the mock recorder for MockPierManager.
|
||||||
type MockPierManagerMockRecorder struct {
|
type MockPierManagerMockRecorder struct {
|
||||||
mock *MockPierManager
|
mock *MockPierManager
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMockPierManager creates a new mock instance
|
// NewMockPierManager creates a new mock instance.
|
||||||
func NewMockPierManager(ctrl *gomock.Controller) *MockPierManager {
|
func NewMockPierManager(ctrl *gomock.Controller) *MockPierManager {
|
||||||
mock := &MockPierManager{ctrl: ctrl}
|
mock := &MockPierManager{ctrl: ctrl}
|
||||||
mock.recorder = &MockPierManagerMockRecorder{mock}
|
mock.recorder = &MockPierManagerMockRecorder{mock}
|
||||||
return 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 {
|
func (m *MockPierManager) EXPECT() *MockPierManagerMockRecorder {
|
||||||
return m.recorder
|
return m.recorder
|
||||||
}
|
}
|
||||||
|
|
||||||
// Piers mocks base method
|
// AskPierMaster 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
|
|
||||||
func (m *MockPierManager) AskPierMaster(arg0 string) (bool, error) {
|
func (m *MockPierManager) AskPierMaster(arg0 string) (bool, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "AskPierMaster", arg0)
|
ret := m.ctrl.Call(m, "AskPierMaster", arg0)
|
||||||
|
@ -289,8 +276,22 @@ func (m *MockPierManager) AskPierMaster(arg0 string) (bool, error) {
|
||||||
return ret0, ret1
|
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 {
|
func (mr *MockPierManagerMockRecorder) AskPierMaster(arg0 interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AskPierMaster", reflect.TypeOf((*MockPierManager)(nil).AskPierMaster), arg0)
|
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))
|
||||||
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ func TestVerifyPool_CheckProof(t *testing.T) {
|
||||||
ID: from,
|
ID: from,
|
||||||
Name: "appchain A",
|
Name: "appchain A",
|
||||||
Validators: "",
|
Validators: "",
|
||||||
ConsensusType: 0,
|
ConsensusType: "rbft",
|
||||||
ChainType: "fabric",
|
ChainType: "fabric",
|
||||||
Desc: "",
|
Desc: "",
|
||||||
Version: "",
|
Version: "",
|
||||||
|
@ -129,7 +129,7 @@ func TestVerifyPool_CheckProof2(t *testing.T) {
|
||||||
ID: from,
|
ID: from,
|
||||||
Name: "appchain" + from,
|
Name: "appchain" + from,
|
||||||
Validators: "",
|
Validators: "",
|
||||||
ConsensusType: int32(1),
|
ConsensusType: "rbft",
|
||||||
ChainType: "fabric",
|
ChainType: "fabric",
|
||||||
Desc: "",
|
Desc: "",
|
||||||
Version: "",
|
Version: "",
|
||||||
|
|
|
@ -27,6 +27,10 @@ func (b *BoltStubImpl) Callee() string {
|
||||||
return b.ctx.Callee.String()
|
return b.ctx.Callee.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *BoltStubImpl) CurrentCaller() string {
|
||||||
|
return b.ctx.CurrentCaller.String()
|
||||||
|
}
|
||||||
|
|
||||||
func (b *BoltStubImpl) Logger() logrus.FieldLogger {
|
func (b *BoltStubImpl) Logger() logrus.FieldLogger {
|
||||||
return b.ctx.Logger
|
return b.ctx.Logger
|
||||||
}
|
}
|
||||||
|
@ -126,6 +130,7 @@ func (b *BoltStubImpl) CrossInvoke(address, method string, args ...*pb.Arg) *bol
|
||||||
ctx := &vm.Context{
|
ctx := &vm.Context{
|
||||||
Caller: b.bvm.ctx.Caller,
|
Caller: b.bvm.ctx.Caller,
|
||||||
Callee: addr,
|
Callee: addr,
|
||||||
|
CurrentCaller: b.bvm.ctx.Callee,
|
||||||
Ledger: b.bvm.ctx.Ledger,
|
Ledger: b.bvm.ctx.Ledger,
|
||||||
TransactionIndex: b.bvm.ctx.TransactionIndex,
|
TransactionIndex: b.bvm.ctx.TransactionIndex,
|
||||||
TransactionHash: b.bvm.ctx.TransactionHash,
|
TransactionHash: b.bvm.ctx.TransactionHash,
|
||||||
|
|
|
@ -94,6 +94,11 @@ func (bvm *BoltVM) HandleIBTP(ibtp *pb.IBTP) (ret []byte, err error) {
|
||||||
ve: bvm.ve,
|
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)
|
res := con.HandleIBTP(ibtp)
|
||||||
if !res.Ok {
|
if !res.Ok {
|
||||||
return nil, fmt.Errorf("call error: %s", res.Result)
|
return nil, fmt.Errorf("call error: %s", res.Result)
|
||||||
|
|
|
@ -7,23 +7,28 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
"github.com/meshplus/bitxhub-core/agency"
|
|
||||||
appchain_mgr "github.com/meshplus/bitxhub-core/appchain-mgr"
|
appchain_mgr "github.com/meshplus/bitxhub-core/appchain-mgr"
|
||||||
"github.com/meshplus/bitxhub-core/validator/mock_validator"
|
"github.com/meshplus/bitxhub-core/validator/mock_validator"
|
||||||
"github.com/meshplus/bitxhub-kit/log"
|
"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-kit/types"
|
||||||
"github.com/meshplus/bitxhub-model/constant"
|
"github.com/meshplus/bitxhub-model/constant"
|
||||||
"github.com/meshplus/bitxhub-model/pb"
|
"github.com/meshplus/bitxhub-model/pb"
|
||||||
"github.com/meshplus/bitxhub/internal/executor/contracts"
|
"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/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
from = "0x3f9d18f7C3a6E5E4C0B877FE3E688aB08840b997"
|
from = "0x3f9d18f7C3a6E5E4C0B877FE3E688aB08840b997"
|
||||||
to = "0x000018f7C3A6E5E4c0b877fe3E688ab08840b997"
|
to = "0x000018f7C3A6E5E4c0b877fe3E688ab08840b997"
|
||||||
|
admin1 = "0xc7F999b83Af6DF9e67d0a37Ee7e900bF38b3D013"
|
||||||
|
admin2 = "0x79a1215469FaB6f9c63c1816b45183AD3624bE34"
|
||||||
|
admin3 = "0x97c8B516D19edBf575D72a172Af7F418BE498C37"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetBoltContracts() map[string]agency.Contract {
|
func GetBoltContracts() map[string]agency.Contract {
|
||||||
|
@ -128,23 +133,96 @@ func TestBoltVM_Run(t *testing.T) {
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
proposals := make([][]byte, 0)
|
proposals := make([][]byte, 0)
|
||||||
proposals = append(proposals, proposalData)
|
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(), contracts.PROPOSAL_PREFIX).Return(true, proposals).AnyTimes()
|
||||||
mockLedger.EXPECT().QueryByPrefix(gomock.Any(), appchain_mgr.PREFIX).Return(true, data).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) {
|
mockLedger.EXPECT().GetState(gomock.Any(), gomock.Any()).DoAndReturn(func(addr *types.Address, key []byte) (bool, []byte) {
|
||||||
switch addr.String() {
|
switch addr.String() {
|
||||||
case constant.AppchainMgrContractAddr.String():
|
case constant.AppchainMgrContractAddr.Address().String():
|
||||||
return false, nil
|
return false, nil
|
||||||
case constant.InterchainContractAddr.String():
|
case constant.InterchainContractAddr.Address().String():
|
||||||
return false, nil
|
return false, nil
|
||||||
|
case constant.RoleContractAddr.Address().String():
|
||||||
|
return true, adminsData
|
||||||
}
|
}
|
||||||
return false, nil
|
return false, nil
|
||||||
}).Times(1)
|
}).Times(1)
|
||||||
mockLedger.EXPECT().GetState(gomock.Any(), gomock.Any()).DoAndReturn(func(addr *types.Address, key []byte) (bool, []byte) {
|
mockLedger.EXPECT().GetState(gomock.Any(), gomock.Any()).DoAndReturn(func(addr *types.Address, key []byte) (bool, []byte) {
|
||||||
switch addr.String() {
|
switch addr.String() {
|
||||||
case constant.AppchainMgrContractAddr.String():
|
case constant.AppchainMgrContractAddr.Address().String():
|
||||||
return true, nil
|
return true, chainRegistingData
|
||||||
case constant.InterchainContractAddr.String():
|
case constant.InterchainContractAddr.Address().String():
|
||||||
return false, nil
|
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
|
return true, nil
|
||||||
}).AnyTimes()
|
}).AnyTimes()
|
||||||
|
@ -160,6 +238,24 @@ func TestBoltVM_Run(t *testing.T) {
|
||||||
ctx := vm.NewContext(tx, 1, nil, mockLedger, log.NewWithModule("vm"))
|
ctx := vm.NewContext(tx, 1, nil, mockLedger, log.NewWithModule("vm"))
|
||||||
boltVM := New(ctx, mockEngine, GetBoltContracts())
|
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{
|
ip := &pb.InvokePayload{
|
||||||
Method: "CountAppchains",
|
Method: "CountAppchains",
|
||||||
}
|
}
|
||||||
|
@ -197,8 +293,8 @@ func TestBoltVM_Run(t *testing.T) {
|
||||||
Value: []byte(from),
|
Value: []byte(from),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: pb.Arg_I32,
|
Type: pb.Arg_String,
|
||||||
Value: []byte(strconv.Itoa(1)),
|
Value: []byte("rbft"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: pb.Arg_String,
|
Type: pb.Arg_String,
|
||||||
|
@ -227,6 +323,28 @@ func TestBoltVM_Run(t *testing.T) {
|
||||||
ret, err = boltVM.Run(input)
|
ret, err = boltVM.Run(input)
|
||||||
require.Nil(t, err)
|
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{
|
ip = &pb.InvokePayload{
|
||||||
Method: "DeleteAppchain",
|
Method: "DeleteAppchain",
|
||||||
Args: []*pb.Arg{
|
Args: []*pb.Arg{
|
||||||
|
@ -244,7 +362,7 @@ func TestBoltVM_Run(t *testing.T) {
|
||||||
require.Contains(t, err.Error(), "caller is not an admin account")
|
require.Contains(t, err.Error(), "caller is not an admin account")
|
||||||
|
|
||||||
ibtp := mockIBTP(t, 1, pb.IBTP_INTERCHAIN)
|
ibtp := mockIBTP(t, 1, pb.IBTP_INTERCHAIN)
|
||||||
_, err = boltVM.HandleIBTP(ibtp)
|
_, err = boltVMInterchain.HandleIBTP(ibtp)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
type Context struct {
|
type Context struct {
|
||||||
Caller *types.Address
|
Caller *types.Address
|
||||||
Callee *types.Address
|
Callee *types.Address
|
||||||
|
CurrentCaller *types.Address
|
||||||
Ledger ledger.Ledger
|
Ledger ledger.Ledger
|
||||||
TransactionIndex uint64
|
TransactionIndex uint64
|
||||||
TransactionHash *types.Hash
|
TransactionHash *types.Hash
|
||||||
|
@ -24,6 +25,7 @@ func NewContext(tx *pb.Transaction, txIndex uint64, data *pb.TransactionData, le
|
||||||
return &Context{
|
return &Context{
|
||||||
Caller: tx.From,
|
Caller: tx.From,
|
||||||
Callee: tx.To,
|
Callee: tx.To,
|
||||||
|
CurrentCaller: tx.From,
|
||||||
Ledger: ledger,
|
Ledger: ledger,
|
||||||
TransactionIndex: txIndex,
|
TransactionIndex: txIndex,
|
||||||
TransactionHash: tx.TransactionHash,
|
TransactionHash: tx.TransactionHash,
|
||||||
|
|
Binary file not shown.
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -60,6 +60,13 @@ func New(ctx *vm.Context, imports *wasmer.Imports, instances map[string]wasmer.I
|
||||||
return nil, err
|
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
|
wasmVM.w = w
|
||||||
|
|
||||||
return wasmVM, nil
|
return wasmVM, nil
|
||||||
|
|
|
@ -19,6 +19,7 @@ import (
|
||||||
"github.com/meshplus/bitxhub/internal/ledger"
|
"github.com/meshplus/bitxhub/internal/ledger"
|
||||||
"github.com/meshplus/bitxhub/internal/repo"
|
"github.com/meshplus/bitxhub/internal/repo"
|
||||||
"github.com/meshplus/bitxhub/pkg/vm"
|
"github.com/meshplus/bitxhub/pkg/vm"
|
||||||
|
"github.com/meshplus/bitxhub/pkg/vm/wasm/vmledger"
|
||||||
libp2pcert "github.com/meshplus/go-libp2p-cert"
|
libp2pcert "github.com/meshplus/go-libp2p-cert"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -45,7 +46,7 @@ func initCreateContext(t *testing.T, name string) *vm.Context {
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
dir := filepath.Join(os.TempDir(), "wasm", name)
|
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)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
data := &pb.TransactionData{
|
data := &pb.TransactionData{
|
||||||
|
@ -171,10 +172,10 @@ func TestExecute(t *testing.T) {
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
invokePayload := &pb.InvokePayload{
|
invokePayload := &pb.InvokePayload{
|
||||||
Method: "a",
|
Method: "state_test_set",
|
||||||
Args: []*pb.Arg{
|
Args: []*pb.Arg{
|
||||||
{Type: pb.Arg_I32, Value: []byte(fmt.Sprintf("%d", 1))},
|
{Type: pb.Arg_Bytes, Value: []byte("alice")},
|
||||||
{Type: pb.Arg_I32, Value: []byte(fmt.Sprintf("%d", 2))},
|
{Type: pb.Arg_Bytes, Value: []byte("111")},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
payload, err := invokePayload.Marshal()
|
payload, err := invokePayload.Marshal()
|
||||||
|
@ -188,14 +189,39 @@ func TestExecute(t *testing.T) {
|
||||||
TransactionData: data,
|
TransactionData: data,
|
||||||
Ledger: ctx.Ledger,
|
Ledger: ctx.Ledger,
|
||||||
}
|
}
|
||||||
imports1, err := validatorlib.New()
|
imports1, err := vmledger.New()
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
fmt.Println(imports1)
|
||||||
wasm1, err := New(ctx1, imports1, instances)
|
wasm1, err := New(ctx1, imports1, instances)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
fmt.Println(wasm1.w.Instance.Exports)
|
||||||
|
|
||||||
result, err := wasm1.Run(payload)
|
result, err := wasm1.Run(payload)
|
||||||
require.Nil(t, err)
|
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) {
|
func TestWasm_RunFabValidation(t *testing.T) {
|
||||||
|
@ -352,7 +378,7 @@ func TestWasm_RunWithoutMethod(t *testing.T) {
|
||||||
TransactionData: data,
|
TransactionData: data,
|
||||||
Ledger: ctx.Ledger,
|
Ledger: ctx.Ledger,
|
||||||
}
|
}
|
||||||
imports1, err := validatorlib.New()
|
imports1, err := vmledger.New()
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
wasm1, err := New(ctx1, imports1, instances)
|
wasm1, err := New(ctx1, imports1, instances)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
|
@ -24,3 +24,48 @@ fi
|
||||||
if ! type mockgen >/dev/null 2>&1; then
|
if ! type mockgen >/dev/null 2>&1; then
|
||||||
go get github.com/golang/mock/mockgen
|
go get github.com/golang/mock/mockgen
|
||||||
fi
|
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
|
||||||
|
|
|
@ -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
|
|
@ -43,7 +43,7 @@ func (suite *RegisterAppchain) TestRegisterAppchain() {
|
||||||
|
|
||||||
args := []*pb.Arg{
|
args := []*pb.Arg{
|
||||||
pb.String("validators"),
|
pb.String("validators"),
|
||||||
pb.Int32(0),
|
pb.String(""),
|
||||||
pb.String("hyperchain"),
|
pb.String("hyperchain"),
|
||||||
pb.String("税务链"),
|
pb.String("税务链"),
|
||||||
pb.String("趣链税务链"),
|
pb.String("趣链税务链"),
|
||||||
|
@ -82,7 +82,7 @@ func (suite *RegisterAppchain) TestFetchAppchains() {
|
||||||
|
|
||||||
args := []*pb.Arg{
|
args := []*pb.Arg{
|
||||||
pb.String(""),
|
pb.String(""),
|
||||||
pb.Int32(0),
|
pb.String(""),
|
||||||
pb.String("hyperchain"),
|
pb.String("hyperchain"),
|
||||||
pb.String("税务链"),
|
pb.String("税务链"),
|
||||||
pb.String("趣链税务链"),
|
pb.String("趣链税务链"),
|
||||||
|
@ -97,7 +97,7 @@ func (suite *RegisterAppchain) TestFetchAppchains() {
|
||||||
|
|
||||||
args = []*pb.Arg{
|
args = []*pb.Arg{
|
||||||
pb.String(""),
|
pb.String(""),
|
||||||
pb.Int32(0),
|
pb.String(""),
|
||||||
pb.String("fabric"),
|
pb.String("fabric"),
|
||||||
pb.String("政务链"),
|
pb.String("政务链"),
|
||||||
pb.String("fabric政务"),
|
pb.String("fabric政务"),
|
||||||
|
@ -160,7 +160,7 @@ func (suite *RegisterAppchain) TestGetPubKeyByChainID() {
|
||||||
|
|
||||||
args := []*pb.Arg{
|
args := []*pb.Arg{
|
||||||
pb.String(""),
|
pb.String(""),
|
||||||
pb.Int32(0),
|
pb.String(""),
|
||||||
pb.String("hyperchain"),
|
pb.String("hyperchain"),
|
||||||
pb.String("税务链"),
|
pb.String("税务链"),
|
||||||
pb.String("趣链税务链"),
|
pb.String("趣链税务链"),
|
||||||
|
@ -174,7 +174,7 @@ func (suite *RegisterAppchain) TestGetPubKeyByChainID() {
|
||||||
|
|
||||||
args = []*pb.Arg{
|
args = []*pb.Arg{
|
||||||
pb.String(""),
|
pb.String(""),
|
||||||
pb.Int32(0),
|
pb.String(""),
|
||||||
pb.String("fabric"),
|
pb.String("fabric"),
|
||||||
pb.String("政务链"),
|
pb.String("政务链"),
|
||||||
pb.String("fabric政务"),
|
pb.String("fabric政务"),
|
||||||
|
@ -212,7 +212,7 @@ func (suite *RegisterAppchain) TestUpdateAppchains() {
|
||||||
|
|
||||||
args := []*pb.Arg{
|
args := []*pb.Arg{
|
||||||
pb.String(""),
|
pb.String(""),
|
||||||
pb.Int32(0),
|
pb.String(""),
|
||||||
pb.String("hyperchain"),
|
pb.String("hyperchain"),
|
||||||
pb.String("税务链"),
|
pb.String("税务链"),
|
||||||
pb.String("趣链税务链"),
|
pb.String("趣链税务链"),
|
||||||
|
@ -235,7 +235,7 @@ func (suite *RegisterAppchain) TestUpdateAppchains() {
|
||||||
|
|
||||||
args = []*pb.Arg{
|
args = []*pb.Arg{
|
||||||
pb.String(""),
|
pb.String(""),
|
||||||
pb.Int32(0),
|
pb.String(""),
|
||||||
pb.String("hyperchain"),
|
pb.String("hyperchain"),
|
||||||
pb.String("管理链"),
|
pb.String("管理链"),
|
||||||
pb.String("趣链管理链"),
|
pb.String("趣链管理链"),
|
||||||
|
@ -250,7 +250,7 @@ func (suite *RegisterAppchain) TestUpdateAppchains() {
|
||||||
//UpdateAppchain
|
//UpdateAppchain
|
||||||
args = []*pb.Arg{
|
args = []*pb.Arg{
|
||||||
pb.String(""),
|
pb.String(""),
|
||||||
pb.Int32(0),
|
pb.String(""),
|
||||||
pb.String("hyperchain"),
|
pb.String("hyperchain"),
|
||||||
pb.String("税务链"),
|
pb.String("税务链"),
|
||||||
pb.String("趣链税务链"),
|
pb.String("趣链税务链"),
|
||||||
|
|
|
@ -3,20 +3,17 @@ package tester
|
||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
"time"
|
"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"
|
||||||
"github.com/meshplus/bitxhub-kit/crypto/asym"
|
"github.com/meshplus/bitxhub-kit/crypto/asym"
|
||||||
"github.com/meshplus/bitxhub-model/constant"
|
"github.com/meshplus/bitxhub-model/constant"
|
||||||
"github.com/meshplus/bitxhub-model/pb"
|
"github.com/meshplus/bitxhub-model/pb"
|
||||||
"github.com/meshplus/bitxhub/internal/coreapi/api"
|
"github.com/meshplus/bitxhub/internal/coreapi/api"
|
||||||
|
"github.com/meshplus/bitxhub/internal/executor/contracts"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
|
"github.com/tidwall/gjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Interchain struct {
|
type Interchain struct {
|
||||||
|
@ -28,6 +25,28 @@ func (suite *Interchain) SetupSuite() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *Interchain) TestHandleIBTP() {
|
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)
|
k1, err := asym.GenerateKeyPair(crypto.Secp256k1)
|
||||||
suite.Require().Nil(err)
|
suite.Require().Nil(err)
|
||||||
k2, err := asym.GenerateKeyPair(crypto.Secp256k1)
|
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",
|
ret, err := invokeBVMContract(suite.api, k1, k1Nonce, constant.AppchainMgrContractAddr.Address(), "Register",
|
||||||
pb.String(""),
|
pb.String(""),
|
||||||
pb.Int32(0),
|
pb.String("rbft"),
|
||||||
pb.String("hyperchain"),
|
pb.String("hyperchain"),
|
||||||
pb.String("婚姻链"),
|
pb.String("婚姻链"),
|
||||||
pb.String("趣链婚姻链"),
|
pb.String("趣链婚姻链"),
|
||||||
|
@ -58,24 +77,43 @@ func (suite *Interchain) TestHandleIBTP() {
|
||||||
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
|
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
|
||||||
k1Nonce++
|
k1Nonce++
|
||||||
id1 := gjson.Get(string(ret.Ret), "chain_id").String()
|
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))
|
ret, err = invokeBVMContract(suite.api, k1, k1Nonce, constant.AppchainMgrContractAddr.Address(), "GetAppchain", pb.String(id1))
|
||||||
suite.Require().Nil(err)
|
suite.Require().Nil(err)
|
||||||
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
|
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
|
||||||
k1Nonce++
|
k1Nonce++
|
||||||
|
|
||||||
ret, err = invokeBVMContract(suite.api, k1, k1Nonce, constant.AppchainMgrContractAddr.Address(), "Manager",
|
ret, err = invokeBVMContract(suite.api, priAdmin1, adminNonce1, constant.GovernanceContractAddr.Address(), "Vote",
|
||||||
pb.String(string(appchain_mgr.EventRegister)),
|
pb.String(proposalId1),
|
||||||
pb.String(string(contracts.APPOVED)),
|
pb.String(string(contracts.APPOVED)),
|
||||||
pb.Bytes(ret.Ret),
|
pb.String("reason"),
|
||||||
)
|
)
|
||||||
suite.Require().Nil(err)
|
suite.Require().Nil(err)
|
||||||
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
|
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",
|
ret, err = invokeBVMContract(suite.api, k2, k2Nonce, constant.AppchainMgrContractAddr.Address(), "Register",
|
||||||
pb.String(""),
|
pb.String(""),
|
||||||
pb.Int32(0),
|
pb.String("rbft"),
|
||||||
pb.String("fabric"),
|
pb.String("fabric"),
|
||||||
pb.String("税务链"),
|
pb.String("税务链"),
|
||||||
pb.String("fabric婚姻链"),
|
pb.String("fabric婚姻链"),
|
||||||
|
@ -86,20 +124,39 @@ func (suite *Interchain) TestHandleIBTP() {
|
||||||
suite.Require().True(ret.IsSuccess())
|
suite.Require().True(ret.IsSuccess())
|
||||||
k2Nonce++
|
k2Nonce++
|
||||||
id2 := gjson.Get(string(ret.Ret), "chain_id").String()
|
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))
|
ret, err = invokeBVMContract(suite.api, k2, k2Nonce, constant.AppchainMgrContractAddr.Address(), "GetAppchain", pb.String(id2))
|
||||||
suite.Require().Nil(err)
|
suite.Require().Nil(err)
|
||||||
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
|
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
|
||||||
k2Nonce++
|
k2Nonce++
|
||||||
|
|
||||||
ret, err = invokeBVMContract(suite.api, k2, k2Nonce, constant.AppchainMgrContractAddr.Address(), "Manager",
|
ret, err = invokeBVMContract(suite.api, priAdmin1, adminNonce1, constant.GovernanceContractAddr.Address(), "Vote",
|
||||||
pb.String(string(appchain_mgr.EventRegister)),
|
pb.String(proposalId2),
|
||||||
pb.String(string(contracts.APPOVED)),
|
pb.String(string(contracts.APPOVED)),
|
||||||
pb.Bytes(ret.Ret),
|
pb.String("reason"),
|
||||||
)
|
)
|
||||||
suite.Require().Nil(err)
|
suite.Require().Nil(err)
|
||||||
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
|
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
|
// deploy rule
|
||||||
bytes, err := ioutil.ReadFile("./test_data/hpc_rule.wasm")
|
bytes, err := ioutil.ReadFile("./test_data/hpc_rule.wasm")
|
||||||
|
@ -117,8 +174,9 @@ func (suite *Interchain) TestHandleIBTP() {
|
||||||
proof := []byte("true")
|
proof := []byte("true")
|
||||||
proofHash := sha256.Sum256(proof)
|
proofHash := sha256.Sum256(proof)
|
||||||
ib := &pb.IBTP{From: f.String(), To: t.String(), Index: ibtpNonce, Timestamp: time.Now().UnixNano(), Proof: proofHash[:]}
|
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)
|
suite.Require().Nil(err)
|
||||||
|
k1Nonce++
|
||||||
|
|
||||||
tx.Extra = proof
|
tx.Extra = proof
|
||||||
ret, err = sendTransactionWithReceipt(suite.api, tx)
|
ret, err = sendTransactionWithReceipt(suite.api, tx)
|
||||||
|
@ -128,6 +186,28 @@ func (suite *Interchain) TestHandleIBTP() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *Interchain) TestGetIBTPByID() {
|
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)
|
k1, err := asym.GenerateKeyPair(crypto.Secp256k1)
|
||||||
suite.Require().Nil(err)
|
suite.Require().Nil(err)
|
||||||
k2, err := asym.GenerateKeyPair(crypto.Secp256k1)
|
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",
|
ret, err := invokeBVMContract(suite.api, k1, k1Nonce, constant.AppchainMgrContractAddr.Address(), "Register",
|
||||||
pb.String(string(confByte)),
|
pb.String(string(confByte)),
|
||||||
pb.Int32(0),
|
pb.String("rbft"),
|
||||||
pb.String("hyperchain"),
|
pb.String("hyperchain"),
|
||||||
pb.String("婚姻链"),
|
pb.String("婚姻链"),
|
||||||
pb.String("趣链婚姻链"),
|
pb.String("趣链婚姻链"),
|
||||||
|
@ -161,24 +241,43 @@ func (suite *Interchain) TestGetIBTPByID() {
|
||||||
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
|
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
|
||||||
k1Nonce++
|
k1Nonce++
|
||||||
id1 := gjson.Get(string(ret.Ret), "chain_id").String()
|
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))
|
ret, err = invokeBVMContract(suite.api, k1, k1Nonce, constant.AppchainMgrContractAddr.Address(), "GetAppchain", pb.String(id1))
|
||||||
suite.Require().Nil(err)
|
suite.Require().Nil(err)
|
||||||
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
|
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
|
||||||
k1Nonce++
|
k1Nonce++
|
||||||
|
|
||||||
ret, err = invokeBVMContract(suite.api, k1, k1Nonce, constant.AppchainMgrContractAddr.Address(), "Manager",
|
ret, err = invokeBVMContract(suite.api, priAdmin1, adminNonce1, constant.GovernanceContractAddr.Address(), "Vote",
|
||||||
pb.String(string(appchain_mgr.EventRegister)),
|
pb.String(proposalId1),
|
||||||
pb.String(string(contracts.APPOVED)),
|
pb.String(string(contracts.APPOVED)),
|
||||||
pb.Bytes(ret.Ret),
|
pb.String("reason"),
|
||||||
)
|
)
|
||||||
suite.Require().Nil(err)
|
suite.Require().Nil(err)
|
||||||
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
|
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",
|
ret, err = invokeBVMContract(suite.api, k2, k2Nonce, constant.AppchainMgrContractAddr.Address(), "Register",
|
||||||
pb.String(""),
|
pb.String(""),
|
||||||
pb.Int32(0),
|
pb.String("rbft"),
|
||||||
pb.String("fabric"),
|
pb.String("fabric"),
|
||||||
pb.String("税务链"),
|
pb.String("税务链"),
|
||||||
pb.String("fabric税务链"),
|
pb.String("fabric税务链"),
|
||||||
|
@ -189,20 +288,39 @@ func (suite *Interchain) TestGetIBTPByID() {
|
||||||
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
|
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
|
||||||
k2Nonce++
|
k2Nonce++
|
||||||
id2 := gjson.Get(string(ret.Ret), "chain_id").String()
|
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))
|
ret, err = invokeBVMContract(suite.api, k2, k2Nonce, constant.AppchainMgrContractAddr.Address(), "GetAppchain", pb.String(id2))
|
||||||
suite.Require().Nil(err)
|
suite.Require().Nil(err)
|
||||||
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
|
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
|
||||||
k2Nonce++
|
k2Nonce++
|
||||||
|
|
||||||
ret, err = invokeBVMContract(suite.api, k2, k2Nonce, constant.AppchainMgrContractAddr.Address(), "Manager",
|
ret, err = invokeBVMContract(suite.api, priAdmin1, adminNonce1, constant.GovernanceContractAddr.Address(), "Vote",
|
||||||
pb.String(string(appchain_mgr.EventRegister)),
|
pb.String(proposalId2),
|
||||||
pb.String(string(contracts.APPOVED)),
|
pb.String(string(contracts.APPOVED)),
|
||||||
pb.Bytes(ret.Ret),
|
pb.String("reason"),
|
||||||
)
|
)
|
||||||
suite.Require().Nil(err)
|
suite.Require().Nil(err)
|
||||||
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
|
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")
|
contractByte, err := ioutil.ReadFile("./test_data/fabric_policy.wasm")
|
||||||
suite.Require().Nil(err)
|
suite.Require().Nil(err)
|
||||||
|
@ -220,30 +338,33 @@ func (suite *Interchain) TestGetIBTPByID() {
|
||||||
|
|
||||||
proofHash := sha256.Sum256(proof)
|
proofHash := sha256.Sum256(proof)
|
||||||
ib := &pb.IBTP{From: f.String(), To: t.String(), Index: ibtpNonce, Payload: []byte("111"), Timestamp: time.Now().UnixNano(), Proof: proofHash[:]}
|
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)
|
suite.Require().Nil(err)
|
||||||
tx.Extra = proof
|
tx.Extra = proof
|
||||||
receipt, err := sendTransactionWithReceipt(suite.api, tx)
|
receipt, err := sendTransactionWithReceipt(suite.api, tx)
|
||||||
suite.Require().Nil(err)
|
suite.Require().Nil(err)
|
||||||
suite.Require().EqualValues(true, receipt.IsSuccess(), string(receipt.Ret))
|
suite.Require().EqualValues(true, receipt.IsSuccess(), string(receipt.Ret))
|
||||||
ibtpNonce++
|
ibtpNonce++
|
||||||
|
k1Nonce++
|
||||||
|
|
||||||
ib2 := &pb.IBTP{From: f.String(), To: t.String(), Index: ibtpNonce, Payload: []byte("111"), Timestamp: time.Now().UnixNano(), Proof: proofHash[:]}
|
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)
|
suite.Require().Nil(err)
|
||||||
tx.Extra = proof
|
tx.Extra = proof
|
||||||
receipt, err = sendTransactionWithReceipt(suite.api, tx)
|
receipt, err = sendTransactionWithReceipt(suite.api, tx)
|
||||||
suite.Require().Nil(err)
|
suite.Require().Nil(err)
|
||||||
suite.Require().EqualValues(true, receipt.IsSuccess(), string(receipt.Ret))
|
suite.Require().EqualValues(true, receipt.IsSuccess(), string(receipt.Ret))
|
||||||
ibtpNonce++
|
ibtpNonce++
|
||||||
|
k1Nonce++
|
||||||
|
|
||||||
ib3 := &pb.IBTP{From: f.String(), To: t.String(), Index: ibtpNonce, Payload: []byte("111"), Timestamp: time.Now().UnixNano(), Proof: proofHash[:]}
|
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)
|
suite.Require().Nil(err)
|
||||||
tx.Extra = proof
|
tx.Extra = proof
|
||||||
receipt, err = sendTransactionWithReceipt(suite.api, tx)
|
receipt, err = sendTransactionWithReceipt(suite.api, tx)
|
||||||
suite.Assert().Nil(err)
|
suite.Assert().Nil(err)
|
||||||
ibtpNonce++
|
ibtpNonce++
|
||||||
|
k1Nonce++
|
||||||
|
|
||||||
ib.Index = 2
|
ib.Index = 2
|
||||||
ret, err = invokeBVMContract(suite.api, k1, k1Nonce, constant.InterchainContractAddr.Address(), "GetIBTPByID", pb.String(ib.ID()))
|
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() {
|
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)
|
k1, err := asym.GenerateKeyPair(crypto.Secp256k1)
|
||||||
suite.Require().Nil(err)
|
suite.Require().Nil(err)
|
||||||
k1Nonce := uint64(1)
|
k1Nonce := uint64(1)
|
||||||
|
@ -262,7 +405,7 @@ func (suite *Interchain) TestInterchain() {
|
||||||
|
|
||||||
ret, err := invokeBVMContract(suite.api, k1, k1Nonce, constant.AppchainMgrContractAddr.Address(), "Register",
|
ret, err := invokeBVMContract(suite.api, k1, k1Nonce, constant.AppchainMgrContractAddr.Address(), "Register",
|
||||||
pb.String(""),
|
pb.String(""),
|
||||||
pb.Int32(0),
|
pb.String("rbft"),
|
||||||
pb.String("hyperchain"),
|
pb.String("hyperchain"),
|
||||||
pb.String("婚姻链"),
|
pb.String("婚姻链"),
|
||||||
pb.String("趣链婚姻链"),
|
pb.String("趣链婚姻链"),
|
||||||
|
@ -273,20 +416,39 @@ func (suite *Interchain) TestInterchain() {
|
||||||
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
|
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
|
||||||
k1Nonce++
|
k1Nonce++
|
||||||
id1 := gjson.Get(string(ret.Ret), "chain_id").String()
|
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))
|
ret, err = invokeBVMContract(suite.api, k1, k1Nonce, constant.AppchainMgrContractAddr.Address(), "GetAppchain", pb.String(id1))
|
||||||
suite.Require().Nil(err)
|
suite.Require().Nil(err)
|
||||||
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
|
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
|
||||||
k1Nonce++
|
k1Nonce++
|
||||||
|
|
||||||
ret, err = invokeBVMContract(suite.api, k1, k1Nonce, constant.AppchainMgrContractAddr.Address(), "Manager",
|
ret, err = invokeBVMContract(suite.api, priAdmin1, adminNonce1, constant.GovernanceContractAddr.Address(), "Vote",
|
||||||
pb.String(string(appchain_mgr.EventRegister)),
|
pb.String(proposalId1),
|
||||||
pb.String(string(contracts.APPOVED)),
|
pb.String(string(contracts.APPOVED)),
|
||||||
pb.Bytes(ret.Ret),
|
pb.String("reason"),
|
||||||
)
|
)
|
||||||
suite.Require().Nil(err)
|
suite.Require().Nil(err)
|
||||||
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
|
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")
|
ret, err = invokeBVMContract(suite.api, k1, k1Nonce, constant.InterchainContractAddr.Address(), "Interchain")
|
||||||
suite.Require().Nil(err)
|
suite.Require().Nil(err)
|
||||||
|
|
|
@ -6,6 +6,8 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/meshplus/bitxhub/internal/executor/contracts"
|
||||||
|
|
||||||
"github.com/meshplus/bitxhub-kit/crypto"
|
"github.com/meshplus/bitxhub-kit/crypto"
|
||||||
"github.com/meshplus/bitxhub-kit/crypto/asym"
|
"github.com/meshplus/bitxhub-kit/crypto/asym"
|
||||||
"github.com/meshplus/bitxhub-model/constant"
|
"github.com/meshplus/bitxhub-model/constant"
|
||||||
|
@ -37,7 +39,7 @@ func (suite *Role) TestGetRole() {
|
||||||
suite.Assert().Nil(err)
|
suite.Assert().Nil(err)
|
||||||
_, err = invokeBVMContract(suite.api, suite.privKey, suite.normalNonce, constant.AppchainMgrContractAddr.Address(), "Register",
|
_, err = invokeBVMContract(suite.api, suite.privKey, suite.normalNonce, constant.AppchainMgrContractAddr.Address(), "Register",
|
||||||
pb.String(""),
|
pb.String(""),
|
||||||
pb.Int32(0),
|
pb.String("rbft"),
|
||||||
pb.String("hyperchain"),
|
pb.String("hyperchain"),
|
||||||
pb.String("婚姻链"),
|
pb.String("婚姻链"),
|
||||||
pb.String("趣链婚姻链"),
|
pb.String("趣链婚姻链"),
|
||||||
|
@ -106,6 +108,28 @@ func (suite *Role) TestIsAdmin() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *Role) TestGetRuleAddress() {
|
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)
|
k1, err := asym.GenerateKeyPair(crypto.Secp256k1)
|
||||||
suite.Require().Nil(err)
|
suite.Require().Nil(err)
|
||||||
k2, err := asym.GenerateKeyPair(crypto.Secp256k1)
|
k2, err := asym.GenerateKeyPair(crypto.Secp256k1)
|
||||||
|
@ -126,7 +150,7 @@ func (suite *Role) TestGetRuleAddress() {
|
||||||
// Register
|
// Register
|
||||||
ret, err := invokeBVMContract(suite.api, k1, k1Nonce, constant.AppchainMgrContractAddr.Address(), "Register",
|
ret, err := invokeBVMContract(suite.api, k1, k1Nonce, constant.AppchainMgrContractAddr.Address(), "Register",
|
||||||
pb.String(""),
|
pb.String(""),
|
||||||
pb.Int32(0),
|
pb.String("rbft"),
|
||||||
pb.String("hyperchain"),
|
pb.String("hyperchain"),
|
||||||
pb.String("婚姻链"),
|
pb.String("婚姻链"),
|
||||||
pb.String("趣链婚姻链"),
|
pb.String("趣链婚姻链"),
|
||||||
|
@ -137,10 +161,38 @@ func (suite *Role) TestGetRuleAddress() {
|
||||||
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
|
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
|
||||||
k1Nonce++
|
k1Nonce++
|
||||||
id1 := gjson.Get(string(ret.Ret), "chain_id").String()
|
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",
|
ret, err = invokeBVMContract(suite.api, k2, k2Nonce, constant.AppchainMgrContractAddr.Address(), "Register",
|
||||||
pb.String(""),
|
pb.String(""),
|
||||||
pb.Int32(0),
|
pb.String("rbft"),
|
||||||
pb.String("fabric"),
|
pb.String("fabric"),
|
||||||
pb.String("政务链"),
|
pb.String("政务链"),
|
||||||
pb.String("fabric政务"),
|
pb.String("fabric政务"),
|
||||||
|
@ -151,6 +203,34 @@ func (suite *Role) TestGetRuleAddress() {
|
||||||
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
|
suite.Require().True(ret.IsSuccess(), string(ret.Ret))
|
||||||
k2Nonce++
|
k2Nonce++
|
||||||
id2 := gjson.Get(string(ret.Ret), "chain_id").String()
|
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
|
// deploy rule
|
||||||
bytes, err := ioutil.ReadFile("./test_data/hpc_rule.wasm")
|
bytes, err := ioutil.ReadFile("./test_data/hpc_rule.wasm")
|
||||||
|
@ -207,7 +287,7 @@ func (suite *Role) TestSetAdminRoles() {
|
||||||
// register
|
// register
|
||||||
retReg, err := invokeBVMContract(suite.api, priAdmin, adminNonce, constant.AppchainMgrContractAddr.Address(), "Register",
|
retReg, err := invokeBVMContract(suite.api, priAdmin, adminNonce, constant.AppchainMgrContractAddr.Address(), "Register",
|
||||||
pb.String(""),
|
pb.String(""),
|
||||||
pb.Int32(0),
|
pb.String("rbft"),
|
||||||
pb.String("hyperchain"),
|
pb.String("hyperchain"),
|
||||||
pb.String("管理链"),
|
pb.String("管理链"),
|
||||||
pb.String("趣链管理链"),
|
pb.String("趣链管理链"),
|
||||||
|
|
|
@ -64,7 +64,7 @@ func (suite *Governance) TestGovernance() {
|
||||||
// 1. Register ==============================================
|
// 1. Register ==============================================
|
||||||
ret, err := invokeBVMContract(suite.api, appchainPri, appchainNonce, constant.AppchainMgrContractAddr.Address(), "Register",
|
ret, err := invokeBVMContract(suite.api, appchainPri, appchainNonce, constant.AppchainMgrContractAddr.Address(), "Register",
|
||||||
pb.String("validators"),
|
pb.String("validators"),
|
||||||
pb.Int32(0),
|
pb.String("rbft"),
|
||||||
pb.String("hyperchain"),
|
pb.String("hyperchain"),
|
||||||
pb.String("税务链"),
|
pb.String("税务链"),
|
||||||
pb.String("趣链税务链"),
|
pb.String("趣链税务链"),
|
||||||
|
@ -80,7 +80,7 @@ func (suite *Governance) TestGovernance() {
|
||||||
// repeated registration
|
// repeated registration
|
||||||
ret, err = invokeBVMContract(suite.api, appchainPri, appchainNonce, constant.AppchainMgrContractAddr.Address(), "Register",
|
ret, err = invokeBVMContract(suite.api, appchainPri, appchainNonce, constant.AppchainMgrContractAddr.Address(), "Register",
|
||||||
pb.String("validators"),
|
pb.String("validators"),
|
||||||
pb.Int32(0),
|
pb.String("rbft"),
|
||||||
pb.String("hyperchain"),
|
pb.String("hyperchain"),
|
||||||
pb.String("税务链"),
|
pb.String("税务链"),
|
||||||
pb.String("趣链税务链"),
|
pb.String("趣链税务链"),
|
||||||
|
|
|
@ -21,7 +21,7 @@ func genXVMContractTransaction(privateKey crypto.PrivateKey, nonce uint64, addre
|
||||||
return genContractTransaction(pb.TransactionData_XVM, privateKey, nonce, address, method, args...)
|
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()
|
from, err := privateKey.PublicKey().Address()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -58,7 +58,7 @@ func genIBTPTransaction(privateKey crypto.PrivateKey, ibtp *pb.IBTP) (*pb.Transa
|
||||||
To: constant.InterchainContractAddr.Address(),
|
To: constant.InterchainContractAddr.Address(),
|
||||||
Payload: payload,
|
Payload: payload,
|
||||||
Timestamp: time.Now().UnixNano(),
|
Timestamp: time.Now().UnixNano(),
|
||||||
Nonce: ibtp.Index,
|
Nonce: nonce,
|
||||||
IBTP: ibtp,
|
IBTP: ibtp,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -95,6 +95,7 @@ func newTesterBitXHub(rep *repo.Repo) (*app.BitXHub, error) {
|
||||||
order.WithDigest(chainMeta.BlockHash.String()),
|
order.WithDigest(chainMeta.BlockHash.String()),
|
||||||
order.WithGetChainMetaFunc(bxh.Ledger.GetChainMeta),
|
order.WithGetChainMetaFunc(bxh.Ledger.GetChainMeta),
|
||||||
order.WithGetBlockByHeightFunc(bxh.Ledger.GetBlock),
|
order.WithGetBlockByHeightFunc(bxh.Ledger.GetBlock),
|
||||||
|
order.WithGetAccountNonceFunc(bxh.Ledger.GetNonce),
|
||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Reference in New Issue