Commit ffa42a91 authored by suyanlong's avatar suyanlong

Initial commit

parents
test/*
plugins/build
Dockerfile
\ No newline at end of file
---
name: 'Bug Report'
about: 'As a User, I want to report a Bug.'
labels: bug
---
## Bug Report
Please answer these questions before submitting your issue. Thanks!
### 1. What did you do?
<!-- If possible, provide a recipe for reproducing the error. -->
### 2. What did you expect to see?
### 3. What did you see instead?
### 4. What version of BitXHub are you using? (`bitxhub version` on BitXHub)
---
name: 'Development Task'
about: 'As a BitXHub developer, I want to record a development task.'
labels: enhancement
---
## Development Task
---
name: 'Feature Request'
about: 'As a user, I want to request a New Feature on the product.'
labels: feature
---
## Feature Request
**Is your feature request related to a problem? Please describe:**
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
**Describe the feature you'd like:**
<!-- A clear and concise description of what you want to happen. -->
**Describe alternatives you've considered:**
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
**Teachability, Documentation, Adoption, Migration Strategy:**
<!-- If you can, explain some scenarios how users might use this, situations it would be helpful in. Any API designs, mockups, or diagrams are also helpful. -->
---
name: 'Ask a Question'
about: 'I want to ask a question.'
labels: question
---
## General Question
name: build
on:
push:
branches:
- master
- release-*
pull_request:
branches:
- master
- release-*
jobs:
lint:
name: Run golanci-lint
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.17
uses: actions/setup-go@v1
with:
go-version: 1.17
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Run golangci-lint
uses: Mushus/golangci-linter@v1
test:
name: Run unittest
runs-on: ubuntu-latest
steps:
- name: Set up Go
uses: actions/setup-go@v1
with:
go-version: 1.17
- name: Check out code
uses: actions/checkout@v1
- name: Run Unit tests.
run: |
export PATH=$PATH:$(go env GOPATH)/bin
make prepare
make test-coverage
- name: Upload Coverage report to CodeCov
uses: codecov/codecov-action@v1.0.0
with:
token: ${{secrets.CODECOV_TOKEN}}
file: ./coverage.txt
build:
name: Build project
runs-on: ubuntu-latest
needs: [lint, test]
steps:
- name: Set up Go 1.17
uses: actions/setup-go@v1
with:
go-version: 1.17
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Go build
run: go build -v ./cmd/sidecar
name: Notify
on:
pull_request:
types: [opened, reopened]
jobs:
notify:
name: notify on DingTalk
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v2
- name: Ding Talk PR Notify
uses: lijinke666/ding-talk-pr-notify@main
with:
ding_talk_token: ${{ secrets.DING_TALK_ACCESS_TOKEN }}
repo_url: https://github.com/link33/sidecar/pull
at_all: false
\ No newline at end of file
name: Release
on:
push:
tags:
- 'v*.*.*'
jobs:
release-binary:
name: Release binary 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.17
- 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: bin/**.tar.gz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
release-docker:
name: Release sidecar docker image
runs-on: ${{matrix.os}}
strategy:
matrix:
os: [ubuntu-latest]
go_version: [1.17]
steps:
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go_version }}
- name: Check out code
uses: actions/checkout@v2
- name: Build Binary
run: make release-binary
- name: Build and push sidecar image
uses: elgohr/Publish-Docker-Github-Action@master
with:
name: link33/sidecar
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
registry: hub.docker.com
dockerfile: Dockerfile-sidecar
tag_names: true
\ No newline at end of file
.DS_Store
.idea
*.tar
logs/*
internal/repo/a_repo-packr.go
bin
cover.out
coverage.out
coverage.txt
cover.html
imports/imports.go
goent.mod
goent.sum
testdata/
!internal/checker/testdata
\ No newline at end of file
linters:
# please, do not use `enable-all`: it's deprecated and will be removed soon.
# inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint
disable-all: true
enable:
- bodyclose
- deadcode
- depguard
- dogsled
- dupl
- errcheck
# - funlen
- gochecknoinits
- goconst
- gocritic
- gocyclo
- gofmt
- goimports
# - golint
# - gomnd
- goprintffuncname
# - gosec
- gosimple
- govet
- ineffassign
# - interfacer
# - lll
- misspell
- nakedret
- rowserrcheck
# - scopelint
- staticcheck
- structcheck
# - stylecheck
- typecheck
- unconvert
# - unparam
# - unused
- varcheck
# - whitespace
# Contributing
## <a name="submit"></a> Submission Guidelines
### <a name="submit-pr"></a> Submitting a Pull Request (PR)
Before you submit your Pull Request (PR) consider the following guidelines:
1. Search [GitHub]() for an open or closed PR
that relates to your submission. You don't want to duplicate effort.
1. Be sure that an issue describes the problem you're fixing, or documents the design for the feature you'd like to add.
Discussing the design up front helps to ensure that we're ready to accept your work.
1. Fork the meshplus/bitxhub repo.
1. Make your changes in a new git branch:
```shell
git checkout -b my-fix-branch master
```
1. Create your patch, **including appropriate test cases**.
1. Follow gofmt and golint specifications.
1. Run the full test suite and ensure that all tests pass.
1. Commit your changes using a descriptive commit message that follows our
[commit message conventions](#commit). Adherence to these conventions
is necessary because release notes are automatically generated from these messages.
```shell
git commit -a
```
Note: the optional commit `-a` command line option will automatically "add" and "rm" edited files.
1. Push your branch to GitHub:
```shell
git push origin my-fix-branch
```
1. In GitHub, send a pull request to `bitxhub:master`.
* If we suggest changes then:
* Make the required updates.
* Re-run the test suites to ensure tests are still passing.
* Rebase your branch and force push to your GitHub repository (this will update your Pull Request):
```shell
git rebase master -i
git push -f
```
That's it! Thank you for your contribution!
#### After your pull request is merged
After your pull request is merged, you can safely delete your branch and pull the changes
from the main (upstream) repository:
* Delete the remote branch on GitHub either through the GitHub web UI or your local shell as follows:
```shell
git push origin --delete my-fix-branch
```
* Check out the master branch:
```shell
git checkout master -f
```
* Delete the local branch:
```shell
git branch -D my-fix-branch
```
* Update your master with the latest upstream version:
```shell
git pull --ff upstream master
```
## <a name="commit"> Commit Message Guidelines
Commit Message in this repository should strictly follow the [AngularJS Git Commit Message Conventions](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-commit-message-guidelines). We explain the conventions below. For detail explanation, it can be found in this [document](https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#).
### Commit Message Format
Each commit message consists of a **header**, a **body** and a **footer**. The header has a special
format that includes a **type**, a **scope** and a **subject**:
```
<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>
```
The **header** is mandatory and the **scope** of the header is optional.
Any line of the commit message cannot be longer than 100 characters! This allows the message to be easier
to read on GitHub as well as in various git tools.
The footer should contain a [closing reference to an issue](https://help.github.com/articles/closing-issues-via-commit-messages/) if any.
### Revert
If the commit reverts a previous commit, it should begin with `revert:`, followed by the header of the reverted commit. In the body it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.
### Type
Must be one of the following:
* **build**: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
* **ci**: Changes to our CI configuration files and scripts (example scopes: Circle, BrowserStack, SauceLabs)
* **docs**: Documentation only changes
* **feat**: A new feature
* **fix**: A bug fix
* **perf**: A code change that improves performance
* **refactor**: A code change that neither fixes a bug nor adds a feature
* **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
* **test**: Adding missing tests or correcting existing tests
### Subject
The subject contains a succinct description of the change:
* use the imperative, present tense: "change" not "changed" nor "changes"
* don't capitalize the first letter
* no dot (.) at the end
### Body
Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes".
The body should include the motivation for the change and contrast this with previous behavior.
### Footer
The footer should contain any information about **Breaking Changes** and is also the place to
reference GitHub issues that this commit **Closes**.
**Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines. The rest of the commit message is then used for this.
This diff is collapsed.
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.
# use : docker build --build-arg PLUGIN_URL=https://github.com/link33/sidecar-client-fabric --build-arg PLUGIN_VERSION=v1.0.0-rc2 -t sidecar:1.0.0 .
FROM golang:1.17 as builder
RUN mkdir -p /go/src/github.com/link33/sidecar
WORKDIR /go/src/github.com/link33/sidecar
# Cache dependencies
COPY go.mod .
COPY go.sum .
RUN go env -w GOPROXY=https://goproxy.cn,direct
RUN go mod download -x
# Build real binaries
COPY . .
RUN go get -u github.com/gobuffalo/packr/packr
RUN make install
RUN cd .. && \
git clone https://github.com/link33/sidecar-client-fabric.git && \
cd sidecar-client-fabric && \
git checkout v1.1.0-rc1 && \
make fabric1.4 && \
cp build/fabric-client-1.4 /go/bin/fabric-client-1.4
# Final image
FROM frolvlad/alpine-glibc
WORKDIR /root
# Copy over binaries from the builder
COPY --from=builder /go/bin/sidecar /usr/local/bin
COPY ./build/libwasmer.so /lib
ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/lib
RUN ["sidecar", "init"]
RUN mkdir -p /root/sidecar/plugins
COPY --from=builder /go/bin/fabric-client-1.4 /root/sidecar/plugins/appchain_plugin
COPY scripts/docker_entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker_entrypoint.sh
ENV APPCHAIN_NAME=fabric-client-1.4 \
PLUGIN_CONFIG=fabric
EXPOSE 44555 44544
# run sidecar inside container using:
# docker run -t --env APPCHAIN_NAME=fabric-client-1.4.so \
# --env PLUGIN_CONFIG=fabric \
# -v your/path/to/rule:/root/sidecar/validating.wasm \
# -v your/path/to/sidecar.toml:/root/sidecar/sidecar.toml \
# -v your/path/to/plugin_config:/root/sidecar/fabric \
# sidecar:1.0.0
ENTRYPOINT docker_entrypoint.sh, "$APPCHAIN_NAME", "$PLUGIN_CONFIG"
FROM frolvlad/alpine-glibc
# Environmental preparation
COPY ./build/libwasmer.so /lib
ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/lib
RUN mkdir -p /root/sidecar/plugins
WORKDIR /root/sidecar
# Copy essential binaries from the bin
COPY ./bin/sidecar /usr/local/bin/
# Generate configuration
RUN sidecar --repo=/root/sidecar init
EXPOSE 44555 44544
CMD ["sidecar", "start"]
SHELL := /bin/bash
CURRENT_PATH = $(shell pwd)
APP_NAME = sidecar
# build with verison infos
VERSION_DIR = github.com/link33/${APP_NAME}
BUILD_DATE = $(shell date +%FT%T)
GIT_COMMIT = $(shell git log --pretty=format:'%h' -n 1)
GIT_BRANCH = $(shell git rev-parse --abbrev-ref HEAD)
ifeq (${GIT_BRANCH},HEAD)
APP_VERSION = $(shell git describe --tags HEAD)
else
APP_VERSION = dev
endif
GOLDFLAGS += -X "${VERSION_DIR}.BuildDate=${BUILD_DATE}"
GOLDFLAGS += -X "${VERSION_DIR}.CurrentCommit=${GIT_COMMIT}"
GOLDFLAGS += -X "${VERSION_DIR}.CurrentBranch=${GIT_BRANCH}"
GOLDFLAGS += -X "${VERSION_DIR}.CurrentVersion=${APP_VERSION}"
STATIC_LDFLAGS += ${GOLDFLAGS}
STATIC_LDFLAGS += -linkmode external -extldflags -static
GO = GO111MODULE=on go
TEST_PKGS := $(shell $(GO) list ./... | grep -v 'cmd' | grep -v 'mock_*' | grep -v 'proto' | grep -v 'imports' | grep -v 'internal/app' | grep -v 'api')
RED=\033[0;31m
GREEN=\033[0;32m
BLUE=\033[0;34m
NC=\033[0m
.PHONY: test
help: Makefile
@echo "Choose a command run:"
@sed -n 's/^##//p' $< | column -t -s ':' | sed -e 's/^/ /'
## make test: Run go unittest
test:
go generate ./...
@$(GO) test ${TEST_PKGS} -count=1
## make test-coverage: Test project with cover
test-coverage:
go generate ./...
@go test -short -coverprofile cover.out -covermode=atomic ${TEST_PKGS}
@cat cover.out >> coverage.txt
packr:
cd internal/repo && packr
prepare:
cd scripts && bash prepare.sh
## make install: Go install the project (hpc)
install: packr
rm -f imports/imports.go
$(GO) install -ldflags '${GOLDFLAGS}' ./cmd/${APP_NAME}
@printf "${GREEN}Build sidecar successfully${NC}\n"
build: packr
@mkdir -p bin
rm -f imports/imports.go
$(GO) build -ldflags '${GOLDFLAGS}' ./cmd/${APP_NAME}
@mv ./sidecar bin
@printf "${GREEN}Build sidecar successfully!${NC}\n"
installent: packr
cp imports/imports.go.template imports/imports.go
@sed "s?)?$(MODS)@)?" go.mod | tr '@' '\n' > goent.mod
$(GO) install -tags ent -ldflags '${GOLDFLAGS}' -modfile goent.mod ./cmd/${APP_NAME}
buildent: packr
@mkdir -p bin
cp imports/imports.go.template imports/imports.go
@sed "s?)?$(MODS)@)?" go.mod | tr '@' '\n' > goent.mod
$(GO) build -tags ent -ldflags '${GOLDFLAGS}' -modfile goent.mod ./cmd/${APP_NAME}
@mv ./sidecar bin
@printf "${GREEN}Build sidecar ent successfully!${NC}\n"
mod:
sed "s?)?$(MODS)\n)?" go.mod
docker-build: packr
$(GO) install -ldflags '${STATIC_LDFLAGS}' ./cmd/${APP_NAME}
@echo "Build sidecar successfully"
## make build-linux: Go build linux executable file
build-linux:
cd scripts && bash cross_compile.sh linux-amd64 ${CURRENT_PATH}
## make release: Build release before push
release-binary:
@cd scripts && bash release_binary.sh
## make linter: Run golanci-lint
linter:
golangci-lint run
golangci-lint run -E goimports -E bodyclose --skip-dirs-use-default
fmt:
go fmt ./...
all: pb grpc
pb:
@cd model/pb && protoc -I=. \
-I${GOPATH}/src \
-I${GOPATH}/src/github.com/gogo/protobuf/protobuf \
--gogofaster_out=:. \
ibtp.proto ibtpx.proto basic.proto message.proto
#pb:
# cd model/pb && protoc -I=. \
# -I${GOPATH}/src \
# --gogofaster_out=:. \
# block.proto ibtp.proto ibtpx.proto network.proto receipt.proto bxh_transaction.proto chain.proto arg.proto interchain_meta.proto plugin.proto vp_info.proto basic.proto
grpc:
cd model/pb && protoc -I=. \
-I=${GOPATH}/src \
-I=${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
-I=${GOPATH}/src/github.com/gogo/protobuf/protobuf \
--grpc-gateway_out=logtostderr=true:. \
--swagger_out=logtostderr=true:. \
--gogofaster_out=plugins=grpc:. \
broker.proto plugin.proto
clean:
rm pb/*.pb.go
rm pb/*.json
rm pb/*.gw.go
.PHONY: pb
# sidecar
![build](https://github.com/link33/sidecar/workflows/build/badge.svg)
[![codecov](https://codecov.io/gh/link33/sidecar/branch/master/graph/badge.svg)](https://codecov.io/gh/link33/sidecar)
## Build
Using the follow command to install necessary tools.
```bash
make prepare
```
And then install sidecar using the following command.
```bash
make install
```
## Initialization
Using the follow command to initialize sidecar.
```bash
sidecar init
```
Default repo path is `~/sidecar`. If you want to specify the repo path, you can use `--repo` flag.
```bash
sidecar init --repo=$HOME/sidecar
```
After initializing sidecar, it will generate the follow directory:
```
~/sidecar
├── sidecar.toml
├── key.json
```
## Configuration
```toml
title = "sidecar"
[port]
pprof = 44555
[log]
level = "debug"
dir = "logs"
filename = "sidecar.log"
report_caller = false
[appchain]
plugin = "fabric-client-1.4.so"
config = "fabric"
```
`port.pprof`: the pprof server port
`log.level`: log level: debug, info, warn, error, fatal
`appchain.plugin`: relative path in sidecar repo of appchain plugin
`appchain.config`: relative path of appchain config directory
package api
import (
"context"
"fmt"
"github.com/gin-gonic/gin"
"github.com/link33/sidecar/cmd/sidecar/client"
"github.com/link33/sidecar/internal/repo"
"github.com/link33/sidecar/model/pb"
"github.com/sirupsen/logrus"
)
type Server struct {
router *gin.Engine
config *repo.Config
logger logrus.FieldLogger
ctx context.Context
cancel context.CancelFunc
}
type response struct {
Data []byte `json:"data"`
}
func NewServer(config *repo.Config, logger logrus.FieldLogger) (*Server, error) {
ctx, cancel := context.WithCancel(context.Background())
gin.SetMode(gin.ReleaseMode)
router := gin.New()
return &Server{
router: router,
config: config,
logger: logger,
ctx: ctx,
cancel: cancel,
}, nil
}
func (g *Server) Start() error {
g.router.Use(gin.Recovery())
v1 := g.router.Group("/v1")
{
v1.POST(client.RegisterAppchainUrl, g.registerAppchain)
v1.POST(client.UpdateAppchainUrl, g.updateAppchain)
v1.GET(client.GetAppchainUrl, g.getAppchain)
}
go func() {
go func() {
err := g.router.Run(fmt.Sprintf(":%d", g.config.Port.Http))
if err != nil {
panic(err)
}
}()
<-g.ctx.Done()
}()
return nil
}
func (g *Server) Stop() error {
g.cancel()
g.logger.Infoln("gin service stop")
return nil
}
func (g *Server) updateAppchain(c *gin.Context) {
g.sendAppchain(c, pb.Message_APPCHAIN_UPDATE)
}
func (g *Server) registerAppchain(c *gin.Context) {
g.sendAppchain(c, pb.Message_APPCHAIN_REGISTER)
}
func (g *Server) sendAppchain(c *gin.Context, appchainType pb.Message_Type) {
panic("implement me")
}
func (g *Server) getAppchain(c *gin.Context) {
panic("implement me")
}
This diff is collapsed.
package client
import (
"encoding/json"
"fmt"
"io/ioutil"
"strconv"
"github.com/link33/sidecar/internal/repo"
appchainmgr "github.com/meshplus/bitxhub-core/appchain-mgr"
"github.com/meshplus/bitxhub-kit/crypto"
"github.com/meshplus/bitxhub-kit/crypto/asym"
"github.com/urfave/cli"
)
type Approve struct {
Id string `json:"id"`
IsApproved int32 `json:"is_approved"`
Desc string `json:"desc"`
}
var clientCMD = cli.Command{
Name: "client",
Usage: "Command about appchain in sidecar",
Subcommands: []cli.Command{
{
Name: "register",
Usage: "Register appchain in sidecar",
Flags: []cli.Flag{
cli.StringFlag{
Name: "sidecar-id",
Usage: "Specify target sidecar id",
Required: true,
},
cli.StringFlag{
Name: "name",
Usage: "Specify appchain name",
Required: true,
},
cli.StringFlag{
Name: "type",
Usage: "Specify appchain type",
Required: true,
},
cli.StringFlag{
Name: "desc",
Usage: "Specify appchain description",
Required: true,
},
cli.StringFlag{
Name: "version",
Usage: "Specify appchain version",
Required: true,
},
cli.StringFlag{
Name: "validators",
Usage: "Specify appchain validators path",
Required: true,
},
cli.StringFlag{
Name: "consensus-type",
Usage: "Specify appchain consensus type",
Required: true,
},
},
Action: registerSidecarAppchain,
},
{
Name: "update",
Usage: "Update appchain in sidecar",
Flags: []cli.Flag{
cli.StringFlag{
Name: "sidecar-id",
Usage: "Specify target sidecar id",
Required: false,
},
cli.StringFlag{
Name: "name",
Usage: "Specify appchain name",
Required: false,
},
cli.StringFlag{
Name: "type",
Usage: "Specify appchain type",
Required: false,
},
cli.StringFlag{
Name: "desc",
Usage: "Specify appchain description",
Required: false,
},
cli.StringFlag{
Name: "version",
Usage: "Specify appchain version",
Required: false,
},
cli.StringFlag{
Name: "validators",
Usage: "Specify appchain validators path",
Required: false,
},
cli.StringFlag{
Name: "consensus-type",
Usage: "Specify appchain consensus type",
Required: false,
},
},
Action: updateSidecarAppchain,
},
{
Name: "audit",
Usage: "Audit appchain in sidecar",
Flags: []cli.Flag{
cli.StringFlag{
Name: "id",
Usage: "Specific appchain id",
Required: true,
},
cli.StringFlag{
Name: "is-approved",
Usage: "Specific approved signal",
Required: true,
},
cli.StringFlag{
Name: "desc",
Usage: "Specific audit description",
Required: true,
},
},
Action: auditSidecarAppchain,
},
{
Name: "get",
Usage: "Get appchain info",
Flags: []cli.Flag{
cli.StringFlag{
Name: "sidecar-id",
Usage: "Specific target sidecar id",
Required: true,
},
},
Action: getSidecarAppchain,
},
},
}
func LoadClientCMD() cli.Command {
return clientCMD
}
func registerSidecarAppchain(ctx *cli.Context) error {
return saveSidecarAppchain(ctx, RegisterAppchainUrl)
}
func updateSidecarAppchain(ctx *cli.Context) error {
return saveSidecarAppchain(ctx, UpdateAppchainUrl)
}
func auditSidecarAppchain(ctx *cli.Context) error {
id := ctx.String("id")
isApproved := ctx.String("is-approved")
desc := ctx.String("desc")
ia, err := strconv.ParseInt(isApproved, 0, 64)
if err != nil {
return fmt.Errorf("isApproved must be 0 or 1: %w", err)
}
approve := &Approve{
Id: id,
IsApproved: int32(ia),
Desc: desc,
}
data, err := json.Marshal(approve)
if err != nil {
return err
}
url, err := getURL(ctx, AuditAppchainUrl)
if err != nil {
return err
}
_, err = httpPost(url, data)
if err != nil {
return err
}
fmt.Printf("audit appchain %s successfully\n", id)
return nil
}
func saveSidecarAppchain(ctx *cli.Context, path string) error {
sidecar := ctx.String("sidecar-id")
name := ctx.String("name")
typ := ctx.String("type")
desc := ctx.String("desc")
version := ctx.String("version")
validatorsPath := ctx.String("validators")
consensusType := ctx.String("consensus-type")
url, err := getURL(ctx, fmt.Sprintf("%s?sidecar_id=%s", path, sidecar))
if err != nil {
return err
}
res, err := httpGet(url)
if err != nil {
return err
}
appchainInfo := appchainmgr.Appchain{}
if err = json.Unmarshal(res, &appchainInfo); err != nil {
return err
}
if name == "" {
name = appchainInfo.Name
}
if typ == "" {
typ = appchainInfo.ChainType
}
if desc == "" {
desc = appchainInfo.Desc
}
if version == "" {
version = appchainInfo.Version
}
validators := ""
if validatorsPath == "" {
validators = appchainInfo.Validators
} else {
data, err := ioutil.ReadFile(validatorsPath)
if err != nil {
return fmt.Errorf("read validators file: %w", err)
}
validators = string(data)
}
if consensusType == "" {
consensusType = appchainInfo.ConsensusType
}
repoRoot, err := repo.PathRootWithDefault(ctx.GlobalString("repo"))
if err != nil {
return err
}
pubKey, err := getPubKey(repo.KeyPath(repoRoot))
if err != nil {
return fmt.Errorf("get public key: %w", err)
}
addr, _ := pubKey.Address()
pubKeyBytes, _ := pubKey.Bytes()
appchain := &appchainmgr.Appchain{
ID: addr.String(),
Name: name,
Validators: validators,
ConsensusType: consensusType,
ChainType: typ,
Desc: desc,
Version: version,
PublicKey: string(pubKeyBytes),
}
data, err := json.Marshal(appchain)
if err != nil {
return fmt.Errorf("marshal appchain error: %w", err)
}
url, err = getURL(ctx, fmt.Sprintf("%s?sidecar_id=%s", path, sidecar))
if err != nil {
return err
}
resp, err := httpPost(url, data)
if err != nil {
return err
}
fmt.Println(parseResponse(resp))
return nil
}
func getSidecarAppchain(ctx *cli.Context) error {
targetSidecarID := ctx.String("sidecar-id")
url, err := getURL(ctx, fmt.Sprintf("%s?sidecar_id=%s", GetAppchainUrl, targetSidecarID))
if err != nil {
return err
}
res, err := httpGet(url)
if err != nil {
return err
}
fmt.Println(parseResponse(res))
return nil
}
func getPubKey(keyPath string) (crypto.PublicKey, error) {
privKey, err := asym.RestorePrivateKey(keyPath, "bitxhub")
if err != nil {
return nil, err
}
return privKey.PublicKey(), nil
}
package client
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"strings"
"github.com/link33/sidecar/internal/repo"
"github.com/urfave/cli"
)
const (
RegisterAppchainUrl = "appchain/register"
UpdateAppchainUrl = "appchain/update"
AuditAppchainUrl = "appchain/audit"
GetAppchainUrl = "appchain/get"
RegisterRuleUrl = "rule/register"
)
func httpGet(url string) ([]byte, error) {
/* #nosec */
resp, err := http.Get(url)
if err != nil {
return nil, err
}
c, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
err = resp.Body.Close()
if err != nil {
return nil, err
}
return c, nil
}
func httpPost(url string, data []byte) ([]byte, error) {
buffer := bytes.NewBuffer(data)
/* #nosec */
resp, err := http.Post(url, "application/json", buffer)
if err != nil {
return nil, err
}
c, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
err = resp.Body.Close()
if err != nil {
return nil, err
}
return c, nil
}
func getURL(ctx *cli.Context, path string) (string, error) {
repoRoot, err := repo.PathRootWithDefault(ctx.GlobalString("repo"))
if err != nil {
return "", err
}
api, err := repo.GetAPI(repoRoot)
if err != nil {
return "", fmt.Errorf("get api file: %w", err)
}
api = strings.TrimSpace(api)
return api + path, nil
}
package client
import (
"encoding/base64"
"fmt"
"github.com/tidwall/gjson"
)
func parseResponse(data []byte) (string, error) {
res := gjson.Get(string(data), "data")
ret, err := base64.StdEncoding.DecodeString(res.String())
if err != nil {
return "", fmt.Errorf("wrong data: %w", err)
}
return string(ret), nil
}
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
rpcx "github.com/link33/sidecar/hub/client"
"github.com/link33/sidecar/internal/repo"
"github.com/link33/sidecar/model/constant"
"github.com/meshplus/bitxhub-kit/crypto/asym"
"github.com/meshplus/bitxhub-kit/types"
"github.com/meshplus/bitxid"
"github.com/urfave/cli"
)
const (
bitxhubRootPrefix = "did:bitxhub"
relayRootSubMethod = "relayroot"
fakeSignature = "fake signature"
fakeDocAddr = "/ipfs/QmQVxzUqN2Yv2UHUQXYwH8dSNkM8ReJ9qPqwJsf8zzoNUi"
fakeDocHash = "QmQVxzUqN2Yv2UHUQXYwH8dSNkM8ReJ9qPqwJsf8zzoNUi"
)
type GovernanceResult struct {
ProposalID string `json:"proposal_id"`
Extra []byte `json:"extra"`
}
var methodCommand = cli.Command{
Name: "method",
Usage: "Command about appchain method",
Subcommands: []cli.Command{
{
Name: "register",
Usage: "Register appchain did method and info to bitxhub",
Flags: []cli.Flag{
adminKeyPathFlag,
methodFlag,
didDocAddrFlag,
didDocHashFlag,
appchainNameFlag,
appchainTypeFlag,
appchainDescFlag,
appchainVersionFlag,
appchainValidatorFlag,
appchainConsensusFlag,
},
Action: registerMethod,
},
},
}
var didCommand = cli.Command{
Name: "did",
Usage: "Command about appchain did",
Subcommands: []cli.Command{
{
Name: "register",
Usage: "Register appchain did in bitxhub",
Flags: []cli.Flag{
adminKeyPathFlag,
didFlag,
},
Action: registerDID,
},
{
Name: "audit",
Usage: "Audit registered appchain did info in bitxhub",
Flags: []cli.Flag{
adminKeyPathFlag,
didFlag,
statusFlag,
},
Action: auditDID,
},
},
}
func registerMethod(ctx *cli.Context) error {
method := ctx.String("method")
chainAdminKeyPath := ctx.String("admin-key")
didDocAddr := ctx.String("doc-addr")
didDocHash := ctx.String("doc-hash")
name := ctx.String("name")
typ := ctx.String("type")
desc := ctx.String("desc")
version := ctx.String("version")
validatorsPath := ctx.String("validators")
consensus := ctx.String("consensus")
validatorData, err := ioutil.ReadFile(validatorsPath)
if err != nil {
return fmt.Errorf("read validators file: %w", err)
}
// get repo public key
pubKey, err := getPubKey(chainAdminKeyPath)
if err != nil {
return fmt.Errorf("get public key: %w", err)
}
client, address, err := initClientWithKeyPath(ctx, chainAdminKeyPath)
if err != nil {
return err
}
appchainAdminDID := fmt.Sprintf("%s:%s:%s", bitxhubRootPrefix, method, address.String())
appchainMethod := fmt.Sprintf("%s:%s:.", bitxhubRootPrefix, method)
// init method registry with this admin key
receipt, err := client.InvokeBVMContract(
constant.AppchainMgrContractAddr.Address(),
"Register", nil,
rpcx.String(appchainAdminDID), rpcx.String(appchainMethod),
rpcx.String(didDocAddr), rpcx.String(didDocHash),
rpcx.String(string(validatorData)), rpcx.String(consensus), rpcx.String(typ),
rpcx.String(name), rpcx.String(desc), rpcx.String(version),
rpcx.String(pubKey),
)
if err != nil {
return fmt.Errorf("invoke bvm contract: %w", err)
}
if !receipt.IsSuccess() {
return fmt.Errorf("register method info faild: %s", string(receipt.Ret))
}
ret := &GovernanceResult{}
if err := json.Unmarshal(receipt.Ret, ret); err != nil {
return err
}
fmt.Printf("Register appchain method info for %s successfully, wait for proposal %s to finish.\n", string(ret.Extra), ret.ProposalID)
return nil
}
func registerDID(ctx *cli.Context) error {
did := ctx.String("did")
chainAdminKeyPath := ctx.String("admin-key")
client, address, err := initClientWithKeyPath(ctx, chainAdminKeyPath)
if err != nil {
return err
}
appchainDID := bitxid.DID(did)
method := appchainDID.GetRootMethod()
appchainAdminDID := fmt.Sprintf("%s:%s:%s", bitxhubRootPrefix, method, address.String())
receipt, err := client.InvokeBVMContract(
constant.DIDRegistryContractAddr.Address(),
"Register", nil, rpcx.String(appchainAdminDID),
rpcx.String(did), rpcx.String(fakeDocAddr),
rpcx.String(fakeDocHash), rpcx.Bytes([]byte(fakeSignature)),
)
if err != nil {
return fmt.Errorf("invoke bvm contract: %w", err)
}
if !receipt.IsSuccess() {
return fmt.Errorf("register did info faild: %s", string(receipt.Ret))
}
fmt.Printf("Register did doc info for %s successfully\n", did)
return nil
}
func auditDID(ctx *cli.Context) error {
// todo: wait for audit did info api in bitxhub to implement
return nil
}
func initClientWithKeyPath(ctx *cli.Context, chainAdminKeyPath string) (rpcx.Client, *types.Address, error) {
repoRoot, err := repo.PathRootWithDefault(ctx.GlobalString("repo"))
if err != nil {
return nil, nil, err
}
config, err := repo.UnmarshalConfig(repoRoot)
if err != nil {
return nil, nil, fmt.Errorf("init config error: %s", err)
}
adminPriv, err := asym.RestorePrivateKey(chainAdminKeyPath, "bitxhub")
if err != nil {
return nil, nil, err
}
address, err := adminPriv.PublicKey().Address()
if err != nil {
return nil, nil, err
}
client, err := loadClient(chainAdminKeyPath, config.Peer.Peers, ctx)
if err != nil {
return nil, nil, fmt.Errorf("load client: %w", err)
}
return client, address, nil
}
package main
import "github.com/urfave/cli"
var (
bxhAddrFlag = cli.StringFlag{
Name: "addr",
Usage: "Specific bitxhub node address",
Value: "localhost:60011",
Required: false,
}
adminKeyPathFlag = cli.StringFlag{
Name: "admin-key",
Usage: "Specific admin key path",
Required: true,
}
methodFlag = cli.StringFlag{
Name: "method",
Usage: "Specific did sub method name(like appchain)",
Required: true,
}
didFlag = cli.StringFlag{
Name: "did",
Usage: "Specific full did name(like did:bitxhub:appchain1:0xc7F999b83Af6DF9e67d0a37Ee7e900bF38b3D013)",
Required: true,
}
statusFlag = cli.IntFlag{
Name: "status",
Usage: "Specify the status you want to set(1 is pass, 0 is reject, default is 1)",
Required: false,
Value: 1,
}
didDocAddrFlag = cli.StringFlag{
Name: "doc-addr",
Usage: "Specify the addr of did document",
Required: true,
}
didDocHashFlag = cli.StringFlag{
Name: "doc-hash",
Usage: "Specify the hash of did document",
Required: true,
}
// appchain info related flags
appchainNameFlag = cli.StringFlag{
Name: "name",
Usage: "Specific appchain name",
Required: true,
}
appchainTypeFlag = cli.StringFlag{
Name: "type",
Usage: "Specific appchain type",
Required: true,
}
appchainDescFlag = cli.StringFlag{
Name: "desc",
Usage: "Specific appchain description",
Required: true,
}
appchainVersionFlag = cli.StringFlag{
Name: "version",
Usage: "Specific appchain version",
Required: true,
}
appchainValidatorFlag = cli.StringFlag{
Name: "validators",
Usage: "Specific appchain validators path",
Required: true,
}
appchainConsensusFlag = cli.StringFlag{
Name: "consensus",
Usage: "Specific appchain consensus type",
Required: true,
}
)
package main
import (
"fmt"
rpcx "github.com/link33/sidecar/hub/client"
"github.com/link33/sidecar/model/constant"
"github.com/urfave/cli"
)
var governanceCMD = cli.Command{
Name: "proposals",
Usage: "proposals manage command",
Subcommands: cli.Commands{
cli.Command{
Name: "withdraw",
Usage: "withdraw a proposal",
Flags: []cli.Flag{
adminKeyPathFlag,
cli.StringFlag{
Name: "id",
Usage: "proposal id",
Required: true,
},
},
Action: withdraw,
},
},
}
// TODO
func withdraw(ctx *cli.Context) error {
chainAdminKeyPath := ctx.String("admin-key")
id := ctx.String("id")
client, _, err := initClientWithKeyPath(ctx, chainAdminKeyPath)
if err != nil {
return fmt.Errorf("load client: %w", err)
}
// TODO modify hub client
receipt, err := client.InvokeBVMContract(
constant.GovernanceContractAddr.Address(),
"WithdrawProposal", nil, rpcx.String(id),
)
if err != nil {
return fmt.Errorf("invoke bvm contract: %w", err)
}
if !receipt.IsSuccess() {
return fmt.Errorf("invoke withdraw proposal: %s", receipt.Ret)
}
return nil
}
package main
import (
"fmt"
"path/filepath"
"github.com/link33/sidecar/internal/repo"
"github.com/meshplus/bitxhub-kit/crypto/asym"
"github.com/urfave/cli"
)
var idCMD = cli.Command{
Name: "id",
Usage: "Get appchain id",
Action: func(ctx *cli.Context) error {
repoRoot, err := repo.PathRootWithDefault(ctx.GlobalString("repo"))
if err != nil {
return err
}
keyPath := filepath.Join(repoRoot, "key.json")
privKey, err := asym.RestorePrivateKey(keyPath, "bitxhub")
if err != nil {
return err
}
address, err := privKey.PublicKey().Address()
if err != nil {
return err
}
fmt.Println(address.String())
return nil
},
}
package main
import (
"bufio"
"fmt"
"os"
"path/filepath"
"github.com/link33/sidecar/internal/repo"
"github.com/meshplus/bitxhub-kit/fileutil"
"github.com/urfave/cli"
)
var initCMD = cli.Command{
Name: "init",
Usage: "Initialize sidecar local configuration",
Action: func(ctx *cli.Context) error {
repoRoot, err := repo.PathRootWithDefault(ctx.GlobalString("repo"))
if err != nil {
return err
}
if fileutil.Exist(filepath.Join(repoRoot, repo.ConfigName)) {
fmt.Println("sidecar configuration file already exists")
fmt.Println("reinitializing would overwrite your configuration, Y/N?")
input := bufio.NewScanner(os.Stdin)
input.Scan()
if input.Text() == "Y" || input.Text() == "y" {
return repo.Initialize(repoRoot)
}
return nil
}
return repo.Initialize(repoRoot)
},
}
package main
import (
"fmt"
rpcx "github.com/link33/sidecar/hub/client"
"github.com/link33/sidecar/internal/repo"
"github.com/link33/sidecar/model/constant"
"github.com/meshplus/bitxhub-kit/types"
"github.com/urfave/cli"
)
var interchainCMD = cli.Command{
Name: "interchain",
Usage: "Query interchain info",
Flags: []cli.Flag{
cli.StringFlag{
Name: "key",
Usage: "Specific key.json path",
Required: true,
},
},
Subcommands: []cli.Command{
{
Name: "ibtp",
Usage: "Query ibtp by id",
Flags: []cli.Flag{
cli.StringFlag{
Name: "id",
Usage: "Specific ibtp id",
Required: true,
},
},
Action: getIBTP,
},
},
}
func getIBTP(ctx *cli.Context) error {
id := ctx.String("id")
repoRoot, err := repo.PathRootWithDefault(ctx.GlobalString("repo"))
if err != nil {
return err
}
config, err := repo.UnmarshalConfig(repoRoot)
if err != nil {
return fmt.Errorf("init config error: %s", err)
}
client, err := loadClient(repo.KeyPath(repoRoot), config.Peer.Peers, ctx)
if err != nil {
return fmt.Errorf("load client: %w", err)
}
receipt, err := client.InvokeBVMContract(
constant.InterchainContractAddr.Address(),
"GetIBTPByID", nil,
rpcx.String(id),
)
if err != nil {
return err
}
hash := types.NewHash(receipt.Ret)
fmt.Printf("Tx hash: %s\n", hash.String())
response, err := client.GetTransaction(hash.String())
if err != nil {
return err
}
fmt.Println(response)
return nil
}
package main
import (
"crypto"
"fmt"
crypto2 "github.com/libp2p/go-libp2p-core/crypto"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/link33/sidecar/internal/repo"
"github.com/meshplus/bitxhub-kit/crypto/asym"
"github.com/meshplus/bitxhub-kit/crypto/asym/ecdsa"
"github.com/urfave/cli"
)
var p2pCMD = cli.Command{
Name: "p2p",
Usage: "Command about p2p",
Subcommands: []cli.Command{
{
Name: "id",
Usage: "get sidecar unique id in p2p network",
Action: p2pID,
},
},
}
func p2pID(ctx *cli.Context) error {
repoRoot, err := repo.PathRootWithDefault(ctx.GlobalString("repo"))
if err != nil {
return err
}
privKey, err := repo.LoadNodePrivateKey(repoRoot)
if err != nil {
return err
}
stdKey, err := asym.PrivKeyToStdKey(privKey)
if err != nil {
return err
}
_, pk, err := crypto2.KeyPairFromStdKey(&stdKey)
if err != nil {
return err
}
id, err := peer.IDFromPublicKey(pk)
if err != nil {
return err
}
fmt.Println(id)
return nil
}
func convertToLibp2pPrivKey(privateKey crypto.PrivateKey) (crypto2.PrivKey, error) {
ecdsaPrivKey, ok := privateKey.(*ecdsa.PrivateKey)
if !ok {
return nil, fmt.Errorf("convert to libp2p private key: not ecdsa private key")
}
libp2pPrivKey, _, err := crypto2.ECDSAKeyPairFromKey(ecdsaPrivKey.K)
if err != nil {
return nil, err
}
return libp2pPrivKey, nil
}
package main
import (
"fmt"
"io/ioutil"
"github.com/fatih/color"
rpcx "github.com/link33/sidecar/hub/client"
"github.com/link33/sidecar/model/constant"
"github.com/tidwall/gjson"
"github.com/urfave/cli"
)
var ruleCMD = cli.Command{
Name: "rule",
Usage: "Command about rule",
Subcommands: cli.Commands{
{
Name: "deploy",
Usage: "Deploy validation rule",
Flags: []cli.Flag{
cli.StringFlag{
Name: "path",
Usage: "Specific rule path",
Required: true,
},
methodFlag,
adminKeyPathFlag,
},
Action: deployRule,
},
{
Name: "update",
Usage: "update master rule",
Flags: []cli.Flag{
cli.StringFlag{
Name: "addr",
Usage: "Specific rule addr",
Required: true,
},
methodFlag,
adminKeyPathFlag,
},
Action: updateMasterRule,
},
{
Name: "logout",
Usage: "logout validation rule",
Flags: []cli.Flag{
cli.StringFlag{
Name: "addr",
Usage: "Specific rule addr",
Required: true,
},
methodFlag,
adminKeyPathFlag,
},
Action: logoutRule,
},
},
}
func deployRule(ctx *cli.Context) error {
rulePath := ctx.String("path")
method := ctx.String("method")
chainAdminKeyPath := ctx.String("admin-key")
client, _, err := initClientWithKeyPath(ctx, chainAdminKeyPath)
if err != nil {
return fmt.Errorf("Load client: %w", err)
}
contract, err := ioutil.ReadFile(rulePath)
if err != nil {
return err
}
// 1. deploy
contractAddr, err := client.DeployContract(contract, nil)
if err != nil {
color.Red("Deploy rule error: %w", err)
return nil
} else {
color.Green(fmt.Sprintf("Deploy rule to bitxhub for appchain %s successfully: %s", method, contractAddr.String()))
}
// 2. register
appchainMethod := fmt.Sprintf("%s:%s:.", bitxhubRootPrefix, method)
receipt, err := client.InvokeBVMContract(
constant.RuleManagerContractAddr.Address(),
"RegisterRule", nil,
rpcx.String(appchainMethod), rpcx.String(contractAddr.String()))
if err != nil {
return fmt.Errorf("Register rule: %w", err)
}
if !receipt.IsSuccess() {
color.Red(fmt.Sprintf("Register rule to bitxhub for appchain %s error: %s", appchainMethod, string(receipt.Ret)))
} else {
proposalId := gjson.Get(string(receipt.Ret), "proposal_id").String()
if proposalId != "" {
color.Green(fmt.Sprintf("Register rule to bitxhub for appchain %s successfully, the bind request was submitted successfully, wait for proposal %s to finish.", appchainMethod, proposalId))
} else {
color.Green(fmt.Sprintf("Register rule to bitxhub for appchain %s successfully.", appchainMethod))
}
}
return nil
}
func updateMasterRule(ctx *cli.Context) error {
ruleAddr := ctx.String("addr")
method := ctx.String("method")
chainAdminKeyPath := ctx.String("admin-key")
client, _, err := initClientWithKeyPath(ctx, chainAdminKeyPath)
if err != nil {
return fmt.Errorf("Load client: %w", err)
}
appchainMethod := fmt.Sprintf("%s:%s:.", bitxhubRootPrefix, method)
receipt, err := client.InvokeBVMContract(
constant.RuleManagerContractAddr.Address(),
"UpdateMasterRule", nil,
rpcx.String(appchainMethod), rpcx.String(ruleAddr))
if err != nil {
return fmt.Errorf("Update master rule: %w", err)
}
if !receipt.IsSuccess() {
color.Red(fmt.Sprintf("Update master rule to bitxhub for appchain %s error: %s", appchainMethod, string(receipt.Ret)))
} else {
proposalId := gjson.Get(string(receipt.Ret), "proposal_id").String()
color.Green(fmt.Sprintf("Update master rule to bitxhub for appchain %s successfully, wait for proposal %s to finish.", appchainMethod, proposalId))
}
return nil
}
//TODO
func bindRule(ctx *cli.Context) error {
ruleAddr := ctx.String("addr")
method := ctx.String("method")
chainAdminKeyPath := ctx.String("admin-key")
client, _, err := initClientWithKeyPath(ctx, chainAdminKeyPath)
if err != nil {
return fmt.Errorf("Load client: %w", err)
}
appchainMethod := fmt.Sprintf("%s:%s:.", bitxhubRootPrefix, method)
receipt, err := client.InvokeBVMContract(
constant.RuleManagerContractAddr.Address(),
"BindRule", nil,
rpcx.String(appchainMethod), rpcx.String(ruleAddr))
if err != nil {
return fmt.Errorf("Bind rule: %w", err)
}
if !receipt.IsSuccess() {
color.Red(fmt.Sprintf("Bind rule to bitxhub for appchain %s error: %s", appchainMethod, string(receipt.Ret)))
} else {
proposalId := gjson.Get(string(receipt.Ret), "proposal_id").String()
color.Green(fmt.Sprintf("Bind rule to bitxhub for appchain %s successfully, wait for proposal %s to finish.", appchainMethod, proposalId))
}
return nil
}
func unbindRule(ctx *cli.Context) error {
ruleAddr := ctx.String("addr")
method := ctx.String("method")
chainAdminKeyPath := ctx.String("admin-key")
client, _, err := initClientWithKeyPath(ctx, chainAdminKeyPath)
if err != nil {
return fmt.Errorf("Load client: %w", err)
}
appchainMethod := fmt.Sprintf("%s:%s:.", bitxhubRootPrefix, method)
receipt, err := client.InvokeBVMContract(
constant.RuleManagerContractAddr.Address(),
"UnbindRule", nil,
rpcx.String(appchainMethod), rpcx.String(ruleAddr))
if err != nil {
return fmt.Errorf("Unind rule: %w", err)
}
if !receipt.IsSuccess() {
color.Red(fmt.Sprintf("Unbind rule to bitxhub for appchain %s error: %s", appchainMethod, string(receipt.Ret)))
} else {
proposalId := gjson.Get(string(receipt.Ret), "proposal_id").String()
color.Green(fmt.Sprintf("Unbind rule to bitxhub for appchain %s successfully, wait for proposal %s to finish.", appchainMethod, proposalId))
}
return nil
}
func freezeRule(ctx *cli.Context) error {
ruleAddr := ctx.String("addr")
method := ctx.String("method")
chainAdminKeyPath := ctx.String("admin-key")
client, _, err := initClientWithKeyPath(ctx, chainAdminKeyPath)
if err != nil {
return fmt.Errorf("Load client: %w", err)
}
appchainMethod := fmt.Sprintf("%s:%s:.", bitxhubRootPrefix, method)
receipt, err := client.InvokeBVMContract(
constant.RuleManagerContractAddr.Address(),
"FreezeRule", nil,
rpcx.String(appchainMethod), rpcx.String(ruleAddr))
if err != nil {
return fmt.Errorf("Freeze rule: %w", err)
}
if !receipt.IsSuccess() {
color.Red(fmt.Sprintf("Freeze rule to bitxhub for appchain %s error: %s", appchainMethod, string(receipt.Ret)))
} else {
proposalId := gjson.Get(string(receipt.Ret), "proposal_id").String()
color.Green(fmt.Sprintf("Freeze rule to bitxhub for appchain %s successfully, wait for proposal %s to finish.", appchainMethod, proposalId))
}
return nil
}
func activateRule(ctx *cli.Context) error {
ruleAddr := ctx.String("addr")
method := ctx.String("method")
chainAdminKeyPath := ctx.String("admin-key")
client, _, err := initClientWithKeyPath(ctx, chainAdminKeyPath)
if err != nil {
return fmt.Errorf("Load client: %w", err)
}
appchainMethod := fmt.Sprintf("%s:%s:.", bitxhubRootPrefix, method)
receipt, err := client.InvokeBVMContract(
constant.RuleManagerContractAddr.Address(),
"ActivateRule", nil,
rpcx.String(appchainMethod), rpcx.String(ruleAddr))
if err != nil {
return fmt.Errorf("Activate rule: %w", err)
}
if !receipt.IsSuccess() {
color.Red(fmt.Sprintf("Activate rule to bitxhub for appchain %s error: %s", appchainMethod, string(receipt.Ret)))
} else {
proposalId := gjson.Get(string(receipt.Ret), "proposal_id").String()
color.Green(fmt.Sprintf("Activate rule to bitxhub for appchain %s successfully, wait for proposal %s to finish.", appchainMethod, proposalId))
}
return nil
}
func logoutRule(ctx *cli.Context) error {
ruleAddr := ctx.String("addr")
method := ctx.String("method")
chainAdminKeyPath := ctx.String("admin-key")
client, _, err := initClientWithKeyPath(ctx, chainAdminKeyPath)
if err != nil {
return fmt.Errorf("Load client: %w", err)
}
appchainMethod := fmt.Sprintf("%s:%s:.", bitxhubRootPrefix, method)
receipt, err := client.InvokeBVMContract(
constant.RuleManagerContractAddr.Address(),
"LogoutRule", nil,
rpcx.String(appchainMethod), rpcx.String(ruleAddr))
if err != nil {
return fmt.Errorf("Logout rule: %w", err)
}
if !receipt.IsSuccess() {
color.Red(fmt.Sprintf("Logout rule to bitxhub for appchain %s error: %s", appchainMethod, string(receipt.Ret)))
} else {
color.Green("The logout request was submitted successfully\n")
}
return nil
}
package main
import (
"os"
"time"
"github.com/fatih/color"
"github.com/link33/sidecar/cmd/sidecar/client"
"github.com/meshplus/bitxhub-kit/log"
"github.com/urfave/cli"
)
var logger = log.NewWithModule("cmd")
func main() {
app := cli.NewApp()
app.Name = "Sidecar"
app.Usage = "Manipulate the crosschain node"
app.Compiled = time.Now()
// global flags
app.Flags = []cli.Flag{
cli.StringFlag{
Name: "repo",
Usage: "Sidecar repository path",
},
cli.BoolFlag{
Name: "tls",
Usage: "enable tls between sidecar and bitxhub or not",
},
}
app.Commands = []cli.Command{
appchainBxhCMD,
client.LoadClientCMD(),
idCMD,
initCMD,
interchainCMD,
p2pCMD,
//ruleCMD,
startCMD,
versionCMD,
governanceCMD,
}
err := app.Run(os.Args)
if err != nil {
color.Red(err.Error())
os.Exit(-1)
}
}
package main
import (
"fmt"
"github.com/link33/sidecar/internal"
"net/http"
_ "net/http/pprof"
"os"
"os/signal"
"path/filepath"
"sync"
"syscall"
"time"
"github.com/link33/sidecar/internal/app"
"github.com/link33/sidecar/internal/loggers"
"github.com/link33/sidecar/internal/repo"
"github.com/meshplus/bitxhub-kit/log"
"github.com/urfave/cli"
)
var (
startCMD = cli.Command{
Name: "start",
Usage: "Start a long-running daemon process",
Action: start,
}
)
func start(ctx *cli.Context) error {
fmt.Println(getVersion(true))
repoRoot, err := repo.PathRootWithDefault(ctx.GlobalString("repo"))
if err != nil {
return err
}
repo.SetPath(repoRoot)
config, err := repo.UnmarshalConfig(repoRoot)
if err != nil {
return fmt.Errorf("init config error: %s", err)
}
err = log.Initialize(
log.WithReportCaller(config.Log.ReportCaller),
log.WithPersist(true),
log.WithFilePath(filepath.Join(repoRoot, config.Log.Dir)),
log.WithFileName(config.Log.Filename),
log.WithMaxSize(2*1024*1024),
log.WithMaxAge(24*time.Hour),
log.WithRotationTime(24*time.Hour),
)
if err != nil {
return fmt.Errorf("log initialize: %w", err)
}
// init loggers map for sidecar
loggers.InitializeLogger(config)
var sidecar internal.Launcher
sidecar, err = app.NewSidecar(repoRoot, config)
if err != nil {
return err
}
runPProf(config.Port.PProf)
var wg sync.WaitGroup
wg.Add(1)
handleShutdown(sidecar, &wg)
if err := sidecar.Start(); err != nil {
return err
}
wg.Wait()
logger.Info("Sidecar exits")
return nil
}
func handleShutdown(sidecar internal.Launcher, wg *sync.WaitGroup) {
var stop = make(chan os.Signal)
signal.Notify(stop, syscall.SIGTERM)
signal.Notify(stop, syscall.SIGINT)
go func() {
<-stop
fmt.Println("received interrupt signal, shutting down...")
if err := sidecar.Stop(); err != nil {
logger.Error("sidecar stop: ", err)
}
wg.Done()
os.Exit(0)
}()
}
func runPProf(port int64) {
go func() {
addr := fmt.Sprintf("localhost:%d", port)
fmt.Printf("Pprof on localhost:%d\n\n", port)
err := http.ListenAndServe(addr, nil)
if err != nil {
fmt.Println(err)
panic(err)
}
}()
}
func checkPlugin(pluginName string) error {
// check if plugin exists
pluginRoot, err := repo.PluginPath()
if err != nil {
return err
}
pluginPath := filepath.Join(pluginRoot, pluginName)
_, err = os.Stat(pluginPath)
if err != nil {
if os.IsNotExist(err) {
return fmt.Errorf("plugin file `%s` is required", pluginPath)
}
return fmt.Errorf("get plugin file state error: %w", err)
}
return nil
}
package main
import (
"fmt"
"github.com/link33/sidecar"
"github.com/urfave/cli"
)
var versionCMD = cli.Command{
Name: "version",
Usage: "Show version about ap",
Action: func(ctx *cli.Context) error {
fmt.Print(getVersion(true))
return nil
},
}
func getVersion(all bool) string {
version := fmt.Sprintf("Sidecar version: %s-%s-%s\n", sidecar.CurrentVersion, sidecar.CurrentBranch, sidecar.CurrentCommit)
if all {
version += fmt.Sprintf("App build date: %s\n", sidecar.BuildDate)
version += fmt.Sprintf("System version: %s\n", sidecar.Platform)
version += fmt.Sprintf("Golang version: %s\n", sidecar.GoVersion)
}
return version
}
http://localhost:8080/v1/
\ No newline at end of file
CONNECTED(00000005)
---
Certificate chain
0 s:/C=CN/ST=ZJ/L=HZ/O=Hyperchain/OU=DM/CN=localhost/emailAddress=datamesh@hyperchain.cn
i:/C=CN/ST=ZJ/L=HZ/O=Hyperchain/OU=DM/CN=localhost/emailAddress=datamesh@hyperchain.cn
-----BEGIN CERTIFICATE-----
MIIDhjCCAm4CCQD0t1EOr68uHTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMC
Q04xCzAJBgNVBAgMAlpKMQswCQYDVQQHDAJIWjETMBEGA1UECgwKSHlwZXJjaGFp
bjELMAkGA1UECwwCRE0xEjAQBgNVBAMMCWxvY2FsaG9zdDElMCMGCSqGSIb3DQEJ
ARYWZGF0YW1lc2hAaHlwZXJjaGFpbi5jbjAeFw0yMDExMTYwMzAwMDJaFw0yMTEx
MTYwMzAwMDJaMIGEMQswCQYDVQQGEwJDTjELMAkGA1UECAwCWkoxCzAJBgNVBAcM
AkhaMRMwEQYDVQQKDApIeXBlcmNoYWluMQswCQYDVQQLDAJETTESMBAGA1UEAwwJ
bG9jYWxob3N0MSUwIwYJKoZIhvcNAQkBFhZkYXRhbWVzaEBoeXBlcmNoYWluLmNu
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvUURnJYTyByepyuLU2wd
qsTfNtdty5LFjwhuW2JJquQt+qeVOq9y65nKnr/8x1dIE4Z5zmWh2bpTzddT6KIt
lhL1zJY/zF3pbVjUsSiPLfSFRvHQiltUZspKgGij/Tb9I/4hy7vvv95ff6v7C3kg
qFmUzxoRUGil2PoJ88vyDit/coSBIgqTR9QonutL1RIgurraTcGHLPpRqFuPouvU
8aMN8dW2kjqW+YwsnxcpVXegDGDp9k8i2iDcwpMF6C8n7il47O8LWmAMpcT7nsPb
KBiseXMzhNspt8r+otE7T0wAfTKNIVVuEkq/1HQZBUPtnhpTJREHI1kX3Znt4eIk
gwIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQBL0RiqRWBTgeGa0WuncESbg6wy37u3
yWKAbMXGQqiAgDa60cl8H7VOjSgEgFF7ueM4qI7Gg63tqEvYQMfJC7iLnZ0uxlzK
aKwhFVTCBUOdwR1WJrYjO9NpvgR5IzJS4gBI2SixHfKnDAt9kW5ultak+CtN5DLZ
c0xMqay/cZ4Pt3o0yLxYEJrHvhZIeGpYhsBQWngmJKxTVadHSGMbqKX7O+iza4pD
b6r3Uz8HLl2oFmn/T1b5RhuuMS7diQ5y8HzNRYTBdzNmIGovGWg0GQBTZQrUZh6J
75fuj5+DANruj0/MTZqGI71LpqD7QmrKl5Ii+CBknYZPXLnXgw/rGhXE
-----END CERTIFICATE-----
---
Server certificate
subject=/C=CN/ST=ZJ/L=HZ/O=Hyperchain/OU=DM/CN=localhost/emailAddress=datamesh@hyperchain.cn
issuer=/C=CN/ST=ZJ/L=HZ/O=Hyperchain/OU=DM/CN=localhost/emailAddress=datamesh@hyperchain.cn
---
No client certificate CA names sent
Server Temp Key: ECDH, X25519, 253 bits
---
SSL handshake has read 1485 bytes and written 307 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES128-GCM-SHA256
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
Protocol : TLSv1.2
Cipher : ECDHE-RSA-AES128-GCM-SHA256
Session-ID: EF7931988E269C85760BEBBC0379299C1148C3AE8140EA1FD14745C3F0AA4527
Session-ID-ctx:
Master-Key: 08E910F7086C2848818F800ACDBE6D00FE55A896188CDDE281158B1C02D7A458269861E584A3FAECB610BC59C487F322
TLS session ticket:
0000 - 60 f7 27 df 8c 98 71 e3-48 70 29 6f 56 3b 6f 85 `.'...q.Hp)oV;o.
0010 - 16 1f 90 8b 5b 94 c3 c0-94 1c 9d dd e1 83 3f 3c ....[.........?<
0020 - ff cb 93 11 44 ff 57 9d-37 97 8d 8e 04 e2 19 bc ....D.W.7.......
0030 - 6f 75 df b5 fd 00 bb ee-ba 56 c6 fd b2 21 8d ae ou.......V...!..
0040 - f5 39 94 82 69 fa 7f 97-c5 3f 4f 50 20 0f dc d8 .9..i....?OP ...
0050 - 60 00 a0 37 f8 59 19 4d-5c 87 08 e6 37 82 b9 34 `..7.Y.M\...7..4
0060 - 3c 56 38 f1 db cf 4f e1-99 7a dd 50 b0 a8 7a 83 <V8...O..z.P..z.
0070 - 26 82 80 8d 2e ec 0b 67- &......g
Start Time: 1605496292
Timeout : 7200 (sec)
Verify return code: 18 (self signed certificate)
---
\ No newline at end of file
title = "Sidecar"
[port]
http = 44544
pprof = 44555
[log]
level = "info"
dir = "logs"
filename = "sidecar.log"
report_caller = false
[log.module]
api_server = "info"
appchain_mgr = "info"
bxh_lite = "info"
executor = "info"
exchanger = "info"
monitor = "info"
peer_mgr = "info"
router = "info"
rule_mgr = "info"
swarm = "info"
syncer = "info"
#[hub]
#enable = true
#type = "hub"
#did = "did:bitxhub:fabappchain:."
#plugin = "appchain_plugin"
#config = "hub"
#addrs = ["localhost:60011"]
#[security]
#enable_tls = false
#tlsca = "certs/ca.pem"
#common_name = "localhost"
[[appchain]]
enable = true
type = "appchain"
did = "did:bitxhub:fabappchain:."
plugin = "appchain_plugin"
config = "fabric"
[[appchain]]
enable = true
type = "hub"
did = "did:bitxhub:chain33:."
plugin = "appchain_plugin"
config = "chain33"
[peer]
peers = ["localhost:60011", "localhost:60012", "localhost:60013", "localhost:60014"]
connectors = []
providers = 1
module github.com/link33/sidecar
go 1.17
require (
github.com/Rican7/retry v0.1.0
github.com/btcsuite/btcd v0.21.0-beta
github.com/cbergoon/merkletree v0.2.0
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
github.com/fatih/color v1.9.0
github.com/fsnotify/fsnotify v1.4.9
github.com/gin-gonic/gin v1.6.3
github.com/gobuffalo/packd v1.0.0
github.com/gobuffalo/packr v1.30.1
github.com/gogo/protobuf v1.3.2
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7 // indirect
github.com/golang/mock v1.5.0
github.com/golang/protobuf v1.4.3
github.com/golang/snappy v0.0.3-0.20201103224600-674baa8c7fc3 // indirect
github.com/google/go-cmp v0.5.4 // indirect
github.com/google/uuid v1.1.5 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.16.0
github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd
github.com/hashicorp/go-plugin v1.3.0
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect
github.com/huin/goupnp v1.0.1-0.20210310174557-0ca763054c88 // indirect
github.com/ipfs/go-cid v0.0.7
github.com/ipfs/go-ipfs-util v0.0.2 // indirect
github.com/lestrrat-go/strftime v1.0.3 // indirect
github.com/libp2p/go-libp2p-core v0.6.1
github.com/meshplus/bitxhub-core v1.3.1-0.20210524071255-789fd9ab501c
github.com/meshplus/bitxhub-kit v1.2.1-0.20210524063043-9afae78ac098
github.com/meshplus/bitxid v0.0.0-20210412025850-e0eaf0f9063a
github.com/meshplus/go-lightp2p v0.0.0-20200817105923-6b3aee40fa54
github.com/mitchellh/go-homedir v1.1.0
github.com/multiformats/go-multiaddr v0.3.0
github.com/multiformats/go-multiaddr-net v0.2.0 // indirect
github.com/sirupsen/logrus v1.6.0
github.com/smartystreets/goconvey v1.6.4
github.com/spf13/viper v1.7.0
github.com/stretchr/testify v1.7.0
github.com/tidwall/gjson v1.6.8
github.com/urfave/cli v1.22.1
go.uber.org/atomic v1.6.0
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f // indirect
golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d // indirect
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c // indirect
golang.org/x/text v0.3.4 // indirect
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013
google.golang.org/grpc v1.33.1
)
require (
github.com/coreos/go-semver v0.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/davidlazar/go-crypto v0.0.0-20190912175916-7055855a373f // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.13.0 // indirect
github.com/go-playground/universal-translator v0.17.0 // indirect
github.com/go-playground/validator/v10 v10.2.0 // indirect
github.com/gobuffalo/envy v1.7.0 // indirect
github.com/google/gopacket v1.1.17 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-multierror v1.1.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect
github.com/ipfs/go-datastore v0.4.4 // indirect
github.com/ipfs/go-ipns v0.0.2 // indirect
github.com/ipfs/go-log v1.0.4 // indirect
github.com/ipfs/go-log/v2 v2.0.5 // indirect
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect
github.com/jbenet/goprocess v0.1.4 // indirect
github.com/joho/godotenv v1.3.0 // indirect
github.com/json-iterator/go v1.1.9 // indirect
github.com/konsorten/go-windows-terminal-sequences v1.0.3 // indirect
github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d // indirect
github.com/leodido/go-urn v1.2.0 // indirect
github.com/lestrrat-go/file-rotatelogs v2.2.0+incompatible // indirect
github.com/libp2p/go-addr-util v0.0.2 // indirect
github.com/libp2p/go-buffer-pool v0.0.2 // indirect
github.com/libp2p/go-conn-security-multistream v0.2.0 // indirect
github.com/libp2p/go-eventbus v0.1.0 // indirect
github.com/libp2p/go-flow-metrics v0.0.3 // indirect
github.com/libp2p/go-libp2p v0.9.2 // indirect
github.com/libp2p/go-libp2p-autonat v0.2.3 // indirect
github.com/libp2p/go-libp2p-blankhost v0.1.6 // indirect
github.com/libp2p/go-libp2p-circuit v0.2.2 // indirect
github.com/libp2p/go-libp2p-crypto v0.1.0 // indirect
github.com/libp2p/go-libp2p-discovery v0.4.0 // indirect
github.com/libp2p/go-libp2p-kad-dht v0.8.2 // indirect
github.com/libp2p/go-libp2p-kbucket v0.4.2 // indirect
github.com/libp2p/go-libp2p-loggables v0.1.0 // indirect
github.com/libp2p/go-libp2p-mplex v0.2.3 // indirect
github.com/libp2p/go-libp2p-nat v0.0.6 // indirect
github.com/libp2p/go-libp2p-peerstore v0.2.4 // indirect
github.com/libp2p/go-libp2p-pnet v0.2.0 // indirect
github.com/libp2p/go-libp2p-record v0.1.2 // indirect
github.com/libp2p/go-libp2p-routing-helpers v0.2.3 // indirect
github.com/libp2p/go-libp2p-secio v0.2.2 // indirect
github.com/libp2p/go-libp2p-swarm v0.2.4 // indirect
github.com/libp2p/go-libp2p-tls v0.1.3 // indirect
github.com/libp2p/go-libp2p-transport-upgrader v0.3.0 // indirect
github.com/libp2p/go-libp2p-yamux v0.2.7 // indirect
github.com/libp2p/go-mplex v0.1.2 // indirect
github.com/libp2p/go-msgio v0.0.4 // indirect
github.com/libp2p/go-nat v0.0.5 // indirect
github.com/libp2p/go-netroute v0.1.2 // indirect
github.com/libp2p/go-openssl v0.0.5 // indirect
github.com/libp2p/go-reuseport v0.0.1 // indirect
github.com/libp2p/go-reuseport-transport v0.0.3 // indirect
github.com/libp2p/go-sockaddr v0.1.0 // indirect
github.com/libp2p/go-stream-muxer-multistream v0.3.0 // indirect
github.com/libp2p/go-tcp-transport v0.2.0 // indirect
github.com/libp2p/go-ws-transport v0.3.1 // indirect
github.com/libp2p/go-yamux v1.3.6 // indirect
github.com/looplab/fsm v0.2.0 // indirect
github.com/magiconair/properties v1.8.1 // indirect
github.com/mattn/go-colorable v0.1.4 // indirect
github.com/mattn/go-isatty v0.0.12 // indirect
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 // indirect
github.com/minio/sha256-simd v0.1.1 // indirect
github.com/mitchellh/go-testing-interface v1.0.0 // indirect
github.com/mitchellh/mapstructure v1.1.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/mr-tron/base58 v1.1.3 // indirect
github.com/multiformats/go-base32 v0.0.3 // indirect
github.com/multiformats/go-base36 v0.1.0 // indirect
github.com/multiformats/go-multiaddr-dns v0.2.0 // indirect
github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect
github.com/multiformats/go-multibase v0.0.3 // indirect
github.com/multiformats/go-multihash v0.0.14 // indirect
github.com/multiformats/go-multistream v0.1.1 // indirect
github.com/multiformats/go-varint v0.0.6 // indirect
github.com/oklog/run v1.0.0 // indirect
github.com/opentracing/opentracing-go v1.1.0 // indirect
github.com/pelletier/go-toml v1.2.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 // indirect
github.com/rogpeppe/go-internal v1.3.0 // indirect
github.com/russross/blackfriday/v2 v2.0.1 // indirect
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/spf13/afero v1.1.2 // indirect
github.com/spf13/cast v1.3.0 // indirect
github.com/spf13/jwalterweatherman v1.0.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 // indirect
github.com/tidwall/match v1.0.3 // indirect
github.com/tidwall/pretty v1.0.2 // indirect
github.com/ugorji/go/codec v1.1.7 // indirect
github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect
github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 // indirect
go.opencensus.io v0.22.3 // indirect
go.uber.org/multierr v1.5.0 // indirect
go.uber.org/zap v1.15.0 // indirect
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 // indirect
google.golang.org/protobuf v1.25.0 // indirect
gopkg.in/ini.v1 v1.51.0 // indirect
gopkg.in/yaml.v2 v2.3.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20200601152816-913338de1bd2 // indirect
)
replace github.com/libp2p/go-libp2p-core => github.com/libp2p/go-libp2p-core v0.5.6
This source diff could not be displayed because it is too large. You can view the blob instead.
package rpcx
import (
"context"
"github.com/link33/sidecar/model/pb"
"github.com/meshplus/bitxhub-kit/crypto"
"github.com/meshplus/bitxhub-kit/types"
)
type SubscriptionType int
const (
SubscribeNewBlock SubscriptionType = iota
)
//go:generate mockgen -destination mock_client/mock_client.go -package mock_client -source client.go
type Client interface {
//Close all connections between BitXHub and the client.
Stop() error
//Reset ecdsa key.
SetPrivateKey(crypto.PrivateKey)
//Send a readonly transaction to BitXHub. If the transaction is writable,
// this transaction will not be executed and error wil be returned.
SendView(tx *pb.BxhTransaction) (*pb.Receipt, error)
//Send a signed transaction to BitXHub. If the signature is illegal,
//the transaction hash will be obtained but the transaction receipt is illegal.
SendTransaction(tx *pb.BxhTransaction, opts *TransactOpts) (string, error)
//Send transaction to BitXHub and get the receipt.
SendTransactionWithReceipt(tx *pb.BxhTransaction, opts *TransactOpts) (*pb.Receipt, error)
//Get the receipt by transaction hash,
//the status of the receipt is a sign of whether the transaction is successful.
GetReceipt(hash string) (*pb.Receipt, error)
//Get transaction from BitXHub by transaction hash.
GetTransaction(hash string) (*pb.GetTransactionResponse, error)
//Get the current blockchain situation of BitXHub.
GetChainMeta() (*pb.ChainMeta, error)
//Get blocks of the specified block height range.
GetBlocks(start uint64, end uint64) (*pb.GetBlocksResponse, error)
//Obtain block information from BitXHub.
//The block header contains the basic information of the block,
//and the block body contains all the transactions packaged.
GetBlock(value string, blockType pb.GetBlockRequest_Type) (*pb.Block, error)
//Get the status of the blockchain from BitXHub, normal or abnormal.
GetChainStatus() (*pb.Response, error)
//Get the validators from BitXHub.
GetValidators() (*pb.Response, error)
//Get the current network situation of BitXHub.
GetNetworkMeta() (*pb.Response, error)
//Get account balance from BitXHub by address.
GetAccountBalance(address string) (*pb.Response, error)
//Get the missing block header from BitXHub.
GetBlockHeader(ctx context.Context, begin, end uint64, ch chan<- *pb.BlockHeader) error
//Get the missing block header from BitXHub.
GetInterchainTxWrappers(ctx context.Context, pid string, begin, end uint64, ch chan<- *pb.InterchainTxWrappers) error
//Subscribe to event notifications from BitXHub.
Subscribe(context.Context, pb.SubscriptionRequest_Type, []byte) (<-chan interface{}, error)
//Deploy the contract, the contract address will be returned when the deployment is successful.
DeployContract(contract []byte, opts *TransactOpts) (contractAddr *types.Address, err error)
//GenerateContractTx generates signed transaction to invoke contract
GenerateContractTx(vmType pb.TransactionData_VMType, address *types.Address, method string, args ...*pb.Arg) (*pb.BxhTransaction, error)
// GenerateIBTPTx generates interchain tx with ibtp specified
GenerateIBTPTx(ibtp *pb.IBTP) (*pb.BxhTransaction, error)
//Call the contract according to the contract type, contract address,
//contract method, and contract method parameters
InvokeContract(vmType pb.TransactionData_VMType, address *types.Address, method string, opts *TransactOpts, args ...*pb.Arg) (*pb.Receipt, error)
//Invoke the BVM contract, BVM is BitXHub's blot contract.
InvokeBVMContract(address *types.Address, method string, opts *TransactOpts, args ...*pb.Arg) (*pb.Receipt, error)
//Invoke the XVM contract, XVM is WebAssembly contract.
InvokeXVMContract(address *types.Address, method string, opts *TransactOpts, args ...*pb.Arg) (*pb.Receipt, error)
// Get BitXHub's signatures specified by id and type.
GetMultiSigns(id string, typ pb.GetMultiSignsRequest_Type) (*pb.SignResponse, error)
// Get BitXHub TPS during block [begin, end]
GetTPS(begin, end uint64) (uint64, error)
// GetPendingNonceByAccount returns the latest nonce of an account in the pending status,
// and it should be the nonce for next transaction
GetPendingNonceByAccount(account string) (uint64, error)
// Delete node according to its pid
DelVPNode(pid string) (*pb.Response, error)
// IPFSPutFromLocal puts local file to ipfs network
IPFSPutFromLocal(localfPath string) (*pb.Response, error)
// IPFSGet gets from ipfs network
IPFSGet(path string) (*pb.Response, error)
// IPFSGetToLocal gets from ipfs and saves to local file path
IPFSGetToLocal(path string, localfPath string) (*pb.Response, error)
//Check whethe there is a master sidecar connect to the BitXHub.
CheckMasterSidecar(address string) (*pb.Response, error)
//Set the master sidecar connect to the BitXHub.
SetMasterSidecar(address string, index string, timeout int64) (*pb.Response, error)
//Update the master sidecar status
HeartBeat(address string, index string) (*pb.Response, error)
}
type TransactOpts struct {
From string
Nonce uint64
}
func New(opts ...Option) (Client, error) {
panic("implement me")
}
type Interchain struct {
ID string `json:"id"`
InterchainCounter map[string]uint64 `json:"interchain_counter,omitempty"`
ReceiptCounter map[string]uint64 `json:"receipt_counter,omitempty"`
SourceReceiptCounter map[string]uint64 `json:"source_receipt_counter,omitempty"`
}
package rpcx
import (
"fmt"
"time"
"github.com/meshplus/bitxhub-kit/crypto"
"github.com/meshplus/bitxhub-kit/fileutil"
"github.com/meshplus/bitxhub-kit/log"
)
const (
blockChanNumber = 1024
defaultTimeout = 1 * time.Second
)
type config struct {
logger Logger
privateKey crypto.PrivateKey
nodesInfo []*NodeInfo
ipfsAddrs []string
timeoutLimit time.Duration // timeout limit config for dialing grpc
}
type NodeInfo struct {
Addr string
EnableTLS bool
CertPath string
CommonName string
}
type Option func(*config)
func WithNodesInfo(nodesInfo ...*NodeInfo) Option {
return func(config *config) {
config.nodesInfo = nodesInfo
}
}
func WithLogger(logger Logger) Option {
return func(config *config) {
config.logger = logger
}
}
func WithPrivateKey(key crypto.PrivateKey) Option {
return func(config *config) {
config.privateKey = key
}
}
func WithIPFSInfo(addrs []string) Option {
return func(config *config) {
config.ipfsAddrs = addrs
}
}
func WithTimeoutLimit(limit time.Duration) Option {
return func(config *config) {
config.timeoutLimit = limit
}
}
func generateConfig(opts ...Option) (*config, error) {
config := &config{}
for _, opt := range opts {
opt(config)
}
if err := checkConfig(config); err != nil {
return nil, err
}
return config, nil
}
func checkConfig(config *config) error {
if config.privateKey == nil {
return fmt.Errorf("private key is empty")
}
if len(config.nodesInfo) == 0 {
return fmt.Errorf("bitxhub addrs cant not be 0")
}
if config.logger == nil {
config.logger = log.NewWithModule("rpcx")
}
if config.timeoutLimit == 0 {
config.timeoutLimit = defaultTimeout
}
// if EnableTLS is set, then tls certs must be provided
for _, nodeInfo := range config.nodesInfo {
if nodeInfo.EnableTLS {
if !fileutil.Exist(nodeInfo.CertPath) {
return fmt.Errorf("ca cert file %s is not found while tls is enabled", nodeInfo.CertPath)
}
}
}
return nil
}
package rpcx
import (
"errors"
"fmt"
)
var (
// error type which can be fixed by retrying
ErrRecoverable = errors.New("recoverable error")
// error type which tx format is invalid to send
ErrReconstruct = errors.New("invalid tx format error")
// set ibtp and normal nonce at the same time
ErrIllegalNonceSet = fmt.Errorf("%w: can't set ibtp nonce and normal nonce at the same time", ErrReconstruct)
// signature for tx is invalid
ErrSignTx = fmt.Errorf("%w: sign for transaction invalid", ErrReconstruct)
// network problem received from grpc
ErrBrokenNetwork = fmt.Errorf("%w: grpc broker error", ErrRecoverable)
)
package rpcx
// The FieldLogger interface generalizes the Entry and logger types
type Logger interface {
Debugf(format string, args ...interface{})
Infof(format string, args ...interface{})
Printf(format string, args ...interface{})
Warningf(format string, args ...interface{})
Errorf(format string, args ...interface{})
Fatalf(format string, args ...interface{})
Panicf(format string, args ...interface{})
Debug(args ...interface{})
Info(args ...interface{})
Print(args ...interface{})
Warning(args ...interface{})
Error(args ...interface{})
Fatal(args ...interface{})
Panic(args ...interface{})
Debugln(args ...interface{})
Infoln(args ...interface{})
Println(args ...interface{})
Warningln(args ...interface{})
Errorln(args ...interface{})
Fatalln(args ...interface{})
Panicln(args ...interface{})
}
This diff is collapsed.
package rpcx
import (
"fmt"
"github.com/link33/sidecar/model/pb"
)
func Int32(i int32) *pb.Arg {
return generateArg(pb.Arg_I32, []byte(fmt.Sprintf("%d", i)))
}
func Int64(i int64) *pb.Arg {
return generateArg(pb.Arg_I64, []byte(fmt.Sprintf("%d", i)))
}
func Uint32(i uint32) *pb.Arg {
return generateArg(pb.Arg_U32, []byte(fmt.Sprintf("%d", i)))
}
func Uint64(i uint64) *pb.Arg {
return generateArg(pb.Arg_U64, []byte(fmt.Sprintf("%d", i)))
}
func String(content string) *pb.Arg {
return generateArg(pb.Arg_String, []byte(content))
}
func Bytes(content []byte) *pb.Arg {
return generateArg(pb.Arg_Bytes, content)
}
func Bool(b bool) *pb.Arg {
return generateArg(pb.Arg_Bool, []byte(fmt.Sprintf("%v", b)))
}
func generateArg(typ pb.Arg_Type, content []byte) *pb.Arg {
return &pb.Arg{
Type: typ,
Value: content,
}
}
// +build ent
package imports
import (
_ "github.com/bitxhub/sidecar-ha"
)
package imports
package app
import (
"context"
"path/filepath"
appchainmgr "github.com/meshplus/bitxhub-core/appchain-mgr"
"github.com/meshplus/bitxhub-kit/crypto"
"github.com/meshplus/bitxhub-kit/storage"
"github.com/meshplus/bitxhub-kit/storage/leveldb"
"github.com/sirupsen/logrus"
_ "github.com/link33/sidecar/imports"
"github.com/link33/sidecar/internal"
"github.com/link33/sidecar/internal/appchain"
"github.com/link33/sidecar/internal/loggers"
"github.com/link33/sidecar/internal/manger"
"github.com/link33/sidecar/internal/peermgr"
"github.com/link33/sidecar/internal/port"
"github.com/link33/sidecar/internal/repo"
"github.com/link33/sidecar/internal/txcrypto"
"github.com/link33/sidecar/pkg/plugins"
)
// Sidecar represents the necessary data for starting the sidecar app
type Sidecar struct {
privateKey crypto.PrivateKey
storage storage.Storage
ctx context.Context
cancel context.CancelFunc
config *repo.Config
logger logrus.FieldLogger
manger internal.Launcher
}
// NewSidecar instantiates sidecar instance.
func NewSidecar(repoRoot string, config *repo.Config) (internal.Launcher, error) {
store, err := leveldb.New(filepath.Join(config.RepoRoot, "store"))
Asset(err)
logger := loggers.Logger(loggers.App)
privateKey, err := repo.LoadPrivateKey(repoRoot)
Asset(err)
addr, err := privateKey.PublicKey().Address()
Asset(err)
nodePrivKey, err := repo.LoadNodePrivateKey(repoRoot)
Asset(err)
var (
//ck checker.Checker
//cryptor txcrypto.Cryptor
//apiServer *api.Server
)
portMap := port.NewPortMap()
pm, err := peermgr.New(config, portMap, nodePrivKey, privateKey, 1, loggers.Logger(loggers.PeerMgr))
Asset(err)
clients := plugins.CreateClients(config.Appchains, nil)
persister := manger.NewPersister(addr.String(), store, loggers.Logger(loggers.Manger))
appchainMgr := appchainmgr.New(persister)
cryptor, err := txcrypto.NewDirectCryptor(appchainMgr, privateKey)
Asset(err)
clientPort := appchain.NewPorts(clients, cryptor, logger)
portMap.Adds(clientPort)
mg, err := manger.NewManager(addr.String(), portMap, pm, appchainMgr, loggers.Logger(loggers.Manger))
Asset(err)
ctx, cancel := context.WithCancel(context.Background())
return &Sidecar{
storage: store,
logger: logger,
ctx: ctx,
cancel: cancel,
config: config,
manger: mg,
}, nil
}
// Start starts three main components of sidecar app
func (s *Sidecar) Start() error {
return s.manger.Start()
}
// Stop stops three main components of sidecar app
func (s *Sidecar) Stop() error {
return s.manger.Stop()
}
func Asset(err error) {
if err != nil {
panic(err)
}
}
package appchain
import (
"context"
"fmt"
"github.com/link33/sidecar/internal/port"
"strconv"
"strings"
"time"
"github.com/Rican7/retry"
"github.com/Rican7/retry/strategy"
"github.com/link33/sidecar/internal/txcrypto"
"github.com/link33/sidecar/model/pb"
"github.com/link33/sidecar/pkg/plugins"
"github.com/sirupsen/logrus"
)
type AppChain interface {
Executor
Monitor
}
type appChain struct {
client plugins.Client
recvCh chan *pb.IBTP
logger logrus.FieldLogger
ctx context.Context
cancel context.CancelFunc
cryptor txcrypto.Cryptor
}
func NewPort(client plugins.Client, cryptor txcrypto.Cryptor, logger logrus.FieldLogger) (port.Port, error) {
ctx, cancel := context.WithCancel(context.Background())
return &appChain{
client: client,
cryptor: cryptor,
logger: logger,
recvCh: make(chan *pb.IBTP, 1024),
ctx: ctx,
cancel: cancel,
}, nil
}
func NewPorts(clients []plugins.Client, cryptor txcrypto.Cryptor, logger logrus.FieldLogger) []port.Port {
var ps []port.Port
for _, c := range clients {
p, err := NewPort(c, cryptor, logger)
if err != nil {
panic(err)
}
ps = append(ps, p)
}
return ps
}
func (a *appChain) QueryInterchainMeta() map[string]uint64 {
execMeta, err := a.client.GetInMeta()
if err != nil {
return map[string]uint64{}
}
return execMeta
}
func (a *appChain) QueryCallbackMeta() map[string]uint64 {
callbackMeta, err := a.client.GetCallbackMeta()
if err != nil {
return map[string]uint64{}
}
return callbackMeta
}
// getReceipt only generates one receipt given source chain id and interchain tx index
func (a *appChain) QueryIBTPReceipt(originalIBTP *pb.IBTP) (*pb.IBTP, error) {
if originalIBTP == nil {
return nil, fmt.Errorf("empty original ibtp")
}
return a.client.GetReceipt(originalIBTP)
}
func (a *appChain) ExecuteIBTP(ibtp *pb.IBTP) (*pb.IBTP, error) {
if ibtp == nil {
a.logger.Error("empty ibtp structure")
return nil, fmt.Errorf("nil ibtp structure")
}
a.logger.WithFields(logrus.Fields{
"index": ibtp.Index,
"type": ibtp.Type,
"from": ibtp.From,
"id": ibtp.ID(),
}).Info("Apply tx")
switch ibtp.Type {
case pb.IBTP_INTERCHAIN:
return a.applyInterchainIBTP(ibtp)
case pb.IBTP_RECEIPT_SUCCESS, pb.IBTP_RECEIPT_FAILURE:
err := a.applyReceiptIBTP(ibtp)
return nil, err
default:
return nil, fmt.Errorf("wrong ibtp type")
}
}
func (a *appChain) applyInterchainIBTP(ibtp *pb.IBTP) (*pb.IBTP, error) {
entry := a.logger.WithFields(logrus.Fields{
"from": ibtp.From,
"type": ibtp.Type,
"index": ibtp.Index,
})
// todo: deal with plugin returned error
// execute interchain tx, and if execution failed, try to rollback
response, err := a.client.SubmitIBTP(ibtp)
if err != nil {
entry.WithField("error", err).Panic("Submit ibtp")
}
if response == nil || response.Result == nil {
entry.WithField("error", err).Panic("empty response")
}
if !response.Status {
pd := &pb.Payload{}
if err := pd.Unmarshal(response.Result.Payload); err != nil {
entry.Panic("Unmarshal payload")
}
entry.WithFields(logrus.Fields{
"result": response.Message,
"payload": pd,
}).Warn("Get wrong response, need rollback on source chain")
}
return response.Result, nil
}
func (a *appChain) applyReceiptIBTP(ibtp *pb.IBTP) error {
pd := &pb.Payload{}
if err := pd.Unmarshal(ibtp.Payload); err != nil {
return fmt.Errorf("unmarshal receipt type ibtp payload: %w", err)
}
ct := &pb.Content{}
contentByte := pd.Content
var err error
if pd.Encrypted {
contentByte, err = a.cryptor.Decrypt(contentByte, ibtp.To)
if err != nil {
return fmt.Errorf("decrypt ibtp payload content: %w", err)
}
}
if err := ct.Unmarshal(contentByte); err != nil {
return fmt.Errorf("unmarshal payload content: %w", err)
}
if err := retry.Retry(func(attempt uint) error {
if err := a.execCallback(ibtp); err != nil {
a.logger.Errorf("Execute callback tx: %s, retry sending tx", err.Error())
return fmt.Errorf("execute callback tx: %w", err)
}
return nil
}, strategy.Wait(1*time.Second)); err != nil {
a.logger.Errorf("Execution of callback function failed: %s", err.Error())
}
return nil
}
func (a *appChain) execCallback(ibtp *pb.IBTP) error {
ibtp.From, ibtp.To = ibtp.To, ibtp.From
// no need to send receipt for callback
resp, err := a.client.SubmitIBTP(ibtp)
if err != nil {
return fmt.Errorf("handle ibtp of callback %w", err)
}
// executor should not change the content of ibtp
ibtp.From, ibtp.To = ibtp.To, ibtp.From
a.logger.WithFields(logrus.Fields{
"index": ibtp.Index,
"type": ibtp.Type,
"status": resp.Status,
"msg": resp.Message,
}).Info("Execute callback")
return nil
}
func (a *appChain) Rollback(ibtp *pb.IBTP, isSrcChain bool) {
if err := retry.Retry(func(attempt uint) error {
err := a.execRollback(ibtp, isSrcChain)
if err != nil {
a.logger.Errorf("Execute callback tx: %s, retry sending tx", err.Error())
return fmt.Errorf("execute callback tx: %w", err)
}
return nil
}, strategy.Wait(1*time.Second)); err != nil {
a.logger.Errorf("Execution of callback function failed: %s", err.Error())
}
}
func (a *appChain) execRollback(ibtp *pb.IBTP, isSrcChain bool) error {
// no need to send receipt for callback
resp, err := a.client.RollbackIBTP(ibtp, isSrcChain)
if err != nil {
return fmt.Errorf("rollback ibtp on source appchain %w", err)
}
a.logger.WithFields(logrus.Fields{
"index": ibtp.Index,
"type": ibtp.Type,
"status": resp.Status,
"msg": resp.Message,
}).Info("Executed rollbcak")
return nil
}
//-------------------------------------------------------------------------------
// Start implements Monitor
func (a *appChain) Start() error {
if err := a.client.Start(); err != nil {
return err
}
ch := a.client.GetIBTP()
go func() {
for {
select {
case e := <-ch:
a.logger.Debugf("Receive ibtp %s from plugin", e.ID())
a.handleIBTP(e)
case <-a.ctx.Done():
return
}
}
}()
a.logger.Info("Monitor started")
return nil
}
// Stop implements Monitor
func (a *appChain) Stop() error {
a.cancel()
a.logger.Info("Monitor stopped")
return nil
}
func (a *appChain) ListenIBTP() <-chan *pb.IBTP {
return a.recvCh
}
// QueryIBTP queries interchain tx recorded in appchain given ibtp id
func (a *appChain) QueryIBTP(id string) (*pb.IBTP, error) {
// TODO(xcc): Encapsulate as a function
args := strings.Split(id, "-")
if len(args) != 3 {
return nil, fmt.Errorf("invalid ibtp id %s", id)
}
idx, err := strconv.ParseUint(args[2], 10, 64)
if err != nil {
return nil, fmt.Errorf("invalid ibtp index")
}
c := make(chan *pb.IBTP, 1)
if err := retry.Retry(func(attempt uint) error {
// TODO(xcc): Need to distinguish error types
e, err := a.client.GetOutMessage(args[1], idx)
if err != nil {
a.logger.WithFields(logrus.Fields{
"error": err,
"ibtp_id": id,
}).Error("Query ibtp")
return err
}
c <- e
return nil
}, strategy.Wait(2*time.Second)); err != nil {
panic(err)
}
ibtp := <-c
if err := a.encryption(ibtp); err != nil {
return nil, err
}
return ibtp, nil
}
// QueryOuterMeta queries outer meta from appchain.
// It will loop until the result is returned or panic.
func (a *appChain) QueryOuterMeta() map[string]uint64 {
var (
meta map[string]uint64
err error
)
if err := retry.Retry(func(attempt uint) error {
meta, err = a.client.GetOutMeta()
if err != nil {
a.logger.WithField("error", err).Error("Get outer meta from appchain")
return err
}
return nil
}, strategy.Wait(2*time.Second)); err != nil {
panic(err)
}
return meta
}
// handleIBTP handle the ibtp package captured by monitor.
func (a *appChain) handleIBTP(ibtp *pb.IBTP) {
if err := a.encryption(ibtp); err != nil {
a.logger.WithFields(logrus.Fields{
"index": ibtp.Index,
"to": ibtp.To,
}).Error("check encryption")
return
}
a.recvCh <- ibtp
}
func (a *appChain) encryption(ibtp *pb.IBTP) error {
pld := &pb.Payload{}
if err := pld.Unmarshal(ibtp.Payload); err != nil {
return err
}
if !pld.Encrypted {
return nil
}
ctb, err := a.cryptor.Encrypt(pld.Content, ibtp.To)
if err != nil {
return err
}
pld.Content = ctb
payload, err := pld.Marshal()
if err != nil {
return err
}
ibtp.Payload = payload
return nil
}
package appchain
import (
"github.com/link33/sidecar/model/pb"
)
//go:generate mockgen -destination mock_executor/mock_executor.go -package mock_executor -source interface.go
type Executor interface {
// ExecuteIBTP handles interchain ibtps from other appchains
// and return the receipt ibtp for ack or callback
ExecuteIBTP(Ibtp *pb.IBTP) (*pb.IBTP, error)
// Rollback rollbacks ibtp on appchain
Rollback(ibtp *pb.IBTP, isSrcChain bool)
// QueryInterchainMeta queries latest index map of ibtps executed on appchain
// For the returned map, key is the source chain ID,
// and value is the latest index of tx executed on appchain
QueryInterchainMeta() map[string]uint64
// QueryCallbackMeta queries latest index map of ibtps callbacks executed on appchain
// For the returned map, key is the destination chain ID,
// and value is the latest index of callback executed on appchain
QueryCallbackMeta() map[string]uint64
// QueryIBTPReceipt query receipt for original interchain ibtp
QueryIBTPReceipt(originalIBTP *pb.IBTP) (*pb.IBTP, error)
}
package appchain
import "github.com/link33/sidecar/model/pb"
//go:generate mockgen -destination mock_monitor/mock_monitor.go -package mock_monitor -source interface.go
type Monitor interface {
// listen on interchain ibtp from appchain
ListenIBTP() <-chan *pb.IBTP
// query historical ibtp by its id
QueryIBTP(id string) (*pb.IBTP, error)
// QueryLatestMeta queries latest index map of ibtps threw on appchain
QueryOuterMeta() map[string]uint64
}
package appchain
import (
"github.com/link33/sidecar/internal/port"
"github.com/link33/sidecar/model/pb"
)
func (a *appChain) ID() string {
return a.client.ID()
}
func (a *appChain) Type() string {
return a.client.Type()
}
func (a *appChain) Tag() string {
return a.client.Type()
}
func (a *appChain) Name() string {
return a.client.Name()
}
func (a *appChain) Send(msg port.Message) (*pb.Message, error) {
panic("implement me")
}
func (a *appChain) AsyncSend(msg port.Message) error {
panic("implement me")
}
func (a *appChain) ListenIBTPX() <-chan *pb.IBTPX {
panic("implement me")
}
package checker
import "github.com/link33/sidecar/model/pb"
type MockChecker struct {
}
func (ck *MockChecker) Check(ibtp *pb.IBTP) error {
return nil
}
package checker
import (
"encoding/json"
"io/ioutil"
"testing"
"time"
"github.com/link33/sidecar/model/pb"
appchainmgr "github.com/meshplus/bitxhub-core/appchain-mgr"
"github.com/stretchr/testify/require"
)
const (
from = "0xe02d8fdacd59020d7f292ab3278d13674f5c404d"
to = "0x0915fdfc96232c95fb9c62d27cc9dc0f13f50161"
from2 = "0x0915fdfc96232c95fb9c62d27cc9dc0f13f50162"
rulePrefix = "validation-rule-"
proofPath = "./testdata/proof_1.0.0_rc"
proofPath2 = "./testdata/proof_1.0.0_rc_complex"
validatorsPath = "./testdata/single_validator"
)
func TestMockChecker_Check(t *testing.T) {
checker := &MockChecker{}
require.Nil(t, checker.Check(nil))
}
func getAppchain(id, chainType string) (*appchainmgr.Appchain, error) {
validators, err := ioutil.ReadFile(validatorsPath)
if err != nil {
return nil, err
}
app := &appchainmgr.Appchain{
ID: id,
Name: "chainA",
Validators: string(validators),
ConsensusType: "rbft",
ChainType: chainType,
Desc: "appchain",
Version: "1.4.3",
PublicKey: "",
}
return app, nil
}
func getIBTP(t *testing.T, index uint64, typ pb.IBTP_Type, fid, tid, proofPath string) *pb.IBTP {
ct := &pb.Content{
SrcContractId: "mychannel&transfer",
DstContractId: "mychannel&transfer",
Func: "get",
Args: [][]byte{[]byte("Alice"), []byte("Alice"), []byte("1")},
Callback: "interchainConfirm",
}
c, err := ct.Marshal()
require.Nil(t, err)
pd := pb.Payload{
Encrypted: false,
Content: c,
}
ibtppd, err := pd.Marshal()
require.Nil(t, err)
proof, err := ioutil.ReadFile(proofPath)
require.Nil(t, err)
return &pb.IBTP{
From: fid,
To: tid,
Payload: ibtppd,
Index: index,
Type: typ,
Timestamp: time.Now().UnixNano(),
Proof: proof,
}
}
// MockAppchainMgr================================================
type MockAppchainMgr struct {
}
func (m MockAppchainMgr) ChangeStatus(id, trigger string, extra []byte) (bool, []byte) {
return true, nil
}
func (m MockAppchainMgr) CountAvailable(extra []byte) (bool, []byte) {
return true, nil
}
func (m MockAppchainMgr) CountAll(extra []byte) (bool, []byte) {
return true, nil
}
func (m MockAppchainMgr) All(extra []byte) (bool, []byte) {
return true, nil
}
func (m MockAppchainMgr) QueryById(id string, extra []byte) (bool, []byte) {
if id == from || id == from2 {
app, err := getAppchain(id, "fabric")
if err != nil {
return false, nil
}
data, err := json.Marshal(app)
if err != nil {
return false, nil
}
return true, data
} else if id == to {
app, err := getAppchain(id, "ethereum")
data, err := json.Marshal(app)
if err != nil {
return false, nil
}
return true, data
} else if id == "10" {
return true, []byte("10")
} else {
return false, nil
}
}
func (m MockAppchainMgr) Register(info []byte) (bool, []byte) {
return true, nil
}
func (m MockAppchainMgr) Update(info []byte) (bool, []byte) {
return true, nil
}
func (m MockAppchainMgr) CountAvailableAppchains() (bool, []byte) {
return true, nil
}
func (m MockAppchainMgr) UpdateAppchain(id, appchainOwner, docAddr, docHash, validators string, consensusType, chainType, name, desc, version, pubkey string) (bool, []byte) {
return true, nil
}
func (m MockAppchainMgr) Audit(proposer string, isApproved int32, desc string) (bool, []byte) {
return true, nil
}
func (m MockAppchainMgr) FetchAuditRecords(id string) (bool, []byte) {
return true, nil
}
func (m MockAppchainMgr) CountApprovedAppchains() (bool, []byte) {
return true, nil
}
func (m MockAppchainMgr) CountAppchains() (bool, []byte) {
return true, nil
}
func (m MockAppchainMgr) Appchains() (bool, []byte) {
return true, nil
}
func (m MockAppchainMgr) DeleteAppchain(id string) (bool, []byte) {
return true, nil
}
func (m MockAppchainMgr) Appchain() (bool, []byte) {
return true, nil
}
func (m MockAppchainMgr) GetPubKeyByChainID(id string) (bool, []byte) {
return true, nil
}
var _ appchainmgr.AppchainMgr = &MockAppchainMgr{}
package checker
import "github.com/link33/sidecar/model/pb"
type Checker interface {
Check(ibtp *pb.IBTP) error
}
-----BEGIN CERTIFICATE-----
MIICKDCCAc+gAwIBAgIRAJq+5G1fMNSIbcDRopzkKtMwCgYIKoZIzj0EAwIwczEL
MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG
cmFuY2lzY28xGTAXBgNVBAoTEG9yZzIuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2Nh
Lm9yZzIuZXhhbXBsZS5jb20wHhcNMjAwNzI4MDg0MjAwWhcNMzAwNzI2MDg0MjAw
WjBqMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMN
U2FuIEZyYW5jaXNjbzENMAsGA1UECxMEcGVlcjEfMB0GA1UEAxMWcGVlcjEub3Jn
Mi5leGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABEoBZRtKxqsk
9bduejlFZr5bAT9RVtETVGus1mAMMvn+PlMw2PDiUGJ7ElpCrxli2xaGbQP5ZhqG
jj+d0Hb3C1ejTTBLMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMCsGA1Ud
IwQkMCKAIGraDjmoNb1W8Oqjv8lW+EBoM2tgZFlNumHqvOvsw072MAoGCCqGSM49
BAMCA0cAMEQCIErGOp47LFK/dSqAUqTZRk8nLSeRzp1D2dA39bsQSV2yAiBiKgay
ND8cmLApyu2Z8UoOaVkoJ+E5W10WlTGhiSjmmw==
-----END CERTIFICATE-----
package exchanger
import (
"github.com/link33/sidecar/api"
"github.com/link33/sidecar/internal/checker"
"github.com/link33/sidecar/internal/peermgr"
"github.com/link33/sidecar/internal/router"
"github.com/link33/sidecar/internal/syncer"
"github.com/meshplus/bitxhub-kit/storage"
"github.com/sirupsen/logrus"
)
type Config struct {
checker checker.Checker
store storage.Storage
peerMgr peermgr.PeerManager
router router.Router
syncer syncer.Syncer
apiServer *api.Server
logger logrus.FieldLogger
}
type Option func(*Config)
func WithChecker(checker checker.Checker) Option {
return func(config *Config) {
config.checker = checker
}
}
func WithPeerMgr(mgr peermgr.PeerManager) Option {
return func(config *Config) {
config.peerMgr = mgr
}
}
func WithRouter(router router.Router) Option {
return func(config *Config) {
config.router = router
}
}
func WithSyncer(syncer syncer.Syncer) Option {
return func(config *Config) {
config.syncer = syncer
}
}
func WithAPIServer(apiServer *api.Server) Option {
return func(config *Config) {
config.apiServer = apiServer
}
}
func WithStorage(store storage.Storage) Option {
return func(config *Config) {
config.store = store
}
}
func WithLogger(logger logrus.FieldLogger) Option {
return func(config *Config) {
config.logger = logger
}
}
func GenerateConfig(opts ...Option) *Config {
config := &Config{}
for _, opt := range opts {
opt(config)
}
return config
}
package exchanger
import (
"encoding/json"
"fmt"
"github.com/Rican7/retry"
"github.com/Rican7/retry/strategy"
"github.com/link33/sidecar/internal/peermgr"
"github.com/link33/sidecar/internal/port"
"github.com/link33/sidecar/model/pb"
"github.com/sirupsen/logrus"
"time"
)
func (ex *Exchanger) feedIBTP(wIbtp *pb.IBTPX) {
var pool *Pool
ibtp := wIbtp.Ibtp
act, loaded := ex.ibtps.Load(ibtp.From)
if !loaded {
pool = NewPool()
ex.ibtps.Store(ibtp.From, pool)
} else {
pool = act.(*Pool)
}
pool.feed(wIbtp)
if !loaded {
go func(pool *Pool) {
defer func() {
if e := recover(); e != nil {
ex.logger.Error(fmt.Errorf("%v", e))
}
}()
inMeta := ex.exec.QueryInterchainMeta()
for wIbtp := range pool.ch {
ibtp := wIbtp.Ibtp
idx := inMeta[ibtp.From]
if ibtp.Index <= idx {
pool.delete(ibtp.Index)
ex.logger.Warnf("ignore ibtp with invalid index: %d", ibtp.Index)
continue
}
if idx+1 == ibtp.Index {
ex.processIBTP(wIbtp)
pool.delete(ibtp.Index)
index := ibtp.Index + 1
wIbtp := pool.get(index)
for wIbtp != nil {
ex.processIBTP(wIbtp)
pool.delete(wIbtp.Ibtp.Index)
index++
wIbtp = pool.get(index)
}
} else {
pool.put(wIbtp)
}
}
}(pool)
}
}
// 直连
func (ex *Exchanger) processIBTP(wIbtp *pb.IBTPX) {
receipt, err := ex.exec.ExecuteIBTP(wIbtp)
if err != nil {
ex.logger.Errorf("Execute ibtp error: %s", err.Error())
return
}
ex.postHandleIBTP(wIbtp.Ibtp.From, receipt)
ex.sendIBTPCounter.Inc()
}
// 直连
func (ex *Exchanger) feedReceipt(receipt *pb.IBTP) {
var pool *Pool
act, loaded := ex.ibtps.Load(receipt.To)
if !loaded {
pool = NewPool()
ex.ibtps.Store(receipt.To, pool)
} else {
pool = act.(*Pool)
}
pool.feed(&pb.IBTPX{Ibtp: receipt, IsValid: true})
if !loaded {
go func(pool *Pool) {
defer func() {
if e := recover(); e != nil {
ex.logger.Error(fmt.Errorf("%v", e))
}
}()
callbackMeta := ex.exec.QueryCallbackMeta()
for wIbtp := range pool.ch {
ibtp := wIbtp.Ibtp
if ibtp.Index <= callbackMeta[ibtp.To] {
pool.delete(ibtp.Index)
ex.logger.Warn("ignore ibtp with invalid index")
continue
}
if callbackMeta[ibtp.To]+1 == ibtp.Index {
ex.processIBTP(wIbtp)
pool.delete(ibtp.Index)
index := ibtp.Index + 1
wIbtp := pool.get(index)
for wIbtp != nil {
ibtp := wIbtp.Ibtp
receipt, _ := ex.exec.ExecuteIBTP(wIbtp)
ex.postHandleIBTP(ibtp.From, receipt)
pool.delete(ibtp.Index)
index++
wIbtp = pool.get(index)
}
} else {
pool.put(wIbtp)
}
}
}(pool)
}
}
func (ex *Exchanger) postHandleIBTP(from string, receipt *pb.IBTP) {
if receipt == nil {
retMsg := peermgr.Message(pb.Message_IBTP_RECEIPT_SEND, true, nil)
err := ex.peerMgr.AsyncSend(from, retMsg)
if err != nil {
ex.logger.Errorf("Send back empty ibtp receipt: %s", err.Error())
}
return
}
data, _ := receipt.Marshal()
retMsg := peermgr.Message(pb.Message_IBTP_RECEIPT_SEND, true, data)
if err := ex.peerMgr.AsyncSend(from, retMsg); err != nil {
ex.logger.Errorf("Send back ibtp receipt: %s", err.Error())
}
}
//直链模式
func (ex *Exchanger) handleSendIBTPMessage(p port.Port, msg *pb.Message) {
ex.ch <- struct{}{}
go func(msg *pb.Message) {
wIbtp := &pb.IBTPX{}
if err := json.Unmarshal(msg.Payload.Data, wIbtp); err != nil {
ex.logger.Errorf("Unmarshal ibtp: %s", err.Error())
return
}
defer ex.timeCost()()
err := ex.checker.Check(wIbtp.Ibtp)
if err != nil {
ex.logger.Error("check ibtp: %w", err)
return
}
ex.feedIBTP(wIbtp)
<-ex.ch
}(msg)
}
//直链模式
func (ex *Exchanger) handleSendIBTPReceiptMessage(p port.Port, msg *pb.Message) {
if msg.Payload.Data == nil {
return
}
receipt := &pb.IBTP{}
if err := receipt.Unmarshal(msg.Payload.Data); err != nil {
ex.logger.Error("unmarshal ibtp: %w", err)
return
}
// ignore msg for receipt type
if receipt.Type == pb.IBTP_RECEIPT_SUCCESS || receipt.Type == pb.IBTP_RECEIPT_FAILURE {
//ex.logger.Warn("ignore receipt ibtp")
return
}
err := ex.checker.Check(receipt)
if err != nil {
ex.logger.Error("check ibtp: %w", err)
return
}
ex.feedReceipt(receipt)
ex.logger.Info("Receive ibtp receipt from other sidecar")
}
// 直连
func (ex *Exchanger) handleGetIBTPMessage(p port.Port, msg *pb.Message) {
ibtpID := string(msg.Payload.Data)
ibtp, err := ex.mnt.QueryIBTP(ibtpID)
if err != nil {
ex.logger.Error("Get wrong ibtp id")
return
}
data, err := ibtp.Marshal()
if err != nil {
return
}
retMsg := peermgr.Message(pb.Message_ACK, true, data)
err = ex.peerMgr.AsyncSendWithPort(p, retMsg)
if err != nil {
ex.logger.Error(err)
}
}
// 直连
func (ex *Exchanger) handleNewConnection(dstSidecarID string) {
appchainMethod := []byte(ex.appchainDID)
msg := peermgr.Message(pb.Message_INTERCHAIN_META_GET, true, appchainMethod)
indices := &struct {
InterchainIndex uint64 `json:"interchain_index"`
ReceiptIndex uint64 `json:"receipt_index"`
}{}
loop := func() error {
interchainMeta, err := ex.peerMgr.Send(dstSidecarID, msg)
if err != nil {
return err
}
if !interchainMeta.Payload.Ok {
return fmt.Errorf("interchain meta message payload is false")
}
if err = json.Unmarshal(interchainMeta.Payload.Data, indices); err != nil {
return err
}
return nil
}
if err := retry.Retry(func(attempt uint) error {
return loop()
}, strategy.Wait(1*time.Second)); err != nil {
ex.logger.Panic(err)
}
}
//直链模式
func (ex *Exchanger) handleGetInterchainMessage(p port.Port, msg *pb.Message) {
mntMeta := ex.mnt.QueryOuterMeta()
execMeta := ex.exec.QueryInterchainMeta()
indices := &struct {
InterchainIndex uint64 `json:"interchain_index"`
ReceiptIndex uint64 `json:"receipt_index"`
}{}
execLoad, ok := execMeta[string(msg.Payload.Data)]
if ok {
indices.InterchainIndex = execLoad
}
mntLoad, ok := mntMeta[string(msg.Payload.Data)]
if ok {
indices.InterchainIndex = mntLoad
}
data, err := json.Marshal(indices)
if err != nil {
panic(err)
}
retMsg := peermgr.Message(pb.Message_ACK, true, data)
if err := ex.peerMgr.AsyncSendWithPort(p, retMsg); err != nil {
ex.logger.Error(err)
return
}
}
//直链模式
func (ex *Exchanger) analysisDirectTPS() {
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
current := time.Now()
counter := ex.sendIBTPCounter.Load()
for {
select {
case <-ticker.C:
tps := ex.sendIBTPCounter.Load() - counter
counter = ex.sendIBTPCounter.Load()
totalTimer := ex.sendIBTPTimer.Load()
if tps != 0 {
ex.logger.WithFields(logrus.Fields{
"tps": tps,
"tps_sum": counter,
"tps_time": totalTimer.Milliseconds() / int64(counter),
"tps_avg": float64(counter) / time.Since(current).Seconds(),
}).Info("analysis")
}
case <-ex.ctx.Done():
return
}
}
}
package exchanger
import (
"context"
"errors"
"fmt"
"sync"
"time"
"github.com/Rican7/retry"
"github.com/Rican7/retry/backoff"
"github.com/Rican7/retry/strategy"
"github.com/link33/sidecar/api"
"github.com/link33/sidecar/internal/checker"
"github.com/link33/sidecar/internal/peermgr"
"github.com/link33/sidecar/internal/repo"
"github.com/link33/sidecar/internal/router"
"github.com/link33/sidecar/internal/syncer"
"github.com/link33/sidecar/model/pb"
"github.com/meshplus/bitxhub-kit/storage"
"github.com/sirupsen/logrus"
"go.uber.org/atomic"
)
type Exchanger struct {
mode string
appchainDID string
store storage.Storage
syncer syncer.Syncer // WrapperSyncer represents the necessary data for sync tx wrappers from bitxhub
router router.Router // 占时不使用
apiServer *api.Server //直连模式使用
peerMgr peermgr.PeerManager
checker checker.Checker
sendIBTPCounter atomic.Uint64
sendIBTPTimer atomic.Duration
ch chan struct{} //control the concurrent count
ibtps sync.Map
receipts sync.Map
logger logrus.FieldLogger
ctx context.Context
cancel context.CancelFunc
}
func New(typ, appchainDID string, meta *pb.Interchain, opts ...Option) (*Exchanger, error) {
config := GenerateConfig(opts...)
ctx, cancel := context.WithCancel(context.Background())
return &Exchanger{
checker: config.checker,
apiServer: config.apiServer,
peerMgr: config.peerMgr,
syncer: config.syncer,
store: config.store,
router: config.router,
logger: config.logger,
ch: make(chan struct{}, 100),
mode: typ,
appchainDID: appchainDID,
ctx: ctx,
cancel: cancel,
}, nil
}
func (ex *Exchanger) Start() error {
var err error
switch ex.mode {
case repo.DirectMode:
err = ex.startWithDirectMode()
case repo.RelayMode:
err = ex.startWithRelayMode()
}
if err != nil {
return err
}
// 这个同样也是,而不是在这里启动。
if ex.mnt != nil {
go ex.listenAndSendIBTPFromMnt()
}
//核心,就是转发
if ex.syncer != nil { //而是根据是否有配置hub判断,而不是这个判断.
go ex.listenAndSendIBTPFromSyncer()
}
ex.logger.Info("Exchanger started")
return nil
}
//直链模式
func (ex *Exchanger) startWithDirectMode() error {
if err := ex.apiServer.Start(); err != nil {
return fmt.Errorf("peerMgr start: %w", err)
}
if err := ex.peerMgr.RegisterConnectHandler(ex.handleNewConnection); err != nil {
return fmt.Errorf("register on connection handler: %w", err)
}
if err := ex.peerMgr.RegisterMsgHandler(pb.Message_INTERCHAIN_META_GET, ex.handleGetInterchainMessage); err != nil {
return fmt.Errorf("register query interchain msg handler: %w", err)
}
// API调用
if err := ex.peerMgr.RegisterMsgHandler(pb.Message_IBTP_SEND, ex.handleSendIBTPMessage); err != nil {
return fmt.Errorf("register ibtp handler: %w", err)
}
if err := ex.peerMgr.RegisterMsgHandler(pb.Message_IBTP_RECEIPT_SEND, ex.handleSendIBTPReceiptMessage); err != nil {
return fmt.Errorf("register ibtp handler: %w", err)
}
if err := ex.peerMgr.RegisterMsgHandler(pb.Message_IBTP_GET, ex.handleGetIBTPMessage); err != nil {
return fmt.Errorf("register ibtp receipt handler: %w", err)
}
if err := ex.peerMgr.Start(); err != nil {
return fmt.Errorf("peerMgr start: %w", err)
}
go ex.analysisDirectTPS()
return nil
}
func (ex *Exchanger) startWithRelayMode() error {
if err := ex.syncer.RegisterRollbackHandler(ex.handleRollback); err != nil {
return fmt.Errorf("register router handler: %w", err)
}
return nil
}
// 中继、直链
func (ex *Exchanger) listenAndSendIBTPFromMnt() {
ch := ex.mnt.ListenIBTP()
for {
select {
case <-ex.ctx.Done():
return
case ibtp, ok := <-ch:
ex.logger.Info("Receive interchain ibtp from monitor")
if !ok {
ex.logger.Warn("Unexpected closed channel while listening on interchain ibtp")
return
}
if err := retry.Retry(func(attempt uint) error {
if err := ex.sendIBTP(ibtp); err != nil {
ex.logger.Errorf("Send ibtp: %s", err.Error())
// if err occurs, try to get new ibtp and resend
ibtpID := ibtp.ID()
if err := retry.Retry(func(attempt uint) error {
ibtp, err = ex.mnt.QueryIBTP(ibtpID)
if err != nil {
ex.logger.Errorf("Query ibtp %s from appchain: %s", ibtpID, err.Error())
return err
}
return nil
}, strategy.Backoff(backoff.Fibonacci(500*time.Millisecond))); err != nil {
ex.logger.Panic(err)
}
return fmt.Errorf("retry sending ibtp")
}
return nil
}, strategy.Backoff(backoff.Fibonacci(500*time.Millisecond))); err != nil {
ex.logger.Panic(err)
}
}
}
}
// 中继模式
func (ex *Exchanger) listenAndSendIBTPFromSyncer() {
ch := ex.syncer.ListenIBTP()
for {
select {
case <-ex.ctx.Done():
return
case wIbtp, ok := <-ch:
if !ok {
ex.logger.Warn("Unexpected closed channel while listening on interchain ibtp")
return
}
entry := ex.logger.WithFields(logrus.Fields{"type": wIbtp.Ibtp.Type, "id": wIbtp.Ibtp.ID()})
entry.Debugf("Exchanger receives ibtp from syncer")
switch wIbtp.Ibtp.Type {
case pb.IBTP_INTERCHAIN:
ex.applyInterchain(wIbtp, entry) // 发送到HUB上
case pb.IBTP_RECEIPT_SUCCESS, pb.IBTP_RECEIPT_FAILURE:
ex.applyReceipt(wIbtp, entry)
default:
entry.Errorf("wrong type of ibtp")
}
}
}
}
func (ex *Exchanger) Stop() error {
ex.cancel()
switch ex.mode {
case repo.DirectMode:
if err := ex.apiServer.Stop(); err != nil {
return fmt.Errorf("gin service stop: %w", err)
}
if err := ex.peerMgr.Stop(); err != nil {
return fmt.Errorf("peerMgr stop: %w", err)
}
case repo.RelayMode:
if err := ex.syncer.Stop(); err != nil {
return fmt.Errorf("syncer stop: %w", err)
}
}
ex.logger.Info("Exchanger stopped")
return nil
}
// 共同
func (ex *Exchanger) sendIBTP(ibtp *pb.IBTP) error {
entry := ex.logger.WithFields(logrus.Fields{"index": ibtp.Index, "type": ibtp.Type, "to": ibtp.To, "id": ibtp.ID()})
//TODO!!!! 根据交易决定
//mode := ibtp.mode
mode := repo.RelayMode
switch mode {
case repo.RelayMode:
err := ex.syncer.SendIBTP(ibtp)
if err != nil {
entry.Errorf("Send ibtp to bitxhub: %s", err.Error())
return fmt.Errorf("send ibtp to bitxhub: %s", err.Error())
}
case repo.DirectMode:
// send ibtp to another sidecar
if err := retry.Retry(func(attempt uint) error {
data, err := ibtp.Marshal()
if err != nil {
panic(fmt.Sprintf("marshal ibtp: %s", err.Error()))
}
msg := pb.Message(pb.Message_IBTP_SEND, true, data)
var dst string
if ibtp.Type == pb.IBTP_INTERCHAIN {
dst = ibtp.To
} else {
dst = ibtp.From
}
if err := ex.peerMgr.AsyncSend(dst, msg); err != nil {
ex.logger.Errorf("Send ibtp to sidecar %s: %s", ibtp.ID(), err.Error())
return err
}
return nil
}, strategy.Wait(1*time.Second)); err != nil {
ex.logger.Panic(err)
}
}
entry.Info("Send ibtp success from monitor")
return nil
}
//主动发起,说明是from自己,
func (ex *Exchanger) queryIBTP(mode string, id, target string) (*pb.IBTP, bool, error) {
verifiedTx := &pb.VerifiedTx{}
v := ex.store.Get(pb.IBTPKey(id))
if v != nil {
if err := verifiedTx.Unmarshal(v); err != nil {
return nil, false, err
}
return verifiedTx.Tx.GetIBTP(), verifiedTx.Valid, nil
}
// query ibtp from counterpart chain
var (
ibtp *pb.IBTP
isValid bool
err error
)
// 交易中,如果是用户,根据用户指定,如果没有按默认配置,依次排序。
switch mode {
case repo.RelayMode: // 中继模式是查询hub上的ibtp交易根据交易ID。
ibtp, isValid, err = ex.syncer.QueryIBTP(id)
if err != nil {
if errors.Is(err, syncer.ErrIBTPNotFound) {
ex.logger.Panicf("query ibtp by id %s from bitxhub: %s", id, err.Error())
}
return nil, false, fmt.Errorf("query ibtp from bitxhub: %s", err.Error())
}
case repo.DirectMode:
// query ibtp from another sidecar
msg := pb.Message(pb.Message_IBTP_GET, true, []byte(id))
result, err := ex.peerMgr.Send(target, msg)
if err != nil {
return nil, false, err
}
ibtp = &pb.IBTP{}
if err := ibtp.Unmarshal(result.Payload.Data); err != nil {
return nil, false, err
}
default:
return nil, false, fmt.Errorf("unsupported sidecar mode")
}
return ibtp, isValid, nil
}
func (ex *Exchanger) queryIBTPForRelay(id, target string) (*pb.IBTP, bool, error) {
verifiedTx := &pb.VerifiedTx{}
v := ex.store.Get(pb.IBTPKey(id))
if v != nil {
if err := verifiedTx.Unmarshal(v); err != nil {
return nil, false, err
}
return verifiedTx.Tx.GetIBTP(), verifiedTx.Valid, nil
}
ibtp, isValid, err := ex.syncer.QueryIBTP(id)
if err != nil {
if errors.Is(err, syncer.ErrIBTPNotFound) {
ex.logger.Panicf("query ibtp by id %s from bitxhub: %s", id, err.Error())
}
return nil, false, fmt.Errorf("query ibtp from bitxhub: %s", err.Error())
}
return ibtp, isValid, nil
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment