├── .github
└── workflows
│ ├── build-and-test.yml
│ └── golangci-lint.yml
├── .gitignore
├── LICENSE
├── Makefile
├── README.md
├── build
└── ci.go
├── cmd
├── swaporacle
│ └── main.go
├── swapserver
│ └── main.go
└── utils
│ ├── flags.go
│ ├── licensecmd.go
│ ├── utils.go
│ └── versioncmd.go
├── common
├── big.go
├── bytes.go
├── ethaddress.go
├── hash.go
├── hexutil
│ ├── hexutil.go
│ ├── hexutil_test.go
│ ├── json.go
│ ├── json_example_test.go
│ └── json_test.go
├── math
│ ├── big.go
│ ├── big_test.go
│ ├── integer.go
│ └── integer_test.go
├── path.go
└── utils.go
├── dcrm
├── accept.go
├── api.go
├── init.go
├── sign.go
└── types.go
├── go.mod
├── go.sum
├── gofmt.sh
├── internal
├── build
│ ├── env.go
│ └── util.go
└── swapapi
│ ├── api.go
│ ├── converts.go
│ └── types.go
├── log
├── logger.go
└── logger_test.go
├── mongodb
├── api.go
├── dbinit.go
├── errors.go
├── status.go
└── tables.go
├── params
├── config.go
├── config.toml
└── version.go
├── rpc
├── client
│ ├── client.go
│ ├── get.go
│ └── post.go
├── restapi
│ └── api.go
├── rpcapi
│ ├── api.go
│ └── buildtx.go
└── server
│ └── server.go
├── tokens
├── bridge
│ └── bridge.go
├── btc
│ ├── address.go
│ ├── aggregate.go
│ ├── bridge.go
│ ├── buildaggregatetx.go
│ ├── buildtx.go
│ ├── callapi.go
│ ├── electrs
│ │ ├── callapi.go
│ │ └── types.go
│ ├── p2shaddress.go
│ ├── printtx.go
│ ├── processtx.go
│ ├── scanchaintx.go
│ ├── scanpooltx.go
│ ├── scanswaphistory.go
│ ├── sendtx.go
│ ├── signtx.go
│ ├── verifyp2shtx.go
│ └── verifytx.go
├── eth
│ ├── abipack.go
│ ├── address.go
│ ├── bridge.go
│ ├── buildswapouttx.go
│ ├── buildtx.go
│ ├── callapi.go
│ ├── callcontract.go
│ ├── processtx.go
│ ├── scanchaintx.go
│ ├── scanpooltx.go
│ ├── scanswaphistory.go
│ ├── sendtx.go
│ ├── signtx.go
│ ├── verifycontractaddress.go
│ ├── verifyerc20tx.go
│ ├── verifyswapouttx.go
│ └── verifytx.go
├── fsn
│ ├── bridge.go
│ └── callapi.go
├── interfaces.go
├── tools
│ ├── cachedscanblocks.go
│ ├── cachedscantxs.go
│ └── swaptools.go
└── types.go
├── tools
├── crypto
│ ├── crypto.go
│ ├── crypto_test.go
│ ├── signature.go
│ └── signature_test.go
├── keystore
│ ├── aes.go
│ ├── key.go
│ └── passphrase.go
├── loadkeystore.go
└── rlp
│ ├── decode.go
│ ├── decode_tail_test.go
│ ├── decode_test.go
│ ├── doc.go
│ ├── encode.go
│ ├── encode_test.go
│ ├── encoder_example_test.go
│ ├── raw.go
│ ├── raw_test.go
│ └── typecache.go
├── types
├── gen_tx_json.go
├── rpctypes.go
├── transaction.go
└── transaction_signing.go
└── worker
├── accept.go
├── aggregate.go
├── common.go
├── recall.go
├── scan.go
├── stable.go
├── swap.go
├── updatelatest.go
├── utils.go
├── verify.go
└── worker.go
/.github/workflows/build-and-test.yml:
--------------------------------------------------------------------------------
1 | name: test
2 | on:
3 | push:
4 | tags:
5 | - v*
6 | branches:
7 | - master
8 | pull_request:
9 | jobs:
10 | test:
11 | name: run
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@master
15 | - name: run test
16 | uses: cedrickring/golang-action@1.5.2
17 | with:
18 | args: make test
19 |
--------------------------------------------------------------------------------
/.github/workflows/golangci-lint.yml:
--------------------------------------------------------------------------------
1 | name: golangci-lint
2 | on:
3 | push:
4 | tags:
5 | - v*
6 | branches:
7 | - master
8 | pull_request:
9 | jobs:
10 | golangci:
11 | name: lint
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v2
15 | - name: golangci-lint
16 | uses: golangci/golangci-lint-action@v1
17 | with:
18 | # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
19 | version: v1.26
20 |
21 | # Optional: working directory, useful for monorepos
22 | # working-directory: somedir
23 |
24 | # Optional: golangci-lint command line arguments.
25 | # args: --issues-exit-code=0
26 |
27 | # Optional: show only new issues if it's a pull request. The default value is `false`.
28 | # only-new-issues: true
29 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.exe~
4 | *.dll
5 | *.so
6 | *.dylib
7 |
8 | # Test binary, built with `go test -c`
9 | *.test
10 |
11 | # Output of the go coverage tool, specifically when used with LiteIDE
12 | *.out
13 |
14 | # Dependency directories (remove the comment below to include it)
15 | # vendor/
16 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: all test testv clean fmt
2 | .PHONY: swapserver swaporacle
3 |
4 | GOBIN = ./build/bin
5 | GOCMD = env GO111MODULE=on GOPROXY=https://goproxy.io go
6 |
7 | swapserver:
8 | $(GOCMD) run build/ci.go install ./cmd/swapserver
9 | @echo "Done building."
10 | @echo "Run \"$(GOBIN)/swapserver\" to launch swapserver."
11 |
12 | swaporacle:
13 | $(GOCMD) run build/ci.go install ./cmd/swaporacle
14 | @echo "Done building."
15 | @echo "Run \"$(GOBIN)/swaporacle\" to launch swaporacle."
16 |
17 | all:
18 | $(GOCMD) build -v ./...
19 | $(GOCMD) run build/ci.go install ./cmd/...
20 | @echo "Done building."
21 | @echo "Find binaries in \"$(GOBIN)\" directory."
22 | @echo ""
23 | @echo "Copy example config.toml to \"$(GOBIN)\" directory (no overwrite if exist)."
24 | @cp -n params/config.toml $(GOBIN)
25 |
26 | test: all
27 | $(GOCMD) test ./...
28 |
29 | testv: all
30 | $(GOCMD) test -v ./...
31 |
32 | clean:
33 | $(GOCMD) clean -cache
34 | rm -fr $(GOBIN)/*
35 |
36 | fmt:
37 | ./gofmt.sh
38 |
--------------------------------------------------------------------------------
/build/ci.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "fmt"
6 | "log"
7 | "os"
8 | "os/exec"
9 | "path/filepath"
10 | "runtime"
11 | "strings"
12 |
13 | "github.com/fsn-dev/crossChain-Bridge/internal/build"
14 | )
15 |
16 | var gobin, _ = filepath.Abs(filepath.Join("build", "bin"))
17 |
18 | func main() {
19 | log.SetFlags(log.Lshortfile)
20 |
21 | if _, err := os.Stat(filepath.Join("build", "ci.go")); os.IsNotExist(err) {
22 | log.Fatal("this script must be run from the root of the repository")
23 | }
24 | if len(os.Args) < 2 {
25 | log.Fatal("need subcommand as first argument")
26 | }
27 | switch os.Args[1] {
28 | case "install":
29 | doInstall(os.Args[2:])
30 | default:
31 | log.Fatal("unknown command ", os.Args[1])
32 | }
33 | }
34 |
35 | // Compiling
36 |
37 | func doInstall(cmdline []string) {
38 | _ = flag.CommandLine.Parse(cmdline)
39 | env := build.Env()
40 |
41 | // Check Go version. People regularly open issues about compilation
42 | // failure with outdated Go. This should save them the trouble.
43 | if !strings.Contains(runtime.Version(), "devel") {
44 | // Figure out the minor version number since we can't textually compare (1.10 < 1.9)
45 | var minor int
46 | fmt.Sscanf(strings.TrimPrefix(runtime.Version(), "go1."), "%d", &minor)
47 |
48 | if minor < 12 {
49 | log.Println("You have Go version", runtime.Version())
50 | log.Println("requires at least Go version 1.12 and cannot")
51 | log.Println("be compiled with an earlier version. Please upgrade your Go installation.")
52 | os.Exit(1)
53 | }
54 | }
55 | // Compile packages given as arguments, or everything if there are no arguments.
56 | packages := []string{"./..."}
57 | if flag.NArg() > 0 {
58 | packages = flag.Args()
59 | }
60 |
61 | goinstall := goTool("install", buildFlags(env)...)
62 | if runtime.GOARCH == "arm64" {
63 | goinstall.Args = append(goinstall.Args, "-p", "1")
64 | }
65 | goinstall.Args = append(goinstall.Args, "-v")
66 | goinstall.Args = append(goinstall.Args, packages...)
67 | build.MustRun(goinstall)
68 | }
69 |
70 | func buildFlags(env *build.Environment) (flags []string) {
71 | var ld []string
72 | if env.Commit != "" {
73 | ld = append(ld,
74 | "-X", "main.gitCommit="+env.Commit,
75 | "-X", "main.gitDate="+env.Date,
76 | )
77 | }
78 | if runtime.GOOS == "darwin" {
79 | ld = append(ld, "-s")
80 | }
81 |
82 | if len(ld) > 0 {
83 | flags = append(flags, "-ldflags", strings.Join(ld, " "))
84 | }
85 | return flags
86 | }
87 |
88 | func goTool(subcmd string, args ...string) *exec.Cmd {
89 | return goToolArch(runtime.GOARCH, os.Getenv("CC"), subcmd, args...)
90 | }
91 |
92 | func goToolArch(arch, cc, subcmd string, args ...string) *exec.Cmd {
93 | cmd := build.GoTool(subcmd, args...)
94 | if arch == "" || arch == runtime.GOARCH {
95 | cmd.Env = append(cmd.Env, "GOBIN="+gobin)
96 | } else {
97 | cmd.Env = append(cmd.Env, "CGO_ENABLED=1", "GOARCH="+arch)
98 | }
99 | if cc != "" {
100 | cmd.Env = append(cmd.Env, "CC="+cc)
101 | }
102 | for _, e := range os.Environ() {
103 | if strings.HasPrefix(e, "GOBIN=") {
104 | continue
105 | }
106 | cmd.Env = append(cmd.Env, e)
107 | }
108 | return cmd
109 | }
110 |
--------------------------------------------------------------------------------
/cmd/swaporacle/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "sort"
7 |
8 | "github.com/fsn-dev/crossChain-Bridge/cmd/utils"
9 | "github.com/fsn-dev/crossChain-Bridge/log"
10 | "github.com/fsn-dev/crossChain-Bridge/params"
11 | "github.com/fsn-dev/crossChain-Bridge/worker"
12 | "github.com/urfave/cli/v2"
13 | )
14 |
15 | var (
16 | clientIdentifier = "swaporacle"
17 | // Git SHA1 commit hash of the release (set via linker flags)
18 | gitCommit = ""
19 | // The app that holds all commands and flags.
20 | app = utils.NewApp(clientIdentifier, gitCommit, "the swaporacle command line interface")
21 | )
22 |
23 | func initApp() {
24 | // Initialize the CLI app and start action
25 | app.Action = swaporacle
26 | app.HideVersion = true // we have a command to print the version
27 | app.Copyright = "Copyright 2017-2020 The crossChain-Bridge Authors"
28 | app.Commands = []*cli.Command{
29 | utils.LicenseCommand,
30 | utils.VersionCommand,
31 | }
32 | app.Flags = []cli.Flag{
33 | utils.DataDirFlag,
34 | utils.ConfigFileFlag,
35 | utils.LogFileFlag,
36 | utils.LogRotationFlag,
37 | utils.LogMaxAgeFlag,
38 | utils.VerbosityFlag,
39 | utils.JSONFormatFlag,
40 | utils.ColorFormatFlag,
41 | }
42 | sort.Sort(cli.CommandsByName(app.Commands))
43 | }
44 |
45 | func main() {
46 | initApp()
47 | if err := app.Run(os.Args); err != nil {
48 | log.Println(err)
49 | os.Exit(1)
50 | }
51 | }
52 |
53 | func swaporacle(ctx *cli.Context) error {
54 | utils.SetLogger(ctx)
55 | if ctx.NArg() > 0 {
56 | return fmt.Errorf("invalid command: %q", ctx.Args().Get(0))
57 | }
58 | exitCh := make(chan struct{})
59 | configFile := utils.GetConfigFilePath(ctx)
60 | params.LoadConfig(configFile, false)
61 |
62 | params.SetDataDir(ctx.String(utils.DataDirFlag.Name))
63 |
64 | worker.StartWork(false)
65 |
66 | <-exitCh
67 | return nil
68 | }
69 |
--------------------------------------------------------------------------------
/cmd/swapserver/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "sort"
7 | "time"
8 |
9 | "github.com/fsn-dev/crossChain-Bridge/cmd/utils"
10 | "github.com/fsn-dev/crossChain-Bridge/log"
11 | "github.com/fsn-dev/crossChain-Bridge/mongodb"
12 | "github.com/fsn-dev/crossChain-Bridge/params"
13 | rpcserver "github.com/fsn-dev/crossChain-Bridge/rpc/server"
14 | "github.com/fsn-dev/crossChain-Bridge/worker"
15 | "github.com/urfave/cli/v2"
16 | )
17 |
18 | var (
19 | clientIdentifier = "swapserver"
20 | // Git SHA1 commit hash of the release (set via linker flags)
21 | gitCommit = ""
22 | // The app that holds all commands and flags.
23 | app = utils.NewApp(clientIdentifier, gitCommit, "the swapserver command line interface")
24 | )
25 |
26 | func initApp() {
27 | // Initialize the CLI app and start action
28 | app.Action = swapserver
29 | app.HideVersion = true // we have a command to print the version
30 | app.Copyright = "Copyright 2017-2020 The crossChain-Bridge Authors"
31 | app.Commands = []*cli.Command{
32 | utils.LicenseCommand,
33 | utils.VersionCommand,
34 | }
35 | app.Flags = []cli.Flag{
36 | utils.DataDirFlag,
37 | utils.ConfigFileFlag,
38 | utils.LogFileFlag,
39 | utils.LogRotationFlag,
40 | utils.LogMaxAgeFlag,
41 | utils.VerbosityFlag,
42 | utils.JSONFormatFlag,
43 | utils.ColorFormatFlag,
44 | }
45 | sort.Sort(cli.CommandsByName(app.Commands))
46 | }
47 |
48 | func main() {
49 | initApp()
50 | if err := app.Run(os.Args); err != nil {
51 | log.Println(err)
52 | os.Exit(1)
53 | }
54 | }
55 |
56 | func swapserver(ctx *cli.Context) error {
57 | utils.SetLogger(ctx)
58 | if ctx.NArg() > 0 {
59 | return fmt.Errorf("invalid command: %q", ctx.Args().Get(0))
60 | }
61 | exitCh := make(chan struct{})
62 | configFile := utils.GetConfigFilePath(ctx)
63 | config := params.LoadConfig(configFile, true)
64 |
65 | params.SetDataDir(ctx.String(utils.DataDirFlag.Name))
66 |
67 | dbConfig := config.MongoDB
68 | mongoURL := dbConfig.GetURL()
69 | dbName := dbConfig.DBName
70 | mongodb.MongoServerInit(mongoURL, dbName)
71 |
72 | worker.StartWork(true)
73 | time.Sleep(100 * time.Millisecond)
74 | rpcserver.StartAPIServer()
75 |
76 | <-exitCh
77 | return nil
78 | }
79 |
--------------------------------------------------------------------------------
/cmd/utils/flags.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "github.com/fsn-dev/crossChain-Bridge/log"
5 | "github.com/urfave/cli/v2"
6 | )
7 |
8 | var (
9 | // DataDirFlag --datadir
10 | DataDirFlag = &cli.StringFlag{
11 | Name: "datadir",
12 | Usage: "Data directory (default in the execute directory)",
13 | Value: "",
14 | }
15 | // ConfigFileFlag -c|--config
16 | ConfigFileFlag = &cli.StringFlag{
17 | Name: "config",
18 | Aliases: []string{"c"},
19 | Usage: "Specify config file",
20 | }
21 | // LogFileFlag --log
22 | LogFileFlag = &cli.StringFlag{
23 | Name: "log",
24 | Usage: "Specify log file, support rotate",
25 | }
26 | // LogRotationFlag --rotate
27 | LogRotationFlag = &cli.Uint64Flag{
28 | Name: "rotate",
29 | Usage: "log rotation time (unit hour)",
30 | Value: 24,
31 | }
32 | // LogMaxAgeFlag --maxage
33 | LogMaxAgeFlag = &cli.Uint64Flag{
34 | Name: "maxage",
35 | Usage: "log max age (unit hour)",
36 | Value: 720,
37 | }
38 | // VerbosityFlag -v|--verbosity
39 | VerbosityFlag = &cli.Uint64Flag{
40 | Name: "verbosity",
41 | Aliases: []string{"v"},
42 | Usage: "log verbosity (0:panic, 1:fatal, 2:error, 3:warn, 4:info, 5:debug, 6:trace)",
43 | Value: 4,
44 | }
45 | // JSONFormatFlag --json
46 | JSONFormatFlag = &cli.BoolFlag{
47 | Name: "json",
48 | Usage: "output log in json format",
49 | }
50 | // ColorFormatFlag --color
51 | ColorFormatFlag = &cli.BoolFlag{
52 | Name: "color",
53 | Usage: "output log in color text format",
54 | Value: true,
55 | }
56 | )
57 |
58 | // SetLogger set log level, json format, color, rotate ...
59 | func SetLogger(ctx *cli.Context) {
60 | logLevel := ctx.Uint64(VerbosityFlag.Name)
61 | jsonFormat := ctx.Bool(JSONFormatFlag.Name)
62 | colorFormat := ctx.Bool(ColorFormatFlag.Name)
63 | log.SetLogger(uint32(logLevel), jsonFormat, colorFormat)
64 |
65 | logFile := ctx.String(LogFileFlag.Name)
66 | if logFile != "" {
67 | logRotation := ctx.Uint64(LogRotationFlag.Name)
68 | logMaxAge := ctx.Uint64(LogMaxAgeFlag.Name)
69 | log.SetLogFile(logFile, logRotation, logMaxAge)
70 | }
71 | }
72 |
73 | // GetConfigFilePath specified by `-c|--config`
74 | func GetConfigFilePath(ctx *cli.Context) string {
75 | return ctx.String(ConfigFileFlag.Name)
76 | }
77 |
--------------------------------------------------------------------------------
/cmd/utils/licensecmd.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/urfave/cli/v2"
7 | )
8 |
9 | var (
10 | // LicenseCommand license cubcommonad
11 | LicenseCommand = &cli.Command{
12 | Action: license,
13 | Name: "license",
14 | Usage: "Display license information",
15 | ArgsUsage: " ",
16 | }
17 | )
18 |
19 | func license(_ *cli.Context) error {
20 | fmt.Println(`crossChain-Bridge is free software: you can redistribute it and/or modify
21 | it under the terms of the GNU General Public License as published by
22 | the Free Software Foundation, either version 3 of the License, or
23 | (at your option) any later version.
24 |
25 | crossChain-Bridge is distributed in the hope that it will be useful,
26 | but WITHOUT ANY WARRANTY; without even the implied warranty of
27 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 | GNU General Public License for more details.
29 |
30 | You should have received a copy of the GNU General Public License
31 | along with efsn. If not, see .`)
32 | return nil
33 | }
34 |
--------------------------------------------------------------------------------
/cmd/utils/utils.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "os"
5 | "path/filepath"
6 |
7 | "github.com/fsn-dev/crossChain-Bridge/params"
8 | "github.com/urfave/cli/v2"
9 | )
10 |
11 | var (
12 | clientIdentifier string
13 | gitCommit string
14 | )
15 |
16 | // NewApp creates an app with sane defaults.
17 | func NewApp(identifier, gitcommit, usage string) *cli.App {
18 | clientIdentifier = identifier
19 | gitCommit = gitcommit
20 | app := cli.NewApp()
21 | app.Name = filepath.Base(os.Args[0])
22 | app.Version = params.VersionWithMeta
23 | if len(gitCommit) >= 8 {
24 | app.Version += "-" + gitCommit[:8]
25 | }
26 | app.Usage = usage
27 | return app
28 | }
29 |
--------------------------------------------------------------------------------
/cmd/utils/versioncmd.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "runtime"
7 | "strings"
8 |
9 | "github.com/fsn-dev/crossChain-Bridge/params"
10 | "github.com/urfave/cli/v2"
11 | )
12 |
13 | var (
14 | // VersionCommand version subcommand
15 | VersionCommand = &cli.Command{
16 | Action: version,
17 | Name: "version",
18 | Usage: "Print version numbers",
19 | ArgsUsage: " ",
20 | Description: `
21 | The output of this command is supposed to be machine-readable.
22 | `,
23 | }
24 | )
25 |
26 | func version(ctx *cli.Context) error {
27 | fmt.Println(strings.Title(clientIdentifier))
28 | fmt.Println("Version:", params.VersionWithMeta)
29 | if gitCommit != "" {
30 | fmt.Println("Git Commit:", gitCommit)
31 | }
32 | fmt.Println("Architecture:", runtime.GOARCH)
33 | fmt.Println("Go Version:", runtime.Version())
34 | fmt.Println("Operating System:", runtime.GOOS)
35 | fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH"))
36 | fmt.Printf("GOROOT=%s\n", runtime.GOROOT())
37 | return nil
38 | }
39 |
--------------------------------------------------------------------------------
/common/big.go:
--------------------------------------------------------------------------------
1 | // Copyright 2014 The go-ethereum Authors
2 | // This file is part of the go-ethereum library.
3 | //
4 | // The go-ethereum library is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU Lesser General Public License as published by
6 | // the Free Software Foundation, either version 3 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // The go-ethereum library is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU Lesser General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU Lesser General Public License
15 | // along with the go-ethereum library. If not, see .
16 |
17 | package common
18 |
19 | import (
20 | "math"
21 | "math/big"
22 | )
23 |
24 | // Common big integers often used
25 | var (
26 | Big1 = big.NewInt(1)
27 | Big2 = big.NewInt(2)
28 | Big3 = big.NewInt(3)
29 | Big0 = big.NewInt(0)
30 | Big32 = big.NewInt(32)
31 | Big256 = big.NewInt(256)
32 | Big257 = big.NewInt(257)
33 |
34 | BigMaxUint64 = new(big.Int).SetUint64(math.MaxUint64)
35 | )
36 |
--------------------------------------------------------------------------------
/common/bytes.go:
--------------------------------------------------------------------------------
1 | // Copyright 2014 The go-ethereum Authors
2 | // This file is part of the go-ethereum library.
3 | //
4 | // The go-ethereum library is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU Lesser General Public License as published by
6 | // the Free Software Foundation, either version 3 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // The go-ethereum library is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU Lesser General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU Lesser General Public License
15 | // along with the go-ethereum library. If not, see .
16 |
17 | // Package common contains various helper functions.
18 | package common
19 |
20 | import "encoding/hex"
21 |
22 | // ToHex returns the hex representation of b, prefixed with '0x'.
23 | func ToHex(b []byte) string {
24 | enc := make([]byte, len(b)*2+2)
25 | copy(enc, "0x")
26 | hex.Encode(enc[2:], b)
27 | return string(enc)
28 | }
29 |
30 | // FromHex returns the bytes represented by the hexadecimal string s.
31 | // s may be prefixed with "0x".
32 | func FromHex(s string) []byte {
33 | if len(s) > 1 {
34 | if s[0:2] == "0x" || s[0:2] == "0X" {
35 | s = s[2:]
36 | }
37 | }
38 | if len(s)%2 == 1 {
39 | s = "0" + s
40 | }
41 | return Hex2Bytes(s)
42 | }
43 |
44 | // CopyBytes returns an exact copy of the provided bytes.
45 | func CopyBytes(b []byte) (copiedBytes []byte) {
46 | if b == nil {
47 | return nil
48 | }
49 | copiedBytes = make([]byte, len(b))
50 | copy(copiedBytes, b)
51 |
52 | return
53 | }
54 |
55 | // HasHexPrefix validates str begins with '0x' or '0X'.
56 | func HasHexPrefix(str string) bool {
57 | return len(str) >= 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X')
58 | }
59 |
60 | // IsHexCharacter returns bool of c being a valid hexadecimal.
61 | func IsHexCharacter(c byte) bool {
62 | return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')
63 | }
64 |
65 | // IsUpperHexCharacter returns bool of c being a valid uppercase hexadecimal.
66 | func IsUpperHexCharacter(c byte) bool {
67 | return c >= 'A' && c <= 'F'
68 | }
69 |
70 | // IsHex validates whether each byte is valid hexadecimal string.
71 | func IsHex(str string) bool {
72 | if len(str)%2 != 0 {
73 | return false
74 | }
75 | for _, c := range []byte(str) {
76 | if !IsHexCharacter(c) {
77 | return false
78 | }
79 | }
80 | return true
81 | }
82 |
83 | // GetUnprefixedHex returns (unprefixed hex, is hex string flag, if has uppercase hexadecimal)
84 | func GetUnprefixedHex(str string) (unprefixedHex string, ok, hasUpperChar bool) {
85 | if len(str)%2 != 0 {
86 | return
87 | }
88 | if HasHexPrefix(str) {
89 | str = str[2:]
90 | }
91 | for _, c := range []byte(str) {
92 | if !IsHexCharacter(c) {
93 | return
94 | }
95 | if !hasUpperChar && IsUpperHexCharacter(c) {
96 | hasUpperChar = true
97 | }
98 | }
99 | unprefixedHex = str
100 | ok = true
101 | return
102 | }
103 |
104 | // Bytes2Hex returns the hexadecimal encoding of d.
105 | func Bytes2Hex(d []byte) string {
106 | return hex.EncodeToString(d)
107 | }
108 |
109 | // Hex2Bytes returns the bytes represented by the hexadecimal string str.
110 | func Hex2Bytes(str string) []byte {
111 | h, _ := hex.DecodeString(str)
112 | return h
113 | }
114 |
115 | // Hex2BytesFixed returns bytes of a specified fixed length flen.
116 | func Hex2BytesFixed(str string, flen int) []byte {
117 | h, _ := hex.DecodeString(str)
118 | if len(h) == flen {
119 | return h
120 | }
121 | if len(h) > flen {
122 | return h[len(h)-flen:]
123 | }
124 | hh := make([]byte, flen)
125 | copy(hh[flen-len(h):flen], h)
126 | return hh
127 | }
128 |
129 | // RightPadBytes zero-pads slice to the right up to length l.
130 | func RightPadBytes(slice []byte, l int) []byte {
131 | if l <= len(slice) {
132 | return slice
133 | }
134 |
135 | padded := make([]byte, l)
136 | copy(padded, slice)
137 |
138 | return padded
139 | }
140 |
141 | // LeftPadBytes zero-pads slice to the left up to length l.
142 | func LeftPadBytes(slice []byte, l int) []byte {
143 | if l <= len(slice) {
144 | return slice
145 | }
146 |
147 | padded := make([]byte, l)
148 | copy(padded[l-len(slice):], slice)
149 |
150 | return padded
151 | }
152 |
--------------------------------------------------------------------------------
/common/hash.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "encoding/hex"
5 | "fmt"
6 | "math/big"
7 | "math/rand"
8 | "reflect"
9 |
10 | "github.com/fsn-dev/crossChain-Bridge/common/hexutil"
11 | )
12 |
13 | // Lengths of hashes in bytes.
14 | const (
15 | // HashLength is the expected length of the hash
16 | HashLength = 32
17 | )
18 |
19 | var (
20 | hashT = reflect.TypeOf(Hash{})
21 | )
22 |
23 | // Hash represents the 32 byte Keccak256 hash of arbitrary data.
24 | type Hash [HashLength]byte
25 |
26 | // BytesToHash sets b to hash.
27 | // If b is larger than len(h), b will be cropped from the left.
28 | func BytesToHash(b []byte) Hash {
29 | var h Hash
30 | h.SetBytes(b)
31 | return h
32 | }
33 |
34 | // BigToHash sets byte representation of b to hash.
35 | // If b is larger than len(h), b will be cropped from the left.
36 | func BigToHash(b *big.Int) Hash { return BytesToHash(b.Bytes()) }
37 |
38 | // HexToHash sets byte representation of s to hash.
39 | // If b is larger than len(h), b will be cropped from the left.
40 | func HexToHash(s string) Hash { return BytesToHash(FromHex(s)) }
41 |
42 | // Bytes gets the byte representation of the underlying hash.
43 | func (h Hash) Bytes() []byte { return h[:] }
44 |
45 | // Big converts a hash to a big integer.
46 | func (h Hash) Big() *big.Int { return new(big.Int).SetBytes(h[:]) }
47 |
48 | // Hex converts a hash to a hex string.
49 | func (h Hash) Hex() string { return hexutil.Encode(h[:]) }
50 |
51 | // TerminalString implements log.TerminalStringer, formatting a string for console
52 | // output during logging.
53 | func (h Hash) TerminalString() string {
54 | return fmt.Sprintf("%x…%x", h[:3], h[29:])
55 | }
56 |
57 | // String implements the stringer interface and is used also by the logger when
58 | // doing full logging into a file.
59 | func (h Hash) String() string {
60 | return h.Hex()
61 | }
62 |
63 | // Format implements fmt.Formatter, forcing the byte slice to be formatted as is,
64 | // without going through the stringer interface used for logging.
65 | func (h Hash) Format(s fmt.State, c rune) {
66 | fmt.Fprintf(s, "%"+string(c), h[:])
67 | }
68 |
69 | // UnmarshalText parses a hash in hex syntax.
70 | func (h *Hash) UnmarshalText(input []byte) error {
71 | return hexutil.UnmarshalFixedText("Hash", input, h[:])
72 | }
73 |
74 | // UnmarshalJSON parses a hash in hex syntax.
75 | func (h *Hash) UnmarshalJSON(input []byte) error {
76 | return hexutil.UnmarshalFixedJSON(hashT, input, h[:])
77 | }
78 |
79 | // MarshalText returns the hex representation of h.
80 | func (h Hash) MarshalText() ([]byte, error) {
81 | return hexutil.Bytes(h[:]).MarshalText()
82 | }
83 |
84 | // SetBytes sets the hash to the value of b.
85 | // If b is larger than len(h), b will be cropped from the left.
86 | func (h *Hash) SetBytes(b []byte) {
87 | if len(b) > len(h) {
88 | b = b[len(b)-HashLength:]
89 | }
90 |
91 | copy(h[HashLength-len(b):], b)
92 | }
93 |
94 | // Generate implements testing/quick.Generator.
95 | func (h Hash) Generate(rander *rand.Rand, size int) reflect.Value {
96 | m := rander.Intn(len(h))
97 | for i := len(h) - 1; i > m; i-- {
98 | h[i] = byte(rander.Uint32())
99 | }
100 | return reflect.ValueOf(h)
101 | }
102 |
103 | // Scan implements Scanner for database/sql.
104 | func (h *Hash) Scan(src interface{}) error {
105 | srcB, ok := src.([]byte)
106 | if !ok {
107 | return fmt.Errorf("can't scan %T into Hash", src)
108 | }
109 | if len(srcB) != HashLength {
110 | return fmt.Errorf("can't scan []byte of len %d into Hash, want %d", len(srcB), HashLength)
111 | }
112 | copy(h[:], srcB)
113 | return nil
114 | }
115 |
116 | // ImplementsGraphQLType returns true if Hash implements the specified GraphQL type.
117 | func (Hash) ImplementsGraphQLType(name string) bool { return name == "Bytes32" }
118 |
119 | // UnmarshalGraphQL unmarshals the provided GraphQL query data.
120 | func (h *Hash) UnmarshalGraphQL(input interface{}) error {
121 | var err error
122 | switch input := input.(type) {
123 | case string:
124 | err = h.UnmarshalText([]byte(input))
125 | default:
126 | err = fmt.Errorf("unexpected type %T for Hash", input)
127 | }
128 | return err
129 | }
130 |
131 | // UnprefixedHash allows marshaling a Hash without 0x prefix.
132 | type UnprefixedHash Hash
133 |
134 | // UnmarshalText decodes the hash from hex. The 0x prefix is optional.
135 | func (h *UnprefixedHash) UnmarshalText(input []byte) error {
136 | return hexutil.UnmarshalFixedUnprefixedText("UnprefixedHash", input, h[:])
137 | }
138 |
139 | // MarshalText encodes the hash as hex.
140 | func (h UnprefixedHash) MarshalText() ([]byte, error) {
141 | return []byte(hex.EncodeToString(h[:])), nil
142 | }
143 |
--------------------------------------------------------------------------------
/common/hexutil/json_example_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 The go-ethereum Authors
2 | // This file is part of the go-ethereum library.
3 | //
4 | // The go-ethereum library is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU Lesser General Public License as published by
6 | // the Free Software Foundation, either version 3 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // The go-ethereum library is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU Lesser General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU Lesser General Public License
15 | // along with the go-ethereum library. If not, see .
16 |
17 | package hexutil_test
18 |
19 | import (
20 | "encoding/json"
21 | "fmt"
22 |
23 | "github.com/fsn-dev/crossChain-Bridge/common/hexutil"
24 | )
25 |
26 | type MyType [5]byte
27 |
28 | func (v *MyType) UnmarshalText(input []byte) error {
29 | return hexutil.UnmarshalFixedText("MyType", input, v[:])
30 | }
31 |
32 | func (v MyType) String() string {
33 | return hexutil.Bytes(v[:]).String()
34 | }
35 |
36 | func ExampleUnmarshalFixedText() {
37 | var v1, v2 MyType
38 | fmt.Println("v1 error:", json.Unmarshal([]byte(`"0x01"`), &v1))
39 | fmt.Println("v2 error:", json.Unmarshal([]byte(`"0x0101010101"`), &v2))
40 | fmt.Println("v2:", v2)
41 | // Output:
42 | // v1 error: hex string has length 2, want 10 for MyType
43 | // v2 error:
44 | // v2: 0x0101010101
45 | }
46 |
--------------------------------------------------------------------------------
/common/math/integer.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 The go-ethereum Authors
2 | // This file is part of the go-ethereum library.
3 | //
4 | // The go-ethereum library is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU Lesser General Public License as published by
6 | // the Free Software Foundation, either version 3 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // The go-ethereum library is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU Lesser General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU Lesser General Public License
15 | // along with the go-ethereum library. If not, see .
16 |
17 | package math
18 |
19 | import (
20 | "fmt"
21 | "strconv"
22 | )
23 |
24 | // Integer limit values.
25 | const (
26 | MaxInt8 = 1<<7 - 1
27 | MinInt8 = -1 << 7
28 | MaxInt16 = 1<<15 - 1
29 | MinInt16 = -1 << 15
30 | MaxInt32 = 1<<31 - 1
31 | MinInt32 = -1 << 31
32 | MaxInt64 = 1<<63 - 1
33 | MinInt64 = -1 << 63
34 | MaxUint8 = 1<<8 - 1
35 | MaxUint16 = 1<<16 - 1
36 | MaxUint32 = 1<<32 - 1
37 | MaxUint64 = 1<<64 - 1
38 | )
39 |
40 | // HexOrDecimal64 marshals uint64 as hex or decimal.
41 | type HexOrDecimal64 uint64
42 |
43 | // UnmarshalText implements encoding.TextUnmarshaler.
44 | func (i *HexOrDecimal64) UnmarshalText(input []byte) error {
45 | num, ok := ParseUint64(string(input))
46 | if !ok {
47 | return fmt.Errorf("invalid hex or decimal integer %q", input)
48 | }
49 | *i = HexOrDecimal64(num)
50 | return nil
51 | }
52 |
53 | // MarshalText implements encoding.TextMarshaler.
54 | func (i HexOrDecimal64) MarshalText() ([]byte, error) {
55 | return []byte(fmt.Sprintf("%#x", uint64(i))), nil
56 | }
57 |
58 | // ParseInt parses s as an integer in decimal syntax
59 | func ParseInt(s string) (int, error) {
60 | return strconv.Atoi(s)
61 | }
62 |
63 | // MustParseInt parses s as an integer and panics if the string is invalid.
64 | func MustParseInt(s string) int {
65 | v, err := ParseInt(s)
66 | if err != nil {
67 | panic("invalid signed integer: " + s)
68 | }
69 | return v
70 | }
71 |
72 | // ParseUint64 parses s as an integer in decimal or hexadecimal syntax.
73 | // Leading zeros are accepted. The empty string parses as zero.
74 | func ParseUint64(s string) (uint64, bool) {
75 | if s == "" {
76 | return 0, true
77 | }
78 | if len(s) >= 2 && (s[:2] == "0x" || s[:2] == "0X") {
79 | v, err := strconv.ParseUint(s[2:], 16, 64)
80 | return v, err == nil
81 | }
82 | v, err := strconv.ParseUint(s, 10, 64)
83 | return v, err == nil
84 | }
85 |
86 | // MustParseUint64 parses s as an integer and panics if the string is invalid.
87 | func MustParseUint64(s string) uint64 {
88 | v, ok := ParseUint64(s)
89 | if !ok {
90 | panic("invalid unsigned 64 bit integer: " + s)
91 | }
92 | return v
93 | }
94 |
95 | // NOTE: The following methods need to be optimized using either bit checking or asm
96 |
97 | // SafeSub returns subtraction result and whether overflow occurred.
98 | func SafeSub(x, y uint64) (uint64, bool) {
99 | return x - y, x < y
100 | }
101 |
102 | // SafeAdd returns the result and whether overflow occurred.
103 | func SafeAdd(x, y uint64) (uint64, bool) {
104 | return x + y, y > MaxUint64-x
105 | }
106 |
107 | // SafeMul returns multiplication result and whether overflow occurred.
108 | func SafeMul(x, y uint64) (uint64, bool) {
109 | if x == 0 || y == 0 {
110 | return 0, false
111 | }
112 | return x * y, y > MaxUint64/x
113 | }
114 |
--------------------------------------------------------------------------------
/common/math/integer_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 The go-ethereum Authors
2 | // This file is part of the go-ethereum library.
3 | //
4 | // The go-ethereum library is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU Lesser General Public License as published by
6 | // the Free Software Foundation, either version 3 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // The go-ethereum library is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU Lesser General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU Lesser General Public License
15 | // along with the go-ethereum library. If not, see .
16 |
17 | package math
18 |
19 | import (
20 | "testing"
21 | )
22 |
23 | type operation byte
24 |
25 | const (
26 | sub operation = iota
27 | add
28 | mul
29 | )
30 |
31 | func TestOverflow(t *testing.T) {
32 | for i, test := range []struct {
33 | x uint64
34 | y uint64
35 | overflow bool
36 | op operation
37 | }{
38 | // add operations
39 | {MaxUint64, 1, true, add},
40 | {MaxUint64 - 1, 1, false, add},
41 |
42 | // sub operations
43 | {0, 1, true, sub},
44 | {0, 0, false, sub},
45 |
46 | // mul operations
47 | {0, 0, false, mul},
48 | {10, 10, false, mul},
49 | {MaxUint64, 2, true, mul},
50 | {MaxUint64, 1, false, mul},
51 | } {
52 | var overflows bool
53 | switch test.op {
54 | case sub:
55 | _, overflows = SafeSub(test.x, test.y)
56 | case add:
57 | _, overflows = SafeAdd(test.x, test.y)
58 | case mul:
59 | _, overflows = SafeMul(test.x, test.y)
60 | }
61 |
62 | if test.overflow != overflows {
63 | t.Errorf("%d failed. Expected test to be %v, got %v", i, test.overflow, overflows)
64 | }
65 | }
66 | }
67 |
68 | func TestHexOrDecimal64(t *testing.T) {
69 | tests := []struct {
70 | input string
71 | num uint64
72 | ok bool
73 | }{
74 | {"", 0, true},
75 | {"0", 0, true},
76 | {"0x0", 0, true},
77 | {"12345678", 12345678, true},
78 | {"0x12345678", 0x12345678, true},
79 | {"0X12345678", 0x12345678, true},
80 | // Tests for leading zero behaviour:
81 | {"0123456789", 123456789, true}, // note: not octal
82 | {"0x00", 0, true},
83 | {"0x012345678abc", 0x12345678abc, true},
84 | // Invalid syntax:
85 | {"abcdef", 0, false},
86 | {"0xgg", 0, false},
87 | // Doesn't fit into 64 bits:
88 | {"18446744073709551617", 0, false},
89 | }
90 | for _, test := range tests {
91 | var num HexOrDecimal64
92 | err := num.UnmarshalText([]byte(test.input))
93 | if (err == nil) != test.ok {
94 | t.Errorf("ParseUint64(%q) -> (err == nil) = %t, want %t", test.input, err == nil, test.ok)
95 | continue
96 | }
97 | if err == nil && uint64(num) != test.num {
98 | t.Errorf("ParseUint64(%q) -> %d, want %d", test.input, num, test.num)
99 | }
100 | }
101 | }
102 |
103 | func TestMustParseUint64(t *testing.T) {
104 | if v := MustParseUint64("12345"); v != 12345 {
105 | t.Errorf(`MustParseUint64("12345") = %d, want 12345`, v)
106 | }
107 | }
108 |
109 | func TestMustParseUint64Panic(t *testing.T) {
110 | defer func() {
111 | if recover() == nil {
112 | t.Error("MustParseBig should've panicked")
113 | }
114 | }()
115 | MustParseUint64("ggg")
116 | }
117 |
--------------------------------------------------------------------------------
/common/path.go:
--------------------------------------------------------------------------------
1 | // Copyright 2014 The go-ethereum Authors
2 | // This file is part of the go-ethereum library.
3 | //
4 | // The go-ethereum library is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU Lesser General Public License as published by
6 | // the Free Software Foundation, either version 3 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // The go-ethereum library is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU Lesser General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU Lesser General Public License
15 | // along with the go-ethereum library. If not, see .
16 |
17 | package common
18 |
19 | import (
20 | "fmt"
21 | "os"
22 | "path/filepath"
23 | "runtime"
24 | )
25 |
26 | // MakeName creates a node name that follows the ethereum convention
27 | // for such names. It adds the operation system name and Go runtime version
28 | // the name.
29 | func MakeName(name, version string) string {
30 | return fmt.Sprintf("%s/v%s/%s/%s", name, version, runtime.GOOS, runtime.Version())
31 | }
32 |
33 | // FileExist checks if a file exists at filePath.
34 | func FileExist(filePath string) bool {
35 | _, err := os.Stat(filePath)
36 | if err != nil && os.IsNotExist(err) {
37 | return false
38 | }
39 |
40 | return true
41 | }
42 |
43 | // AbsolutePath returns datadir + filename, or filename if it is absolute.
44 | func AbsolutePath(datadir, filename string) string {
45 | if filepath.IsAbs(filename) {
46 | return filename
47 | }
48 | return filepath.Join(datadir, filename)
49 | }
50 |
51 | // ExecuteDir returns the execute directory
52 | func ExecuteDir() (string, error) {
53 | return filepath.Abs(filepath.Dir(os.Args[0]))
54 | }
55 |
--------------------------------------------------------------------------------
/common/utils.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "encoding/json"
5 | "errors"
6 | "math/big"
7 | "strconv"
8 | "strings"
9 | "time"
10 |
11 | cmath "github.com/fsn-dev/crossChain-Bridge/common/math"
12 | "golang.org/x/crypto/sha3"
13 | )
14 |
15 | // ToJSONString to json string
16 | func ToJSONString(content interface{}, pretty bool) string {
17 | var data []byte
18 | if pretty {
19 | data, _ = json.MarshalIndent(content, "", " ")
20 | } else {
21 | data, _ = json.Marshal(content)
22 | }
23 | return string(data)
24 | }
25 |
26 | // Keccak256Hash calc keccak hash.
27 | func Keccak256Hash(data ...[]byte) (h Hash) {
28 | d := sha3.NewLegacyKeccak256()
29 | for _, b := range data {
30 | _, _ = d.Write(b)
31 | }
32 | d.Sum(h[:0])
33 | return h
34 | }
35 |
36 | // IsEqualIgnoreCase returns if s1 and s2 are equal ignore case.
37 | func IsEqualIgnoreCase(s1, s2 string) bool {
38 | return strings.EqualFold(s1, s2)
39 | }
40 |
41 | // BigFromUint64 new big int from uint64 value.
42 | func BigFromUint64(value uint64) *big.Int {
43 | return new(big.Int).SetUint64(value)
44 | }
45 |
46 | // GetBigIntFromStr new big int from string.
47 | func GetBigIntFromStr(str string) (*big.Int, error) {
48 | bi, ok := cmath.ParseBig256(str)
49 | if !ok {
50 | return nil, errors.New("invalid 256 bit integer: " + str)
51 | }
52 | return bi, nil
53 | }
54 |
55 | // GetIntFromStr get int from string.
56 | func GetIntFromStr(str string) (int, error) {
57 | res, err := cmath.ParseInt(str)
58 | if err != nil {
59 | return 0, errors.New("invalid signed integer: " + str)
60 | }
61 | return res, nil
62 | }
63 |
64 | // GetUint64FromStr get uint64 from string.
65 | func GetUint64FromStr(str string) (uint64, error) {
66 | res, ok := cmath.ParseUint64(str)
67 | if !ok {
68 | return 0, errors.New("invalid unsigned 64 bit integer: " + str)
69 | }
70 | return res, nil
71 | }
72 |
73 | // Now returns timestamp of the point of calling.
74 | func Now() int64 {
75 | return time.Now().Unix()
76 | }
77 |
78 | // NowStr returns now timestamp of string format.
79 | func NowStr() string {
80 | return strconv.FormatInt((time.Now().Unix()), 10)
81 | }
82 |
83 | // NowMilli returns now timestamp in miliseconds
84 | func NowMilli() int64 {
85 | return time.Now().UnixNano() / 1e6
86 | }
87 |
88 | // NowMilliStr returns now timestamp in miliseconds of string format.
89 | func NowMilliStr() string {
90 | return strconv.FormatInt((time.Now().UnixNano() / 1e6), 10)
91 | }
92 |
93 | // MinUint64 get minimum value of x and y
94 | func MinUint64(x, y uint64) uint64 {
95 | if x <= y {
96 | return x
97 | }
98 | return y
99 | }
100 |
101 | // MaxUint64 get maximum calue of x and y.
102 | func MaxUint64(x, y uint64) uint64 {
103 | if x < y {
104 | return y
105 | }
106 | return x
107 | }
108 |
109 | // GetData get data[start:start+size] (won't out of index range),
110 | // and right padding the bytes to size long
111 | func GetData(data []byte, start, size uint64) []byte {
112 | length := uint64(len(data))
113 | if start > length {
114 | start = length
115 | }
116 | end := start + size
117 | if end > length {
118 | end = length
119 | }
120 | return RightPadBytes(data[start:end], int(size))
121 | }
122 |
123 | // BigUint64 big to uint64 and an overflow flag.
124 | func BigUint64(v *big.Int) (uint64, bool) {
125 | return v.Uint64(), !v.IsUint64()
126 | }
127 |
128 | // GetBigInt get big int from data[start:start+size]
129 | func GetBigInt(data []byte, start, size uint64) *big.Int {
130 | return new(big.Int).SetBytes(GetData(data, start, size))
131 | }
132 |
133 | // GetUint64 get uint64 from data[start:start+size]
134 | func GetUint64(data []byte, start, size uint64) (uint64, bool) {
135 | return BigUint64(GetBigInt(data, start, size))
136 | }
137 |
--------------------------------------------------------------------------------
/dcrm/accept.go:
--------------------------------------------------------------------------------
1 | package dcrm
2 |
3 | import (
4 | "encoding/json"
5 |
6 | "github.com/fsn-dev/crossChain-Bridge/common"
7 | )
8 |
9 | // DoAcceptSign accept sign
10 | func DoAcceptSign(keyID, agreeResult string, msgHash, msgContext []string) (string, error) {
11 | nonce := uint64(0)
12 | data := AcceptData{
13 | TxType: "ACCEPTSIGN",
14 | Key: keyID,
15 | Accept: agreeResult,
16 | MsgHash: msgHash,
17 | //MsgContext: msgContext, // context is verified on top level
18 | TimeStamp: common.NowMilliStr(),
19 | }
20 | payload, err := json.Marshal(data)
21 | if err != nil {
22 | return "", err
23 | }
24 | rawTX, err := BuildDcrmRawTx(nonce, payload)
25 | if err != nil {
26 | return "", err
27 | }
28 | return AcceptSign(rawTX)
29 | }
30 |
--------------------------------------------------------------------------------
/dcrm/api.go:
--------------------------------------------------------------------------------
1 | package dcrm
2 |
3 | import (
4 | "encoding/json"
5 | "errors"
6 | "fmt"
7 |
8 | "github.com/fsn-dev/crossChain-Bridge/common"
9 | "github.com/fsn-dev/crossChain-Bridge/log"
10 | "github.com/fsn-dev/crossChain-Bridge/rpc/client"
11 | )
12 |
13 | // get dcrm sign status error
14 | var (
15 | ErrGetSignStatusTimeout = errors.New("getSignStatus timeout")
16 | ErrGetSignStatusFailed = errors.New("getSignStatus failure")
17 | )
18 |
19 | const (
20 | successStatus = "Success"
21 | )
22 |
23 | func newWrongStatusError(subject, status, errInfo string) error {
24 | return fmt.Errorf("[%v] Wrong status \"%v\", err=\"%v\"", subject, status, errInfo)
25 | }
26 |
27 | func wrapPostError(method string, err error) error {
28 | return fmt.Errorf("[post] %v error, %v", method, err)
29 | }
30 |
31 | func httpPost(result interface{}, method string, params ...interface{}) error {
32 | return client.RPCPost(&result, dcrmRPCAddress, method, params...)
33 | }
34 |
35 | // GetEnode call dcrm_getEnode
36 | func GetEnode() (string, error) {
37 | var result GetEnodeResp
38 | err := httpPost(&result, "dcrm_getEnode")
39 | if err != nil {
40 | return "", wrapPostError("dcrm_getEnode", err)
41 | }
42 | if result.Status != successStatus {
43 | return "", newWrongStatusError("getEnode", result.Status, result.Error)
44 | }
45 | return result.Data.Enode, nil
46 | }
47 |
48 | // GetSignNonce call dcrm_getSignNonce
49 | func GetSignNonce() (uint64, error) {
50 | var result DataResultResp
51 | err := httpPost(&result, "dcrm_getSignNonce", keyWrapper.Address.String())
52 | if err != nil {
53 | return 0, wrapPostError("dcrm_getSignNonce", err)
54 | }
55 | if result.Status != successStatus {
56 | return 0, newWrongStatusError("getSignNonce", result.Status, result.Error)
57 | }
58 | bi, err := common.GetBigIntFromStr(result.Data.Result)
59 | if err != nil {
60 | return 0, fmt.Errorf("getSignNonce can't parse result as big int, %v", err)
61 | }
62 | return bi.Uint64(), nil
63 | }
64 |
65 | // GetSignStatus call dcrm_getSignStatus
66 | func GetSignStatus(key string) (*SignStatus, error) {
67 | var result DataResultResp
68 | err := httpPost(&result, "dcrm_getSignStatus", key)
69 | if err != nil {
70 | return nil, wrapPostError("dcrm_getSignStatus", err)
71 | }
72 | if result.Status != successStatus {
73 | return nil, newWrongStatusError("getSignStatus", result.Status, "response error "+result.Error)
74 | }
75 | data := result.Data.Result
76 | var signStatus SignStatus
77 | err = json.Unmarshal([]byte(data), &signStatus)
78 | if err != nil {
79 | return nil, wrapPostError("dcrm_getSignStatus", err)
80 | }
81 | switch signStatus.Status {
82 | case "Failure":
83 | log.Info("getSignStatus Failure", "keyID", key, "status", data)
84 | return nil, ErrGetSignStatusFailed
85 | case "Timeout":
86 | log.Info("getSignStatus Timeout", "keyID", key, "status", data)
87 | return nil, ErrGetSignStatusTimeout
88 | case successStatus:
89 | return &signStatus, nil
90 | default:
91 | return nil, newWrongStatusError("getSignStatus", signStatus.Status, "sign status error "+signStatus.Error)
92 | }
93 | }
94 |
95 | // GetCurNodeSignInfo call dcrm_getCurNodeSignInfo
96 | func GetCurNodeSignInfo() ([]*SignInfoData, error) {
97 | var result SignInfoResp
98 | err := httpPost(&result, "dcrm_getCurNodeSignInfo", keyWrapper.Address.String())
99 | if err != nil {
100 | return nil, wrapPostError("dcrm_getCurNodeSignInfo", err)
101 | }
102 | if result.Status != successStatus {
103 | return nil, newWrongStatusError("getCurNodeSignInfo", result.Status, result.Error)
104 | }
105 | return result.Data, nil
106 | }
107 |
108 | // Sign call dcrm_sign
109 | func Sign(raw string) (string, error) {
110 | var result DataResultResp
111 | err := httpPost(&result, "dcrm_sign", raw)
112 | if err != nil {
113 | return "", wrapPostError("dcrm_sign", err)
114 | }
115 | if result.Status != successStatus {
116 | return "", newWrongStatusError("sign", result.Status, result.Error)
117 | }
118 | return result.Data.Result, nil
119 | }
120 |
121 | // AcceptSign call dcrm_acceptSign
122 | func AcceptSign(raw string) (string, error) {
123 | var result DataResultResp
124 | err := httpPost(&result, "dcrm_acceptSign", raw)
125 | if err != nil {
126 | return "", wrapPostError("dcrm_acceptSign", err)
127 | }
128 | if result.Status != successStatus {
129 | return "", newWrongStatusError("acceptSign", result.Status, result.Error)
130 | }
131 | return result.Data.Result, nil
132 | }
133 |
134 | // GetGroupByID call dcrm_getGroupByID
135 | func GetGroupByID(groupID string) (*GroupInfo, error) {
136 | var result GetGroupByIDResp
137 | err := httpPost(&result, "dcrm_getGroupByID", groupID)
138 | if err != nil {
139 | return nil, wrapPostError("dcrm_getGroupByID", err)
140 | }
141 | if result.Status != successStatus {
142 | return nil, newWrongStatusError("getGroupByID", result.Status, result.Error)
143 | }
144 | return result.Data, nil
145 | }
146 |
--------------------------------------------------------------------------------
/dcrm/init.go:
--------------------------------------------------------------------------------
1 | package dcrm
2 |
3 | import (
4 | "math/big"
5 |
6 | "github.com/fsn-dev/crossChain-Bridge/common"
7 | "github.com/fsn-dev/crossChain-Bridge/tools"
8 | "github.com/fsn-dev/crossChain-Bridge/tools/keystore"
9 | "github.com/fsn-dev/crossChain-Bridge/types"
10 | )
11 |
12 | const (
13 | // DcrmToAddress used in dcrm sign and accept
14 | DcrmToAddress = "0x00000000000000000000000000000000000000dc"
15 | // DcrmWalletServiceID to make dcrm signer
16 | DcrmWalletServiceID = 30400
17 | )
18 |
19 | var (
20 | dcrmSigner = types.MakeSigner("EIP155", big.NewInt(DcrmWalletServiceID))
21 | dcrmToAddr = common.HexToAddress(DcrmToAddress)
22 | signGroups []string // sub groups for sign
23 |
24 | keyWrapper *keystore.Key
25 | dcrmUser common.Address
26 | dcrmRPCAddress string
27 |
28 | signPubkey string
29 | groupID string
30 | threshold string
31 | mode string
32 |
33 | // ServerDcrmUser dcrm initiator for sign
34 | ServerDcrmUser common.Address
35 | )
36 |
37 | // SetDcrmRPCAddress set dcrm node rpc address
38 | func SetDcrmRPCAddress(url string) {
39 | dcrmRPCAddress = url
40 | }
41 |
42 | // SetSignPubkey set dcrm account public key
43 | func SetSignPubkey(pubkey string) {
44 | signPubkey = pubkey
45 | }
46 |
47 | // SetDcrmGroup set dcrm group
48 | func SetDcrmGroup(group, thresh, mod string) {
49 | groupID = group
50 | threshold = thresh
51 | mode = mod
52 | }
53 |
54 | // GetGroupID return dcrm group id
55 | func GetGroupID() string {
56 | return groupID
57 | }
58 |
59 | // SetSignGroups set sign subgroups
60 | func SetSignGroups(groups []string) {
61 | signGroups = groups
62 | }
63 |
64 | // GetSignGroups get sign subgroups
65 | func GetSignGroups() []string {
66 | return signGroups
67 | }
68 |
69 | // LoadKeyStore load keystore
70 | func LoadKeyStore(keyfile, passfile string) error {
71 | key, err := tools.LoadKeyStore(keyfile, passfile)
72 | if err != nil {
73 | return err
74 | }
75 | keyWrapper = key
76 | dcrmUser = keyWrapper.Address
77 | return nil
78 | }
79 |
80 | // GetDcrmUser returns the dcrm user of specified keystore
81 | func GetDcrmUser() common.Address {
82 | return dcrmUser
83 | }
84 |
85 | // IsSwapServer returns if this dcrm user is the swap server
86 | func IsSwapServer() bool {
87 | return GetDcrmUser() == ServerDcrmUser
88 | }
89 |
--------------------------------------------------------------------------------
/dcrm/sign.go:
--------------------------------------------------------------------------------
1 | package dcrm
2 |
3 | import (
4 | "crypto/rand"
5 | "encoding/json"
6 | "math/big"
7 |
8 | "github.com/fsn-dev/crossChain-Bridge/common"
9 | "github.com/fsn-dev/crossChain-Bridge/log"
10 | "github.com/fsn-dev/crossChain-Bridge/tools/crypto"
11 | "github.com/fsn-dev/crossChain-Bridge/tools/rlp"
12 | "github.com/fsn-dev/crossChain-Bridge/types"
13 | )
14 |
15 | // DoSignOne dcrm sign single msgHash with context msgContext
16 | func DoSignOne(msgHash, msgContext string) (string, error) {
17 | return DoSign([]string{msgHash}, []string{msgContext})
18 | }
19 |
20 | // DoSign dcrm sign msgHash with context msgContext
21 | func DoSign(msgHash, msgContext []string) (string, error) {
22 | log.Debug("dcrm DoSign", "msgHash", msgHash, "msgContext", msgContext)
23 | nonce, err := GetSignNonce()
24 | if err != nil {
25 | return "", err
26 | }
27 | // randomly pick sub-group to sign
28 | randIndex, _ := rand.Int(rand.Reader, big.NewInt(int64(len(signGroups))))
29 | signGroup := signGroups[randIndex.Int64()]
30 | txdata := SignData{
31 | TxType: "SIGN",
32 | PubKey: signPubkey,
33 | MsgHash: msgHash,
34 | MsgContext: msgContext,
35 | Keytype: "ECDSA",
36 | GroupID: signGroup,
37 | ThresHold: threshold,
38 | Mode: mode,
39 | TimeStamp: common.NowMilliStr(),
40 | }
41 | payload, _ := json.Marshal(txdata)
42 | rawTX, err := BuildDcrmRawTx(nonce, payload)
43 | if err != nil {
44 | return "", err
45 | }
46 | return Sign(rawTX)
47 | }
48 |
49 | // BuildDcrmRawTx build dcrm raw tx
50 | func BuildDcrmRawTx(nonce uint64, payload []byte) (string, error) {
51 | tx := types.NewTransaction(
52 | nonce, // nonce
53 | dcrmToAddr, // to address
54 | big.NewInt(0), // value
55 | 100000, // gasLimit
56 | big.NewInt(80000), // gasPrice
57 | payload, // data
58 | )
59 | signature, err := crypto.Sign(dcrmSigner.Hash(tx).Bytes(), keyWrapper.PrivateKey)
60 | if err != nil {
61 | return "", err
62 | }
63 | sigTx, err := tx.WithSignature(dcrmSigner, signature)
64 | if err != nil {
65 | return "", err
66 | }
67 | txdata, err := rlp.EncodeToBytes(sigTx)
68 | if err != nil {
69 | return "", err
70 | }
71 | rawTX := common.ToHex(txdata)
72 | return rawTX, nil
73 | }
74 |
--------------------------------------------------------------------------------
/dcrm/types.go:
--------------------------------------------------------------------------------
1 | package dcrm
2 |
3 | // DataEnode enode
4 | type DataEnode struct {
5 | Enode string
6 | }
7 |
8 | // GetEnodeResp enode response
9 | type GetEnodeResp struct {
10 | Status string
11 | Tip string
12 | Error string
13 | Data *DataEnode
14 | }
15 |
16 | // DataResult result
17 | type DataResult struct {
18 | Result string `json:"result"`
19 | }
20 |
21 | // DataResultResp result response
22 | type DataResultResp struct {
23 | Status string
24 | Tip string
25 | Error string
26 | Data *DataResult
27 | }
28 |
29 | // SignReply sign reply
30 | type SignReply struct {
31 | Enode string
32 | Status string
33 | TimeStamp string
34 | Initiator string
35 | }
36 |
37 | // SignStatus sign status
38 | type SignStatus struct {
39 | Status string
40 | Rsv []string
41 | Tip string
42 | Error string
43 | AllReply []*SignReply
44 | TimeStamp string
45 | }
46 |
47 | // SignInfoData sign info
48 | type SignInfoData struct {
49 | Account string
50 | GroupID string
51 | Key string
52 | KeyType string
53 | Mode string
54 | MsgHash []string
55 | MsgContext []string
56 | Nonce string
57 | PubKey string
58 | ThresHold string
59 | TimeStamp string
60 | }
61 |
62 | // SignInfoResp sign info response
63 | type SignInfoResp struct {
64 | Status string
65 | Tip string
66 | Error string
67 | Data []*SignInfoData
68 | }
69 |
70 | // SignData sign data
71 | type SignData struct {
72 | TxType string
73 | PubKey string
74 | MsgHash []string
75 | MsgContext []string
76 | Keytype string
77 | GroupID string
78 | ThresHold string
79 | Mode string
80 | TimeStamp string
81 | }
82 |
83 | // AcceptData accpet data
84 | type AcceptData struct {
85 | TxType string
86 | Key string
87 | Accept string
88 | MsgHash []string
89 | MsgContext []string
90 | TimeStamp string
91 | }
92 |
93 | // GroupInfo group info
94 | type GroupInfo struct {
95 | GID string
96 | Count int
97 | Enodes []string
98 | }
99 |
100 | // GetGroupByIDResp group response
101 | type GetGroupByIDResp struct {
102 | Status string
103 | Tip string
104 | Error string
105 | Data *GroupInfo
106 | }
107 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/fsn-dev/crossChain-Bridge
2 |
3 | go 1.14
4 |
5 | require (
6 | github.com/BurntSushi/toml v0.3.1
7 | github.com/btcsuite/btcd v0.20.1-beta
8 | github.com/btcsuite/btcutil v1.0.2
9 | github.com/btcsuite/btcwallet/wallet/txauthor v1.0.0
10 | github.com/btcsuite/btcwallet/wallet/txrules v1.0.0
11 | github.com/btcsuite/btcwallet/wallet/txsizes v1.0.0
12 | github.com/gorilla/handlers v1.4.2
13 | github.com/gorilla/mux v1.7.4
14 | github.com/gorilla/rpc v1.2.0
15 | github.com/lestrrat-go/file-rotatelogs v2.3.0+incompatible
16 | github.com/lestrrat-go/strftime v1.0.1 // indirect
17 | github.com/pborman/uuid v1.2.0
18 | github.com/pkg/errors v0.9.1 // indirect
19 | github.com/sirupsen/logrus v1.5.0
20 | github.com/stretchr/testify v1.5.1
21 | github.com/urfave/cli/v2 v2.2.0
22 | golang.org/x/crypto v0.0.0-20200414173820-0848c9571904
23 | gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22
24 | )
25 |
--------------------------------------------------------------------------------
/gofmt.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | find_files() {
4 | find . ! \( \
5 | \( \
6 | -path '.github' \
7 | -o -path '.git' \
8 | -o -path './bin' \
9 | -o -path '*/vendor/*' \
10 | \) -prune \
11 | \) -name '*.go'
12 | }
13 |
14 | GOFMT="gofmt -s -w"
15 | GOIMPORTS="goimports -w"
16 | find_files | xargs $GOFMT
17 | find_files | xargs $GOIMPORTS
18 |
--------------------------------------------------------------------------------
/internal/build/util.go:
--------------------------------------------------------------------------------
1 | // Copyright 2016 The go-ethereum Authors
2 | // This file is part of the go-ethereum library.
3 | //
4 | // The go-ethereum library is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU Lesser General Public License as published by
6 | // the Free Software Foundation, either version 3 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // The go-ethereum library is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU Lesser General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU Lesser General Public License
15 | // along with the go-ethereum library. If not, see .
16 |
17 | package build
18 |
19 | import (
20 | "bytes"
21 | "flag"
22 | "fmt"
23 | "io/ioutil"
24 | "log"
25 | "os"
26 | "os/exec"
27 | "path"
28 | "path/filepath"
29 | "runtime"
30 | "strings"
31 | )
32 |
33 | // DryRunFlag dry run flag
34 | var DryRunFlag = flag.Bool("n", false, "dry run, don't execute commands")
35 |
36 | // MustRun executes the given command and exits the host process for
37 | // any error.
38 | func MustRun(cmd *exec.Cmd) {
39 | fmt.Println(">>>", strings.Join(cmd.Args, " "))
40 | if !*DryRunFlag {
41 | cmd.Stderr = os.Stderr
42 | cmd.Stdout = os.Stdout
43 | if err := cmd.Run(); err != nil {
44 | log.Fatal(err)
45 | }
46 | }
47 | }
48 |
49 | // MustRunCommand wrap MustRun
50 | func MustRunCommand(cmd string, args ...string) {
51 | MustRun(exec.Command(cmd, args...))
52 | }
53 |
54 | var warnedAboutGit bool
55 |
56 | // RunGit runs a git subcommand and returns its output.
57 | // The command must complete successfully.
58 | func RunGit(args ...string) string {
59 | cmd := exec.Command("git", args...)
60 | var stdout, stderr bytes.Buffer
61 | cmd.Stdout, cmd.Stderr = &stdout, &stderr
62 | if err := cmd.Run(); err != nil {
63 | if e, ok := err.(*exec.Error); ok && e.Err == exec.ErrNotFound {
64 | if !warnedAboutGit {
65 | log.Println("Warning: can't find 'git' in PATH")
66 | warnedAboutGit = true
67 | }
68 | return ""
69 | }
70 | log.Fatal(strings.Join(cmd.Args, " "), ": ", err, "\n", stderr.String())
71 | }
72 | return strings.TrimSpace(stdout.String())
73 | }
74 |
75 | // readGitFile returns content of file in .git directory.
76 | func readGitFile(file string) string {
77 | content, err := ioutil.ReadFile(path.Join(".git", file))
78 | if err != nil {
79 | return ""
80 | }
81 | return strings.TrimSpace(string(content))
82 | }
83 |
84 | // GoTool returns the command that runs a go tool. This uses go from GOROOT instead of PATH
85 | // so that go commands executed by build use the same version of Go as the 'host' that runs
86 | // build code. e.g.
87 | //
88 | // /usr/lib/go-1.12.1/bin/go run build/ci.go ...
89 | //
90 | // runs using go 1.12.1 and invokes go 1.12.1 tools from the same GOROOT. This is also important
91 | // because runtime.Version checks on the host should match the tools that are run.
92 | func GoTool(tool string, args ...string) *exec.Cmd {
93 | args = append([]string{tool}, args...)
94 | return exec.Command(filepath.Join(runtime.GOROOT(), "bin", "go"), args...) //nolint:gosec // any better way?
95 | }
96 |
--------------------------------------------------------------------------------
/internal/swapapi/converts.go:
--------------------------------------------------------------------------------
1 | package swapapi
2 |
3 | import (
4 | "github.com/fsn-dev/crossChain-Bridge/mongodb"
5 | "github.com/fsn-dev/crossChain-Bridge/tokens"
6 | )
7 |
8 | // ConvertMgoSwapToSwapInfo convert
9 | func ConvertMgoSwapToSwapInfo(ms *mongodb.MgoSwap) *SwapInfo {
10 | return &SwapInfo{
11 | TxID: ms.TxID,
12 | Bind: ms.Bind,
13 | Status: ms.Status,
14 | Timestamp: ms.Timestamp,
15 | Memo: ms.Memo,
16 | }
17 | }
18 |
19 | // ConvertMgoSwapsToSwapInfos convert
20 | func ConvertMgoSwapsToSwapInfos(msSlice []*mongodb.MgoSwap) []*SwapInfo {
21 | result := make([]*SwapInfo, len(msSlice))
22 | for k, v := range msSlice {
23 | result[k] = ConvertMgoSwapToSwapInfo(v)
24 | }
25 | return result
26 | }
27 |
28 | // ConvertMgoSwapResultToSwapInfo convert
29 | func ConvertMgoSwapResultToSwapInfo(mr *mongodb.MgoSwapResult) *SwapInfo {
30 | var confirmations uint64
31 | if mr.SwapHeight != 0 {
32 | var latest uint64
33 | switch mr.SwapType {
34 | case uint32(tokens.SwapinType):
35 | latest = tokens.DstLatestBlockHeight
36 | case uint32(tokens.SwapoutType), uint32(tokens.SwapRecallType):
37 | latest = tokens.SrcLatestBlockHeight
38 | }
39 | if latest > mr.SwapHeight {
40 | confirmations = latest - mr.SwapHeight
41 | }
42 | }
43 | return &SwapInfo{
44 | TxID: mr.TxID,
45 | TxHeight: mr.TxHeight,
46 | TxTime: mr.TxTime,
47 | From: mr.From,
48 | To: mr.To,
49 | Bind: mr.Bind,
50 | Value: mr.Value,
51 | SwapTx: mr.SwapTx,
52 | SwapHeight: mr.SwapHeight,
53 | SwapTime: mr.SwapTime,
54 | SwapValue: mr.SwapValue,
55 | SwapType: mr.SwapType,
56 | Status: mr.Status,
57 | Timestamp: mr.Timestamp,
58 | Memo: mr.Memo,
59 | Confirmations: confirmations,
60 | }
61 | }
62 |
63 | // ConvertMgoSwapResultsToSwapInfos convert
64 | func ConvertMgoSwapResultsToSwapInfos(mrSlice []*mongodb.MgoSwapResult) []*SwapInfo {
65 | result := make([]*SwapInfo, len(mrSlice))
66 | for k, v := range mrSlice {
67 | result[k] = ConvertMgoSwapResultToSwapInfo(v)
68 | }
69 | return result
70 | }
71 |
--------------------------------------------------------------------------------
/internal/swapapi/types.go:
--------------------------------------------------------------------------------
1 | package swapapi
2 |
3 | import (
4 | "github.com/fsn-dev/crossChain-Bridge/mongodb"
5 | "github.com/fsn-dev/crossChain-Bridge/tokens"
6 | )
7 |
8 | // SwapStatus type alias
9 | type SwapStatus = mongodb.SwapStatus
10 |
11 | // Swap type alias
12 | type Swap = mongodb.MgoSwap
13 |
14 | // SwapResult type alias
15 | type SwapResult = mongodb.MgoSwapResult
16 |
17 | // SwapStatistics type alias
18 | type SwapStatistics = mongodb.SwapStatistics
19 |
20 | // LatestScanInfo type alias
21 | type LatestScanInfo = mongodb.MgoLatestScanInfo
22 |
23 | // ServerInfo server info
24 | type ServerInfo struct {
25 | Identifier string
26 | SrcToken *tokens.TokenConfig
27 | DestToken *tokens.TokenConfig
28 | Version string
29 | }
30 |
31 | // PostResult post result
32 | type PostResult string
33 |
34 | // SuccessPostResult success post result
35 | var SuccessPostResult PostResult = "Success"
36 |
37 | // SwapInfo swap info
38 | type SwapInfo struct {
39 | TxID string `json:"txid"`
40 | TxHeight uint64 `json:"txheight"`
41 | TxTime uint64 `json:"txtime"`
42 | From string `json:"from"`
43 | To string `json:"to"`
44 | Bind string `json:"bind"`
45 | Value string `json:"value"`
46 | SwapTx string `json:"swaptx"`
47 | SwapHeight uint64 `json:"swapheight"`
48 | SwapTime uint64 `json:"swaptime"`
49 | SwapValue string `json:"swapvalue"`
50 | SwapType uint32 `json:"swaptype"`
51 | Status SwapStatus `json:"status"`
52 | Timestamp int64 `json:"timestamp"`
53 | Memo string `json:"memo"`
54 | Confirmations uint64 `json:"confirmations"`
55 | }
56 |
--------------------------------------------------------------------------------
/log/logger_test.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | "time"
7 |
8 | "github.com/stretchr/testify/assert"
9 | )
10 |
11 | var (
12 | now = time.Now().Unix()
13 | err = fmt.Errorf("error message")
14 | )
15 |
16 | // Fatal Fatalf Fatalln is not test
17 | func TestLogger(t *testing.T) {
18 | SetLogger(6, false, true)
19 |
20 | WithFields("timestamp", now, "err", err).Tracef("test WithFields Tracef at %v", now)
21 | WithFields("timestamp", now, "err", err).Debugf("test WithFields Debugf at %v", now)
22 | WithFields("timestamp", now, "err", err).Infof("test WithFields Infof at %v", now)
23 | WithFields("timestamp", now, "err", err).Printf("test WithFields Printf at %v", now)
24 | WithFields("timestamp", now, "err", err).Warnf("test WithFields Warnf at %v", now)
25 | WithFields("timestamp", now, "err", err).Errorf("test WithFields Errorf at %v", now)
26 | assert.Panics(t, func() { WithFields("timestamp", now, "err", err).Panicf("test WithFields Panicf at %v", now) }, "not panic")
27 |
28 | Trace("test Trace", "timestamp", now, "err", err)
29 | Tracef("test Tracef, timestamp=%v err=%v", now, err)
30 | Traceln("test Traceln", "timestamp", now, "err", err)
31 |
32 | Debug("test Debug", "timestamp", now, "err", err)
33 | Debugf("test Debugf, timestamp=%v err=%v", now, err)
34 | Debugln("test Debugln", "timestamp", now, "err", err)
35 |
36 | Info("test Info", "timestamp", now, "err", err)
37 | Infof("test Infof, timestamp=%v err=%v", now, err)
38 | Infoln("test Infoln", "timestamp", now, "err", err)
39 |
40 | Print("test Print ", "timestamp", now, " err ", err)
41 | Printf("test Printf, timestamp=%v err=%v", now, err)
42 | Println("test Println", "timestamp", now, "err", err)
43 |
44 | Warn("test Warn", "timestamp", now, "err", err)
45 | Warnf("test Warnf, timestamp=%v err=%v", now, err)
46 | Warnln("test Warnln", "timestamp", now, "err", err)
47 |
48 | Error("test Error", "timestamp", now, "err", err)
49 | Errorf("test Errorf, timestamp=%v err=%v", now, err)
50 | Errorln("test Errorln", "timestamp", now, "err", err)
51 |
52 | assert.Panics(t, func() { Panic("test Panic", "timestamp", now, "err", err) }, "not panic")
53 | assert.Panics(t, func() { Panicf("test Panicf, timestamp=%v err=%v", now, err) }, "not panic")
54 | assert.Panics(t, func() { Panicln("test Panicln", "timestamp", now, "err", err) }, "not panic")
55 | }
56 |
--------------------------------------------------------------------------------
/mongodb/dbinit.go:
--------------------------------------------------------------------------------
1 | package mongodb
2 |
3 | import (
4 | "fmt"
5 | "time"
6 |
7 | "github.com/fsn-dev/crossChain-Bridge/log"
8 | "gopkg.in/mgo.v2"
9 | )
10 |
11 | var (
12 | database *mgo.Database
13 | session *mgo.Session
14 |
15 | mongoURL string
16 | dbName string
17 | )
18 |
19 | // MongoServerInit int mongodb server session
20 | func MongoServerInit(mongourl, dbname string) {
21 | initMongodb(mongourl, dbname)
22 | mongoConnect()
23 | InitCollections()
24 | go checkMongoSession()
25 | }
26 |
27 | func initMongodb(url, db string) {
28 | mongoURL = url
29 | dbName = db
30 | }
31 |
32 | func mongoReconnect() {
33 | log.Info("[mongodb] reconnect database", "dbName", dbName)
34 | mongoConnect()
35 | go checkMongoSession()
36 | }
37 |
38 | func mongoConnect() {
39 | if session != nil { // when reconnect
40 | session.Close()
41 | }
42 | log.Info("[mongodb] connect database start.", "dbName", dbName)
43 | url := fmt.Sprintf("mongodb://%v/%v", mongoURL, dbName)
44 | var err error
45 | for {
46 | session, err = mgo.Dial(url)
47 | if err == nil {
48 | break
49 | }
50 | log.Printf("[mongodb] dial error, err=%v\n", err)
51 | time.Sleep(1 * time.Second)
52 | }
53 | session.SetMode(mgo.Monotonic, true)
54 | session.SetSafe(&mgo.Safe{FSync: true})
55 | database = session.DB(dbName)
56 | deinintCollections()
57 | log.Info("[mongodb] connect database finished.", "dbName", dbName)
58 | }
59 |
60 | // fix 'read tcp 127.0.0.1:43502->127.0.0.1:27917: i/o timeout'
61 | func checkMongoSession() {
62 | for {
63 | time.Sleep(60 * time.Second)
64 | ensureMongoConnected()
65 | }
66 | }
67 |
68 | func ensureMongoConnected() {
69 | defer func() {
70 | if r := recover(); r != nil {
71 | mongoReconnect()
72 | }
73 | }()
74 | err := session.Ping()
75 | if err != nil {
76 | log.Info("[mongodb] refresh session.", "dbName", dbName)
77 | session.Refresh()
78 | err = session.Ping()
79 | if err == nil {
80 | database = session.DB(dbName)
81 | deinintCollections()
82 | } else {
83 | mongoReconnect()
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/mongodb/errors.go:
--------------------------------------------------------------------------------
1 | package mongodb
2 |
3 | import (
4 | rpcjson "github.com/gorilla/rpc/v2/json2"
5 | "gopkg.in/mgo.v2"
6 | )
7 |
8 | func newError(ec rpcjson.ErrorCode, message string) error {
9 | return &rpcjson.Error{
10 | Code: ec,
11 | Message: message,
12 | }
13 | }
14 |
15 | func mgoError(err error) error {
16 | if err != nil {
17 | if err == mgo.ErrNotFound {
18 | return ErrItemNotFound
19 | }
20 | if mgo.IsDup(err) {
21 | return ErrItemIsDup
22 | }
23 | return newError(-32001, "mgoError: "+err.Error())
24 | }
25 | return nil
26 | }
27 |
28 | // mongodb special errors
29 | var (
30 | ErrItemNotFound = newError(-32002, "mgoError: Item not found")
31 | ErrItemIsDup = newError(-32003, "mgoError: Item is duplicate")
32 | ErrSwapNotFound = newError(-32011, "mgoError: Swap is not found")
33 | ErrSwapinTxNotStable = newError(-32012, "mgoError: Swap in tx is not stable")
34 | ErrSwapinRecallExist = newError(-32013, "mgoError: Swap in recall is exist")
35 | ErrSwapinRecalledOrForbidden = newError(-32014, "mgoError: Swap in is already recalled or can not recall")
36 | )
37 |
--------------------------------------------------------------------------------
/mongodb/status.go:
--------------------------------------------------------------------------------
1 | package mongodb
2 |
3 | // -----------------------------------------------
4 | // swap status change graph
5 | //
6 | // TxNotStable -> |- TxVerifyFailed -> stop
7 | // |- TxCanRecall -> TxToBeRecall -> |- TxRecallFailed -> retry
8 | // |- TxProcessed (->MatchTxNotStable)
9 | // |- TxNotSwapped -> |- TxSwapFailed -> retry
10 | // |- TxProcessed (->MatchTxNotStable)
11 | // -----------------------------------------------
12 | // swap result status change graph
13 | //
14 | // TxWithWrongMemo -> |
15 | // MatchTxEmpty -> | MatchTxNotStable -> MatchTxStable
16 | // -----------------------------------------------
17 |
18 | // SwapStatus swap status
19 | type SwapStatus uint16
20 |
21 | // swap status values
22 | const (
23 | TxNotStable SwapStatus = iota // 0
24 | TxVerifyFailed // 1
25 | TxCanRecall // 2
26 | TxToBeRecall // 3
27 | TxRecallFailed // 4
28 | TxNotSwapped // 5
29 | TxSwapFailed // 6
30 | TxProcessed // 7
31 | MatchTxEmpty // 8
32 | MatchTxNotStable // 9
33 | MatchTxStable // 10
34 | TxWithWrongMemo // 11
35 | )
36 |
37 | func (status SwapStatus) String() string {
38 | switch status {
39 | case TxNotStable:
40 | return "TxNotStable"
41 | case TxVerifyFailed:
42 | return "TxVerifyFailed"
43 | case TxCanRecall:
44 | return "TxCanRecall"
45 | case TxToBeRecall:
46 | return "TxToBeRecall"
47 | case TxRecallFailed:
48 | return "TxRecallFailed"
49 | case TxNotSwapped:
50 | return "TxNotSwapped"
51 | case TxSwapFailed:
52 | return "TxSwapFailed"
53 | case TxProcessed:
54 | return "TxProcessed"
55 | case MatchTxEmpty:
56 | return "MatchTxEmpty"
57 | case MatchTxNotStable:
58 | return "MatchTxNotStable"
59 | case MatchTxStable:
60 | return "MatchTxStable"
61 | case TxWithWrongMemo:
62 | return "TxWithWrongMemo"
63 | default:
64 | panic("unknown swap status")
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/mongodb/tables.go:
--------------------------------------------------------------------------------
1 | package mongodb
2 |
3 | const (
4 | tbSwapins string = "Swapins"
5 | tbSwapouts string = "Swapouts"
6 | tbSwapinResults string = "SwapinResults"
7 | tbSwapoutResults string = "SwapoutResults"
8 | tbP2shAddresses string = "P2shAddresses"
9 | tbSwapStatistics string = "SwapStatistics"
10 | tbLatestScanInfo string = "LatestScanInfo"
11 |
12 | keyOfSwapStatistics string = "latest"
13 | keyOfSrcLatestScanInfo string = "srclatest"
14 | keyOfDstLatestScanInfo string = "dstlatest"
15 | )
16 |
17 | // MgoSwap registered swap
18 | type MgoSwap struct {
19 | Key string `bson:"_id"`
20 | TxID string `bson:"txid"`
21 | TxType uint32 `bson:"txtype"`
22 | Bind string `bson:"bind"`
23 | Status SwapStatus `bson:"status"`
24 | Timestamp int64 `bson:"timestamp"`
25 | Memo string `bson:"memo"`
26 | }
27 |
28 | // MgoSwapResult swap result (verified swap)
29 | type MgoSwapResult struct {
30 | Key string `bson:"_id"`
31 | TxID string `bson:"txid"`
32 | TxHeight uint64 `bson:"txheight"`
33 | TxTime uint64 `bson:"txtime"`
34 | From string `bson:"from"`
35 | To string `bson:"to"`
36 | Bind string `bson:"bind"`
37 | Value string `bson:"value"`
38 | SwapTx string `bson:"swaptx"`
39 | SwapHeight uint64 `bson:"swapheight"`
40 | SwapTime uint64 `bson:"swaptime"`
41 | SwapValue string `bson:"swapvalue"`
42 | SwapType uint32 `bson:"swaptype"`
43 | Status SwapStatus `bson:"status"`
44 | Timestamp int64 `bson:"timestamp"`
45 | Memo string `bson:"memo"`
46 | }
47 |
48 | // SwapResultUpdateItems swap update items
49 | type SwapResultUpdateItems struct {
50 | SwapTx string
51 | SwapHeight uint64
52 | SwapTime uint64
53 | SwapValue string
54 | SwapType uint32
55 | Status SwapStatus
56 | Timestamp int64
57 | Memo string
58 | }
59 |
60 | // MgoP2shAddress key is the bind address
61 | type MgoP2shAddress struct {
62 | Key string `bson:"_id"`
63 | P2shAddress string `bson:"p2shaddress"`
64 | }
65 |
66 | // MgoSwapStatistics swap statistics
67 | type MgoSwapStatistics struct {
68 | Key string `bson:"_id"`
69 | StableSwapinCount int `bson:"swapincount"`
70 | TotalSwapinValue string `bson:"totalswapinvalue"`
71 | TotalSwapinFee string `bson:"totalswapinfee"`
72 | StableSwapoutCount int `bson:"swapoutcount"`
73 | TotalSwapoutValue string `bson:"totalswapoutvalue"`
74 | TotalSwapoutFee string `bson:"totalswapoutfee"`
75 | }
76 |
77 | // MgoLatestScanInfo latest scan info
78 | type MgoLatestScanInfo struct {
79 | Key string `bson:"_id"`
80 | BlockHeight uint64 `bson:"blockheight"`
81 | Timestamp int64 `bson:"timestamp"`
82 | }
83 |
--------------------------------------------------------------------------------
/params/config.toml:
--------------------------------------------------------------------------------
1 | # a short string to identify the bridge
2 | Identifier = "BTC2ETH"
3 |
4 | # modgodb database connection config (server only)
5 | [MongoDB]
6 | DBURL = "localhost:27017"
7 | DBName = "databasename"
8 | UserName = "username"
9 | Password = "password"
10 |
11 | # bridge API service (server only)
12 | [APIServer]
13 | Port = 11556
14 | AllowedOrigins = []
15 |
16 | # oracle config (oracle only)
17 | [Oracle]
18 | # post swap register RPC requests to this server
19 | ServerAPIAddress = "http://127.0.0.1:11556/rpc"
20 |
21 | # customize fees in building btc transaction (server only)
22 | [BtcExtra]
23 | MinRelayFee = 400
24 | RelayFeePerKb = 2000
25 | UtxoAggregateMinCount = 10
26 | UtxoAggregateMinValue = 100000
27 |
28 | # source token config
29 | [SrcToken]
30 | BlockChain = "Bitcoin"
31 | NetID = "TestNet3"
32 | ID = "BTC"
33 | Name = "Bitcoin Coin"
34 | Symbol = "BTC"
35 | Decimals = 8
36 | Description = "Bitcoin Coin"
37 | ContractAddress = ""
38 | DcrmAddress = "mfwPnCuht2b4Lvb5XTds4Rvzy3jZ2ZWrBL"
39 | Confirmations = 0 # suggest >= 6 for Mainnet
40 | MaximumSwap = 1000.0
41 | MinimumSwap = 0.00001
42 | SwapFeeRate = 0.001
43 | InitialHeight = 0
44 |
45 | # source blockchain gateway config
46 | [SrcGateway]
47 | APIAddress = "http://47.107.50.83:3002"
48 |
49 | # dest token config
50 | [DestToken]
51 | BlockChain = "Ethereum"
52 | NetID = "Rinkeby"
53 | ID = "mBTC"
54 | Name = "SMPC Bitcoin"
55 | Symbol = "mBTC"
56 | Decimals = 8
57 | Description = "cross chain bridge BTC with mBTC"
58 | ContractAddress = "0x61b8c4d6d28d5f7edadbea5456db3b4f7f836b64"
59 | DcrmAddress = "0xbF0A46d3700E23a98F38079cE217742c92Bb66bC"
60 | Confirmations = 0 # suggest >= 33 for Mainnet
61 | MaximumSwap = 100.0
62 | MinimumSwap = 0.00001
63 | SwapFeeRate = 0.001
64 | InitialHeight = 0
65 |
66 | # dest blockchain gateway config
67 | [DestGateway]
68 | APIAddress = "http://5.189.139.168:8018"
69 |
70 | # DCRM config
71 | [Dcrm]
72 | # server dcrm user (initiator of dcrm sign)
73 | ServerAccount = "0x00c37841378920E2BA5151a5d1E074Cf367586c4"
74 |
75 | # dcrm group ID
76 | GroupID = "74245ef03937fa75b979bdaa6a5952a93f53e021e0832fca4c2ad8952572c9b70f49e291de7e024b0f7fc54ec5875210db2ac775dba44448b3972b75af074d17"
77 |
78 | # dcrm account public key
79 | Pubkey = "045c8648793e4867af465691685000ae841dccab0b011283139d2eae454b569d5789f01632e13a75a5aad8480140e895dd671cae3639f935750bea7ae4b5a2512e"
80 |
81 | # dcrm sub groups for signing (server only)
82 | SignGroups = [
83 | "38a93f457c793ac3ee242b2c050a403774738e6558cfaa620fe5577bb15a28f63c39adcc0778497e5009a9ee776a0778ffcad4e95827e69efa21b893b8a78793",
84 | "bb1dfe1ec046cc3a3b88408ae03976aabffe459b40e5def09e76f5d4c7a917133241da9da7fc05e3e172fab54ce3129a9a492d52a5a09494d0b9c1e608f661bf"
85 | ]
86 |
87 | # dcrm threshold (NeededOracles=2,TotalOracles=3 represent '2/3' threshold)
88 | NeededOracles = 2
89 | TotalOracles = 3
90 |
91 | # dcrm mode (0:managed 1:private)
92 | Mode = 0
93 |
94 | # dcrm user keystore and password file (suggest using absolute path)
95 | KeystoreFile = "/home/xxx/accounts/keystore1"
96 | PasswordFile = "/home/xxx/accounts/password1"
97 |
98 | # dcrm backend node (gdcrm node RPC address)
99 | RPCAddress = "http://127.0.0.1:2922"
100 |
--------------------------------------------------------------------------------
/params/version.go:
--------------------------------------------------------------------------------
1 | package params
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | // version parts
8 | const (
9 | VersionMajor = 0 // Major version component of the current release
10 | VersionMinor = 2 // Minor version component of the current release
11 | VersionPatch = 11 // Patch version component of the current release
12 | VersionMeta = "alpha" // Version metadata to append to the version string
13 | )
14 |
15 | const (
16 | versionStable = "stable"
17 | )
18 |
19 | // Version holds the textual version string.
20 | var Version = func() string {
21 | return fmt.Sprintf("%d.%d.%d", VersionMajor, VersionMinor, VersionPatch)
22 | }()
23 |
24 | // VersionWithMeta holds the textual version string including the metadata.
25 | var VersionWithMeta = func() string {
26 | v := Version
27 | if VersionMeta != "" {
28 | v += "-" + VersionMeta
29 | }
30 | return v
31 | }()
32 |
33 | // ArchiveVersion holds the textual version string used for Geth archives.
34 | // e.g. "1.8.11-dea1ce05" for stable releases, or
35 | // "1.8.13-unstable-21c059b6" for unstable releases
36 | func ArchiveVersion(gitCommit string) string {
37 | vsn := Version
38 | if VersionMeta != versionStable {
39 | vsn += "-" + VersionMeta
40 | }
41 | if len(gitCommit) >= 8 {
42 | vsn += "-" + gitCommit[:8]
43 | }
44 | return vsn
45 | }
46 |
47 | // VersionWithCommit add git commit and data to version.
48 | func VersionWithCommit(gitCommit, gitDate string) string {
49 | vsn := VersionWithMeta
50 | if len(gitCommit) >= 8 {
51 | vsn += "-" + gitCommit[:8]
52 | }
53 | if (VersionMeta != versionStable) && (gitDate != "") {
54 | vsn += "-" + gitDate
55 | }
56 | return vsn
57 | }
58 |
--------------------------------------------------------------------------------
/rpc/client/client.go:
--------------------------------------------------------------------------------
1 | package client
2 |
3 | import (
4 | "bytes"
5 | "encoding/json"
6 | "io"
7 | "io/ioutil"
8 | "net"
9 | "net/http"
10 | "strings"
11 | "time"
12 | )
13 |
14 | var (
15 | httpClient *http.Client
16 | )
17 |
18 | // InitHTTPClient init http client
19 | func InitHTTPClient() {
20 | httpClient = createHTTPClient()
21 | }
22 |
23 | const (
24 | maxIdleConns int = 100
25 | maxIdleConnsPerHost int = 10
26 | maxConnsPerHost int = 50
27 | idleConnTimeout int = 90
28 | )
29 |
30 | // createHTTPClient for connection re-use
31 | func createHTTPClient() *http.Client {
32 | return &http.Client{
33 | Transport: &http.Transport{
34 | Proxy: http.ProxyFromEnvironment,
35 | DialContext: (&net.Dialer{
36 | Timeout: 30 * time.Second,
37 | KeepAlive: 30 * time.Second,
38 | }).DialContext,
39 | MaxConnsPerHost: maxConnsPerHost,
40 | MaxIdleConns: maxIdleConns,
41 | MaxIdleConnsPerHost: maxIdleConnsPerHost,
42 | IdleConnTimeout: time.Duration(idleConnTimeout) * time.Second,
43 | },
44 | Timeout: defaultTimeout * time.Second,
45 | }
46 | }
47 |
48 | // HTTPGet http get
49 | func HTTPGet(url string, params, headers map[string]string, timeout int) (*http.Response, error) {
50 | req, err := http.NewRequest(http.MethodGet, url, nil)
51 | if err != nil {
52 | return nil, err
53 | }
54 |
55 | addParams(req, params)
56 | addHeaders(req, headers)
57 |
58 | return doRequest(req, timeout)
59 | }
60 |
61 | // HTTPPost http post
62 | func HTTPPost(url string, body interface{}, params, headers map[string]string, timeout int) (*http.Response, error) {
63 | req, err := http.NewRequest(http.MethodPost, url, nil)
64 | if err != nil {
65 | return nil, err
66 | }
67 |
68 | addParams(req, params)
69 | addHeaders(req, headers)
70 | if err := addPostBody(req, body); err != nil {
71 | return nil, err
72 | }
73 |
74 | return doRequest(req, timeout)
75 | }
76 |
77 | // HTTPRawPost http raw post
78 | func HTTPRawPost(url, body string, params, headers map[string]string, timeout int) (*http.Response, error) {
79 | req, err := http.NewRequest(http.MethodPost, url, nil)
80 | if err != nil {
81 | return nil, err
82 | }
83 |
84 | addParams(req, params)
85 | addHeaders(req, headers)
86 | if err := addRawPostBody(req, body); err != nil {
87 | return nil, err
88 | }
89 |
90 | return doRequest(req, timeout)
91 | }
92 |
93 | func addParams(req *http.Request, params map[string]string) {
94 | if params != nil {
95 | q := req.URL.Query()
96 | for key, val := range params {
97 | q.Add(key, val)
98 | }
99 | req.URL.RawQuery = q.Encode()
100 | }
101 | }
102 |
103 | func addHeaders(req *http.Request, headers map[string]string) {
104 | for key, val := range headers {
105 | req.Header.Add(key, val)
106 | }
107 | }
108 |
109 | func addPostBody(req *http.Request, body interface{}) error {
110 | if body != nil {
111 | jsonData, err := json.Marshal(body)
112 | if err != nil {
113 | return err
114 | }
115 | req.Header.Set("Content-type", "application/json")
116 | req.GetBody = func() (io.ReadCloser, error) {
117 | return ioutil.NopCloser(bytes.NewBuffer(jsonData)), nil
118 | }
119 | req.Body, _ = req.GetBody()
120 | }
121 | return nil
122 | }
123 |
124 | func addRawPostBody(req *http.Request, body string) (err error) {
125 | if body == "" {
126 | return nil
127 | }
128 | req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
129 | req.GetBody = func() (io.ReadCloser, error) {
130 | return ioutil.NopCloser(strings.NewReader(body)), nil
131 | }
132 | req.Body, err = req.GetBody()
133 | return err
134 | }
135 |
136 | func doRequest(req *http.Request, timeoutSeconds int) (*http.Response, error) {
137 | timeout := time.Duration(timeoutSeconds) * time.Second
138 | if httpClient == nil {
139 | client := http.Client{
140 | Timeout: timeout,
141 | }
142 | return client.Do(req)
143 | }
144 | httpClient.Timeout = timeout
145 | return httpClient.Do(req)
146 | }
147 |
--------------------------------------------------------------------------------
/rpc/client/get.go:
--------------------------------------------------------------------------------
1 | package client
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io"
7 | "io/ioutil"
8 | )
9 |
10 | // RPCGet rpc get
11 | func RPCGet(result interface{}, url string) error {
12 | return RPCGetRequest(result, url, nil, nil, defaultTimeout)
13 | }
14 |
15 | // RPCGetWithTimeout rpc get with timeout
16 | func RPCGetWithTimeout(result interface{}, url string, timeout int) error {
17 | return RPCGetRequest(result, url, nil, nil, timeout)
18 | }
19 |
20 | // RPCGetRequest rpc get request
21 | func RPCGetRequest(result interface{}, url string, params, headers map[string]string, timeout int) error {
22 | resp, err := HTTPGet(url, params, headers, timeout)
23 | if err != nil {
24 | return fmt.Errorf("GET request error: %v (url: %v, params: %v)", err, url, params)
25 | }
26 |
27 | if resp.StatusCode != 200 {
28 | return fmt.Errorf("error response status: %v (url: %v)", resp.StatusCode, url)
29 | }
30 |
31 | defer resp.Body.Close()
32 | const maxReadContentLength int64 = 1024 * 1024 * 10 // 10M
33 | body, err := ioutil.ReadAll(io.LimitReader(resp.Body, maxReadContentLength))
34 | if err != nil {
35 | return fmt.Errorf("read body error: %v", err)
36 | }
37 |
38 | err = json.Unmarshal(body, &result)
39 | if err != nil {
40 | return fmt.Errorf("unmarshal result error: %v", err)
41 | }
42 | return nil
43 | }
44 |
45 | // RPCRawGet rpc raw get
46 | func RPCRawGet(url string) (string, error) {
47 | return RPCRawGetRequest(url, nil, nil, defaultTimeout)
48 | }
49 |
50 | // RPCRawGetWithTimeout rpc raw get with timeout
51 | func RPCRawGetWithTimeout(url string, timeout int) (string, error) {
52 | return RPCRawGetRequest(url, nil, nil, timeout)
53 | }
54 |
55 | // RPCRawGetRequest rpc raw get request
56 | func RPCRawGetRequest(url string, params, headers map[string]string, timeout int) (string, error) {
57 | resp, err := HTTPGet(url, params, headers, timeout)
58 | if err != nil {
59 | return "", fmt.Errorf("GET request error: %v (url: %v, params: %v)", err, url, params)
60 | }
61 |
62 | defer resp.Body.Close()
63 | const maxReadContentLength int64 = 1024 * 1024 * 10 // 10M
64 | body, err := ioutil.ReadAll(io.LimitReader(resp.Body, maxReadContentLength))
65 | if err != nil {
66 | return "", fmt.Errorf("read body error: %v", err)
67 | }
68 |
69 | if resp.StatusCode != 200 {
70 | return "", fmt.Errorf("wrong response status %v. message: %v", resp.StatusCode, string(body))
71 | }
72 | return string(body), nil
73 | }
74 |
--------------------------------------------------------------------------------
/rpc/client/post.go:
--------------------------------------------------------------------------------
1 | package client
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io"
7 | "io/ioutil"
8 | "net/http"
9 | )
10 |
11 | const (
12 | defaultTimeout = 60 // seconds
13 | defaultRequestID = 1
14 | )
15 |
16 | // Request json rpc request
17 | type Request struct {
18 | Method string
19 | Params interface{}
20 | Timeout int
21 | ID int
22 | }
23 |
24 | // NewRequest new request
25 | func NewRequest(method string, params ...interface{}) *Request {
26 | return &Request{
27 | Method: method,
28 | Params: params,
29 | Timeout: defaultTimeout,
30 | ID: defaultRequestID,
31 | }
32 | }
33 |
34 | // NewRequestWithTimeoutAndID new request with timeout and id
35 | func NewRequestWithTimeoutAndID(timeout, id int, method string, params ...interface{}) *Request {
36 | return &Request{
37 | Method: method,
38 | Params: params,
39 | Timeout: timeout,
40 | ID: id,
41 | }
42 | }
43 |
44 | // RPCPost rpc post
45 | func RPCPost(result interface{}, url, method string, params ...interface{}) error {
46 | req := NewRequest(method, params...)
47 | return RPCPostRequest(url, req, result)
48 | }
49 |
50 | // RPCPostWithTimeoutAndID rpc post with timeout and id
51 | func RPCPostWithTimeoutAndID(result interface{}, timeout, id int, url, method string, params ...interface{}) error {
52 | req := NewRequestWithTimeoutAndID(timeout, id, method, params...)
53 | return RPCPostRequest(url, req, result)
54 | }
55 |
56 | // RequestBody request body
57 | type RequestBody struct {
58 | Version string `json:"jsonrpc"`
59 | Method string `json:"method"`
60 | Params interface{} `json:"params"`
61 | ID int `json:"id"`
62 | }
63 |
64 | type jsonError struct {
65 | Code int `json:"code"`
66 | Message string `json:"message"`
67 | Data interface{} `json:"data,omitempty"`
68 | }
69 |
70 | func (err *jsonError) Error() string {
71 | return fmt.Sprintf("json-rpc error %d, %s", err.Code, err.Message)
72 | }
73 |
74 | type jsonrpcResponse struct {
75 | Version string `json:"jsonrpc,omitempty"`
76 | ID json.RawMessage `json:"id,omitempty"`
77 | Error *jsonError `json:"error,omitempty"`
78 | Result json.RawMessage `json:"result,omitempty"`
79 | }
80 |
81 | // RPCPostRequest rpc post request
82 | func RPCPostRequest(url string, req *Request, result interface{}) error {
83 | reqBody := &RequestBody{
84 | Version: "2.0",
85 | Method: req.Method,
86 | Params: req.Params,
87 | ID: req.ID,
88 | }
89 | resp, err := HTTPPost(url, reqBody, nil, nil, req.Timeout)
90 | if err != nil {
91 | return err
92 | }
93 | return getResultFromJSONResponse(result, resp)
94 | }
95 |
96 | func getResultFromJSONResponse(result interface{}, resp *http.Response) error {
97 | defer resp.Body.Close()
98 | const maxReadContentLength int64 = 1024 * 1024 * 10 // 10M
99 | body, err := ioutil.ReadAll(io.LimitReader(resp.Body, maxReadContentLength))
100 | if err != nil {
101 | return fmt.Errorf("read body error: %v", err)
102 | }
103 | if resp.StatusCode != 200 {
104 | return fmt.Errorf("wrong response status %v. message: %v", resp.StatusCode, string(body))
105 | }
106 | if len(body) == 0 {
107 | return fmt.Errorf("empty response body")
108 | }
109 |
110 | var jsonResp jsonrpcResponse
111 | err = json.Unmarshal(body, &jsonResp)
112 | if err != nil {
113 | return fmt.Errorf("unmarshal body error, body is \"%v\" err=\"%v\"", string(body), err)
114 | }
115 | if jsonResp.Error != nil {
116 | return fmt.Errorf("return error: %v", jsonResp.Error.Error())
117 | }
118 | err = json.Unmarshal(jsonResp.Result, &result)
119 | if err != nil {
120 | return fmt.Errorf("unmarshal result error: %v", err)
121 | }
122 | return nil
123 | }
124 |
125 | // RPCRawPost rpc raw post
126 | func RPCRawPost(url, body string) (string, error) {
127 | return RPCRawPostWithTimeout(url, body, defaultTimeout)
128 | }
129 |
130 | // RPCRawPostWithTimeout rpc raw post with timeout
131 | func RPCRawPostWithTimeout(url, reqBody string, timeout int) (string, error) {
132 | resp, err := HTTPRawPost(url, reqBody, nil, nil, timeout)
133 | if err != nil {
134 | return "", err
135 | }
136 | defer resp.Body.Close()
137 | const maxReadContentLength int64 = 1024 * 1024 * 10 // 10M
138 | body, err := ioutil.ReadAll(io.LimitReader(resp.Body, maxReadContentLength))
139 | if err != nil {
140 | return "", fmt.Errorf("read body error: %v", err)
141 | }
142 | if resp.StatusCode != 200 {
143 | return "", fmt.Errorf("wrong response status %v. message: %v", resp.StatusCode, string(body))
144 | }
145 | return string(body), nil
146 | }
147 |
--------------------------------------------------------------------------------
/rpc/rpcapi/buildtx.go:
--------------------------------------------------------------------------------
1 | package rpcapi
2 |
3 | import (
4 | "net/http"
5 |
6 | "github.com/fsn-dev/crossChain-Bridge/common"
7 | "github.com/fsn-dev/crossChain-Bridge/common/hexutil"
8 | "github.com/fsn-dev/crossChain-Bridge/tokens"
9 | "github.com/fsn-dev/crossChain-Bridge/tokens/eth"
10 | "github.com/fsn-dev/crossChain-Bridge/types"
11 | )
12 |
13 | // BuildSwapoutTxArgs build swapout tx args
14 | type BuildSwapoutTxArgs struct {
15 | From common.Address `json:"from"`
16 | Value *hexutil.Big `json:"value"`
17 | Bind string `json:"bind"`
18 | Gas *hexutil.Uint64 `json:"gas"`
19 | GasPrice *hexutil.Big `json:"gasPrice"`
20 | Nonce *hexutil.Uint64 `json:"nonce"`
21 | }
22 |
23 | // BuildSwapoutTx build swapout tx
24 | func (s *RPCAPI) BuildSwapoutTx(r *http.Request, args *BuildSwapoutTxArgs, result *types.Transaction) error {
25 | from := args.From.String()
26 | token, gateway := tokens.DstBridge.GetTokenAndGateway()
27 | contract := token.ContractAddress
28 | extraArgs := &tokens.EthExtraArgs{
29 | Gas: (*uint64)(args.Gas),
30 | GasPrice: args.GasPrice.ToInt(),
31 | Nonce: (*uint64)(args.Nonce),
32 | }
33 | swapoutVal := args.Value.ToInt()
34 | bindAddr := args.Bind
35 |
36 | ethBridge := eth.NewCrossChainBridge(false)
37 | ethBridge.TokenConfig = token
38 | ethBridge.GatewayConfig = gateway
39 | tx, err := ethBridge.BuildSwapoutTx(from, contract, extraArgs, swapoutVal, bindAddr)
40 | if err != nil {
41 | return err
42 | }
43 | *result = *tx
44 | return nil
45 | }
46 |
--------------------------------------------------------------------------------
/rpc/server/server.go:
--------------------------------------------------------------------------------
1 | package server
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 | "time"
7 |
8 | "github.com/gorilla/handlers"
9 | "github.com/gorilla/mux"
10 | "github.com/gorilla/rpc/v2"
11 | rpcjson "github.com/gorilla/rpc/v2/json2"
12 |
13 | "github.com/fsn-dev/crossChain-Bridge/log"
14 | "github.com/fsn-dev/crossChain-Bridge/params"
15 | "github.com/fsn-dev/crossChain-Bridge/rpc/restapi"
16 | "github.com/fsn-dev/crossChain-Bridge/rpc/rpcapi"
17 | )
18 |
19 | // StartAPIServer start api server
20 | func StartAPIServer() {
21 | router := initRouter()
22 |
23 | apiPort := params.GetAPIPort()
24 | apiServer := params.GetConfig().APIServer
25 | allowedOrigins := apiServer.AllowedOrigins
26 |
27 | corsOptions := []handlers.CORSOption{
28 | handlers.AllowedMethods([]string{"GET", "POST"}),
29 | }
30 | if len(allowedOrigins) != 0 {
31 | corsOptions = append(corsOptions,
32 | handlers.AllowedHeaders([]string{"X-Requested-With", "Content-Type"}),
33 | handlers.AllowedOrigins(allowedOrigins),
34 | )
35 | }
36 |
37 | log.Info("JSON RPC service listen and serving", "port", apiPort, "allowedOrigins", allowedOrigins)
38 | svr := http.Server{
39 | Addr: fmt.Sprintf(":%v", apiPort),
40 | ReadTimeout: 60 * time.Second,
41 | WriteTimeout: 60 * time.Second,
42 | Handler: handlers.CORS(corsOptions...)(router),
43 | }
44 | go func() {
45 | if err := svr.ListenAndServe(); err != nil {
46 | log.Error("ListenAndServe error", "err", err)
47 | }
48 | }()
49 | }
50 |
51 | func initRouter() *mux.Router {
52 | r := mux.NewRouter()
53 |
54 | rpcserver := rpc.NewServer()
55 | rpcserver.RegisterCodec(rpcjson.NewCodec(), "application/json")
56 | _ = rpcserver.RegisterService(new(rpcapi.RPCAPI), "swap")
57 |
58 | r.Handle("/rpc", rpcserver)
59 | r.HandleFunc("/serverinfo", restapi.SeverInfoHandler).Methods("GET")
60 | r.HandleFunc("/statistics", restapi.StatisticsHandler).Methods("GET")
61 | r.HandleFunc("/swapin/post/{txid}", restapi.PostSwapinHandler).Methods("POST")
62 | r.HandleFunc("/swapin/post/{txid}/{bind}", restapi.PostP2shSwapinHandler).Methods("POST")
63 | r.HandleFunc("/swapout/post/{txid}", restapi.PostSwapoutHandler).Methods("POST")
64 | r.HandleFunc("/swapin/recall/{txid}", restapi.RecallSwapinHandler).Methods("POST")
65 | r.HandleFunc("/swapin/{txid}", restapi.GetSwapinHandler).Methods("GET")
66 | r.HandleFunc("/swapout/{txid}", restapi.GetSwapoutHandler).Methods("GET")
67 | r.HandleFunc("/swapin/{txid}/raw", restapi.GetRawSwapinHandler).Methods("GET")
68 | r.HandleFunc("/swapout/{txid}/raw", restapi.GetRawSwapoutHandler).Methods("GET")
69 | r.HandleFunc("/swapin/{txid}/rawresult", restapi.GetRawSwapinResultHandler).Methods("GET")
70 | r.HandleFunc("/swapout/{txid}/rawresult", restapi.GetRawSwapoutResultHandler).Methods("GET")
71 | r.HandleFunc("/swapin/history/{address}", restapi.SwapinHistoryHandler).Methods("GET")
72 | r.HandleFunc("/swapout/history/{address}", restapi.SwapoutHistoryHandler).Methods("GET")
73 | r.HandleFunc("/p2sh/{address}", restapi.GetP2shAddressInfo).Methods("GET", "POST")
74 | r.HandleFunc("/p2sh/bind/{address}", restapi.RegisterP2shAddress).Methods("GET", "POST")
75 |
76 | methodsExcluesGet := []string{"POST", "HEAD", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE", "PATCH"}
77 | methodsExcluesPost := []string{"GET", "HEAD", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE", "PATCH"}
78 | methodsExcluesGetAndPost := []string{"HEAD", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE", "PATCH"}
79 |
80 | r.HandleFunc("/serverinfo", warnHandler).Methods(methodsExcluesGet...)
81 | r.HandleFunc("/statistics", warnHandler).Methods(methodsExcluesGet...)
82 | r.HandleFunc("/swapin/post/{txid}", warnHandler).Methods(methodsExcluesPost...)
83 | r.HandleFunc("/swapin/post/{txid}/{bind}", warnHandler).Methods(methodsExcluesPost...)
84 | r.HandleFunc("/swapout/post/{txid}", warnHandler).Methods(methodsExcluesPost...)
85 | r.HandleFunc("/swapin/recall/{txid}", warnHandler).Methods(methodsExcluesPost...)
86 | r.HandleFunc("/swapin/{txid}", warnHandler).Methods(methodsExcluesGet...)
87 | r.HandleFunc("/swapout/{txid}", warnHandler).Methods(methodsExcluesGet...)
88 | r.HandleFunc("/swapin/{txid}/raw", warnHandler).Methods(methodsExcluesGet...)
89 | r.HandleFunc("/swapout/{txid}/raw", warnHandler).Methods(methodsExcluesGet...)
90 | r.HandleFunc("/swapin/{txid}/rawresult", warnHandler).Methods(methodsExcluesGet...)
91 | r.HandleFunc("/swapout/{txid}/rawresult", warnHandler).Methods(methodsExcluesGet...)
92 | r.HandleFunc("/swapin/history/{address}", warnHandler).Methods(methodsExcluesGet...)
93 | r.HandleFunc("/swapout/history/{address}", warnHandler).Methods(methodsExcluesGet...)
94 | r.HandleFunc("/p2sh/{address}", warnHandler).Methods(methodsExcluesGetAndPost...)
95 | r.HandleFunc("/p2sh/bind/{address}", warnHandler).Methods(methodsExcluesGetAndPost...)
96 |
97 | return r
98 | }
99 |
100 | func warnHandler(w http.ResponseWriter, r *http.Request) {
101 | fmt.Fprintf(w, "Forbid '%v' on '%v'\n", r.Method, r.RequestURI)
102 | }
103 |
--------------------------------------------------------------------------------
/tokens/btc/address.go:
--------------------------------------------------------------------------------
1 | package btc
2 |
3 | import (
4 | "strings"
5 |
6 | "github.com/btcsuite/btcd/chaincfg"
7 | "github.com/btcsuite/btcutil"
8 | )
9 |
10 | // IsValidAddress check address
11 | func (b *Bridge) IsValidAddress(addr string) bool {
12 | chainConfig := b.GetChainConfig()
13 | address, err := btcutil.DecodeAddress(addr, chainConfig)
14 | if err != nil {
15 | return false
16 | }
17 | return address.IsForNet(chainConfig)
18 | }
19 |
20 | // IsP2pkhAddress check p2pkh addrss
21 | func (b *Bridge) IsP2pkhAddress(addr string) bool {
22 | chainConfig := b.GetChainConfig()
23 | address, err := btcutil.DecodeAddress(addr, chainConfig)
24 | if err != nil {
25 | return false
26 | }
27 | if !address.IsForNet(chainConfig) {
28 | return false
29 | }
30 | _, ok := address.(*btcutil.AddressPubKeyHash)
31 | return ok
32 | }
33 |
34 | // IsP2shAddress check p2sh addrss
35 | func (b *Bridge) IsP2shAddress(addr string) bool {
36 | chainConfig := b.GetChainConfig()
37 | address, err := btcutil.DecodeAddress(addr, chainConfig)
38 | if err != nil {
39 | return false
40 | }
41 | if !address.IsForNet(chainConfig) {
42 | return false
43 | }
44 | _, ok := address.(*btcutil.AddressScriptHash)
45 | return ok
46 | }
47 |
48 | // GetChainConfig get chain config (net params)
49 | func (b *Bridge) GetChainConfig() *chaincfg.Params {
50 | token := b.TokenConfig
51 | networkID := strings.ToLower(token.NetID)
52 | switch networkID {
53 | case netMainnet:
54 | return &chaincfg.MainNetParams
55 | case netTestnet3:
56 | return &chaincfg.TestNet3Params
57 | }
58 | return &chaincfg.TestNet3Params
59 | }
60 |
--------------------------------------------------------------------------------
/tokens/btc/aggregate.go:
--------------------------------------------------------------------------------
1 | package btc
2 |
3 | import (
4 | "errors"
5 |
6 | "github.com/fsn-dev/crossChain-Bridge/tokens"
7 | "github.com/fsn-dev/crossChain-Bridge/tokens/btc/electrs"
8 | )
9 |
10 | const (
11 | // AggregateIdentifier used in accepting
12 | AggregateIdentifier = "aggregate"
13 | )
14 |
15 | // AggregateUtxos aggregate uxtos
16 | func (b *Bridge) AggregateUtxos(addrs []string, utxos []*electrs.ElectUtxo) (string, error) {
17 | authoredTx, err := b.BuildAggregateTransaction(addrs, utxos)
18 | if err != nil {
19 | return "", err
20 | }
21 |
22 | args := &tokens.BuildTxArgs{
23 | Extra: &tokens.AllExtras{
24 | BtcExtra: &tokens.BtcExtraArgs{},
25 | },
26 | }
27 |
28 | args.Identifier = AggregateIdentifier
29 | extra := args.Extra.BtcExtra
30 | extra.PreviousOutPoints = make([]*tokens.BtcOutPoint, len(authoredTx.Tx.TxIn))
31 | for i, txin := range authoredTx.Tx.TxIn {
32 | point := txin.PreviousOutPoint
33 | extra.PreviousOutPoints[i] = &tokens.BtcOutPoint{
34 | Hash: point.Hash.String(),
35 | Index: point.Index,
36 | }
37 | }
38 |
39 | signedTx, txHash, err := b.DcrmSignTransaction(authoredTx, args)
40 | if err != nil {
41 | return "", err
42 | }
43 | _, err = b.SendTransaction(signedTx)
44 | if err != nil {
45 | return "", err
46 | }
47 | return txHash, nil
48 | }
49 |
50 | // VerifyAggregateMsgHash verify aggregate msgHash
51 | func (b *Bridge) VerifyAggregateMsgHash(msgHash []string, args *tokens.BuildTxArgs) error {
52 | if args == nil || args.Extra == nil || args.Extra.BtcExtra == nil || len(args.Extra.BtcExtra.PreviousOutPoints) == 0 {
53 | return errors.New("empty btc extra")
54 | }
55 | rawTx, err := b.rebuildAggregateTransaction(args.Extra.BtcExtra.PreviousOutPoints)
56 | if err != nil {
57 | return err
58 | }
59 | return b.VerifyMsgHash(rawTx, msgHash, args.Extra)
60 | }
61 |
--------------------------------------------------------------------------------
/tokens/btc/bridge.go:
--------------------------------------------------------------------------------
1 | package btc
2 |
3 | import (
4 | "strings"
5 | "time"
6 |
7 | "github.com/fsn-dev/crossChain-Bridge/log"
8 | "github.com/fsn-dev/crossChain-Bridge/tokens"
9 | )
10 |
11 | const (
12 | netMainnet = "mainnet"
13 | netTestnet3 = "testnet3"
14 | netCustom = "custom"
15 | )
16 |
17 | // BridgeInstance btc bridge instance
18 | var BridgeInstance *Bridge
19 |
20 | // Bridge btc bridge
21 | type Bridge struct {
22 | *tokens.CrossChainBridgeBase
23 | }
24 |
25 | // NewCrossChainBridge new btc bridge
26 | func NewCrossChainBridge(isSrc bool) *Bridge {
27 | if !isSrc {
28 | panic(tokens.ErrBridgeDestinationNotSupported)
29 | }
30 | BridgeInstance = &Bridge{tokens.NewCrossChainBridgeBase(isSrc)}
31 | return BridgeInstance
32 | }
33 |
34 | // SetTokenAndGateway set token and gateway config
35 | func (b *Bridge) SetTokenAndGateway(tokenCfg *tokens.TokenConfig, gatewayCfg *tokens.GatewayConfig) {
36 | b.CrossChainBridgeBase.SetTokenAndGateway(tokenCfg, gatewayCfg)
37 |
38 | networkID := strings.ToLower(tokenCfg.NetID)
39 | switch networkID {
40 | case netMainnet, netTestnet3:
41 | case netCustom:
42 | return
43 | default:
44 | log.Fatal("unsupported bitcoin network", "netID", tokenCfg.NetID)
45 | }
46 |
47 | if !b.IsP2pkhAddress(tokenCfg.DcrmAddress) {
48 | log.Fatal("invalid dcrm address (not p2pkh)", "address", tokenCfg.DcrmAddress)
49 | }
50 |
51 | if strings.EqualFold(tokenCfg.Symbol, "BTC") && *tokenCfg.Decimals != 8 {
52 | log.Fatal("invalid decimals for BTC", "configed", *tokenCfg.Decimals, "want", 8)
53 | }
54 |
55 | var latest uint64
56 | var err error
57 | for {
58 | latest, err = b.GetLatestBlockNumber()
59 | if err == nil {
60 | tokens.SetLatestBlockHeight(latest, b.IsSrc)
61 | log.Info("get latst block number succeed.", "number", latest, "BlockChain", tokenCfg.BlockChain, "NetID", tokenCfg.NetID)
62 | break
63 | }
64 | log.Error("get latst block number failed.", "BlockChain", tokenCfg.BlockChain, "NetID", tokenCfg.NetID, "err", err)
65 | log.Println("retry query gateway", gatewayCfg.APIAddress)
66 | time.Sleep(3 * time.Second)
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/tokens/btc/buildaggregatetx.go:
--------------------------------------------------------------------------------
1 | package btc
2 |
3 | import (
4 | "fmt"
5 | "time"
6 |
7 | "github.com/btcsuite/btcd/chaincfg/chainhash"
8 | "github.com/btcsuite/btcd/wire"
9 | "github.com/btcsuite/btcutil"
10 | "github.com/btcsuite/btcwallet/wallet/txauthor"
11 | "github.com/fsn-dev/crossChain-Bridge/log"
12 | "github.com/fsn-dev/crossChain-Bridge/tokens"
13 | "github.com/fsn-dev/crossChain-Bridge/tokens/btc/electrs"
14 | "github.com/fsn-dev/crossChain-Bridge/tokens/tools"
15 | )
16 |
17 | // BuildAggregateTransaction build aggregate tx (spend p2sh utxo)
18 | func (b *Bridge) BuildAggregateTransaction(addrs []string, utxos []*electrs.ElectUtxo) (rawTx *txauthor.AuthoredTx, err error) {
19 | if len(addrs) != len(utxos) {
20 | return nil, fmt.Errorf("call BuildAggregateTransaction: count of addrs (%v) is not equal to count of utxos (%v)", len(addrs), len(utxos))
21 | }
22 |
23 | inputSource := func(target btcutil.Amount) (total btcutil.Amount, inputs []*wire.TxIn, inputValues []btcutil.Amount, scripts [][]byte, err error) {
24 | return b.getUtxosFromElectUtxos(target, addrs, utxos)
25 | }
26 |
27 | changeSource := func() ([]byte, error) {
28 | return b.getPayToAddrScript(b.TokenConfig.DcrmAddress)
29 | }
30 |
31 | relayFeePerKb := btcutil.Amount(tokens.BtcRelayFeePerKb)
32 |
33 | return NewUnsignedTransaction(nil, relayFeePerKb, inputSource, changeSource)
34 | }
35 |
36 | func (b *Bridge) rebuildAggregateTransaction(prevOutPoints []*tokens.BtcOutPoint) (rawTx *txauthor.AuthoredTx, err error) {
37 | addrs, utxos, err := b.getUtxosFromOutPoints(prevOutPoints)
38 | if err != nil {
39 | return nil, err
40 | }
41 | return b.BuildAggregateTransaction(addrs, utxos)
42 | }
43 |
44 | func (b *Bridge) getUtxosFromElectUtxos(target btcutil.Amount, addrs []string, utxos []*electrs.ElectUtxo) (total btcutil.Amount, inputs []*wire.TxIn, inputValues []btcutil.Amount, scripts [][]byte, err error) {
45 | var (
46 | txHash *chainhash.Hash
47 | value btcutil.Amount
48 | pkScript []byte
49 | p2shAddr string
50 | errt error
51 | )
52 |
53 | for i, utxo := range utxos {
54 | value = btcutil.Amount(*utxo.Value)
55 | if value == 0 {
56 | continue
57 | }
58 |
59 | address := addrs[i]
60 | if b.IsP2shAddress(address) {
61 | bindAddr := tools.GetP2shBindAddress(address)
62 | if bindAddr == "" {
63 | continue
64 | }
65 | p2shAddr, _, _ = b.GetP2shAddress(bindAddr)
66 | if p2shAddr != address {
67 | log.Warn("wrong registered p2sh address", "have", address, "bind", bindAddr, "want", p2shAddr)
68 | continue
69 | }
70 | }
71 |
72 | pkScript, errt = b.getPayToAddrScript(address)
73 | if errt != nil {
74 | continue
75 | }
76 |
77 | txHash, _ = chainhash.NewHashFromStr(*utxo.Txid)
78 | prevOutPoint := wire.NewOutPoint(txHash, *utxo.Vout)
79 | txIn := wire.NewTxIn(prevOutPoint, pkScript, nil)
80 |
81 | total += value
82 | inputs = append(inputs, txIn)
83 | inputValues = append(inputValues, value)
84 | scripts = append(scripts, pkScript)
85 | }
86 |
87 | if total < target {
88 | log.Warn("getUtxos total %v < target %v", total, target)
89 | }
90 |
91 | return total, inputs, inputValues, scripts, nil
92 | }
93 |
94 | func (b *Bridge) getUtxosFromOutPoints(prevOutPoints []*tokens.BtcOutPoint) (addrs []string, utxos []*electrs.ElectUtxo, err error) {
95 | var (
96 | tx *electrs.ElectTx
97 | outspend *electrs.ElectOutspend
98 | )
99 |
100 | for _, point := range prevOutPoints {
101 | for i := 0; i < retryCount; i++ {
102 | outspend, err = b.GetOutspend(point.Hash, point.Index)
103 | if err == nil {
104 | break
105 | }
106 | time.Sleep(retryInterval)
107 | }
108 | if err != nil {
109 | return nil, nil, err
110 | }
111 | if *outspend.Spent {
112 | if outspend.Status != nil && outspend.Status.BlockHeight != nil {
113 | spentHeight := *outspend.Status.BlockHeight
114 | err = fmt.Errorf("out point (%v, %v) is spent at %v", point.Hash, point.Index, spentHeight)
115 | } else {
116 | err = fmt.Errorf("out point (%v, %v) is spent at txpool", point.Hash, point.Index)
117 | }
118 | return nil, nil, err
119 | }
120 | for i := 0; i < retryCount; i++ {
121 | tx, err = b.GetTransactionByHash(point.Hash)
122 | if err == nil {
123 | break
124 | }
125 | time.Sleep(retryInterval)
126 | }
127 | if err != nil {
128 | return nil, nil, err
129 | }
130 | if point.Index >= uint32(len(tx.Vout)) {
131 | err = fmt.Errorf("out point (%v, %v) index overflow", point.Hash, point.Index)
132 | return nil, nil, err
133 | }
134 | output := tx.Vout[point.Index]
135 | if *output.Value == 0 {
136 | err = fmt.Errorf("out point (%v, %v) with zero value", point.Hash, point.Index)
137 | return nil, nil, err
138 | }
139 |
140 | addrs = append(addrs, *output.ScriptpubkeyAddress)
141 | utxos = append(utxos, &electrs.ElectUtxo{
142 | Txid: &point.Hash,
143 | Vout: &point.Index,
144 | Value: output.Value,
145 | })
146 | }
147 | return addrs, utxos, nil
148 | }
149 |
--------------------------------------------------------------------------------
/tokens/btc/callapi.go:
--------------------------------------------------------------------------------
1 | package btc
2 |
3 | import (
4 | "github.com/fsn-dev/crossChain-Bridge/tokens/btc/electrs"
5 | )
6 |
7 | // GetLatestBlockNumber impl
8 | func (b *Bridge) GetLatestBlockNumber() (uint64, error) {
9 | return electrs.GetLatestBlockNumber(b)
10 | }
11 |
12 | // GetTransactionByHash impl
13 | func (b *Bridge) GetTransactionByHash(txHash string) (*electrs.ElectTx, error) {
14 | return electrs.GetTransactionByHash(b, txHash)
15 | }
16 |
17 | // GetElectTransactionStatus impl
18 | func (b *Bridge) GetElectTransactionStatus(txHash string) (*electrs.ElectTxStatus, error) {
19 | return electrs.GetElectTransactionStatus(b, txHash)
20 | }
21 |
22 | // FindUtxos impl
23 | func (b *Bridge) FindUtxos(addr string) ([]*electrs.ElectUtxo, error) {
24 | return electrs.FindUtxos(b, addr)
25 | }
26 |
27 | // GetPoolTxidList impl
28 | func (b *Bridge) GetPoolTxidList() ([]string, error) {
29 | return electrs.GetPoolTxidList(b)
30 | }
31 |
32 | // GetPoolTransactions impl
33 | func (b *Bridge) GetPoolTransactions(addr string) ([]*electrs.ElectTx, error) {
34 | return electrs.GetPoolTransactions(b, addr)
35 | }
36 |
37 | // GetTransactionHistory impl
38 | func (b *Bridge) GetTransactionHistory(addr, lastSeenTxid string) ([]*electrs.ElectTx, error) {
39 | return electrs.GetTransactionHistory(b, addr, lastSeenTxid)
40 | }
41 |
42 | // GetOutspend impl
43 | func (b *Bridge) GetOutspend(txHash string, vout uint32) (*electrs.ElectOutspend, error) {
44 | return electrs.GetOutspend(b, txHash, vout)
45 | }
46 |
47 | // PostTransaction impl
48 | func (b *Bridge) PostTransaction(txHex string) (txHash string, err error) {
49 | return electrs.PostTransaction(b, txHex)
50 | }
51 |
52 | // GetBlockHash impl
53 | func (b *Bridge) GetBlockHash(height uint64) (string, error) {
54 | return electrs.GetBlockHash(b, height)
55 | }
56 |
57 | // GetBlockTxids impl
58 | func (b *Bridge) GetBlockTxids(blockHash string) ([]string, error) {
59 | return electrs.GetBlockTxids(b, blockHash)
60 | }
61 |
--------------------------------------------------------------------------------
/tokens/btc/electrs/callapi.go:
--------------------------------------------------------------------------------
1 | package electrs
2 |
3 | import (
4 | "fmt"
5 | "sort"
6 |
7 | "github.com/fsn-dev/crossChain-Bridge/rpc/client"
8 | "github.com/fsn-dev/crossChain-Bridge/tokens"
9 | )
10 |
11 | // GetLatestBlockNumber call /blocks/tip/height
12 | func GetLatestBlockNumber(b tokens.CrossChainBridge) (uint64, error) {
13 | _, gateway := b.GetTokenAndGateway()
14 | url := gateway.APIAddress + "/blocks/tip/height"
15 | var result uint64
16 | err := client.RPCGet(&result, url)
17 | return result, err
18 | }
19 |
20 | // GetTransactionByHash call /tx/{txHash}
21 | func GetTransactionByHash(b tokens.CrossChainBridge, txHash string) (*ElectTx, error) {
22 | _, gateway := b.GetTokenAndGateway()
23 | url := gateway.APIAddress + "/tx/" + txHash
24 | var result ElectTx
25 | err := client.RPCGet(&result, url)
26 | return &result, err
27 | }
28 |
29 | // GetElectTransactionStatus call /tx/{txHash}/status
30 | func GetElectTransactionStatus(b tokens.CrossChainBridge, txHash string) (*ElectTxStatus, error) {
31 | _, gateway := b.GetTokenAndGateway()
32 | url := gateway.APIAddress + "/tx/" + txHash + "/status"
33 | var result ElectTxStatus
34 | err := client.RPCGet(&result, url)
35 | return &result, err
36 | }
37 |
38 | // FindUtxos call /address/{add}/utxo (confirmed first, then big value first)
39 | func FindUtxos(b tokens.CrossChainBridge, addr string) ([]*ElectUtxo, error) {
40 | _, gateway := b.GetTokenAndGateway()
41 | url := gateway.APIAddress + "/address/" + addr + "/utxo"
42 | var result []*ElectUtxo
43 | err := client.RPCGet(&result, url)
44 | sort.Sort(SortableElectUtxoSlice(result))
45 | return result, err
46 | }
47 |
48 | // GetPoolTxidList call /mempool/txids
49 | func GetPoolTxidList(b tokens.CrossChainBridge) ([]string, error) {
50 | _, gateway := b.GetTokenAndGateway()
51 | url := gateway.APIAddress + "/mempool/txids"
52 | var result []string
53 | err := client.RPCGet(&result, url)
54 | return result, err
55 | }
56 |
57 | // GetPoolTransactions call /address/{addr}/txs/mempool
58 | func GetPoolTransactions(b tokens.CrossChainBridge, addr string) ([]*ElectTx, error) {
59 | _, gateway := b.GetTokenAndGateway()
60 | url := gateway.APIAddress + "/address/" + addr + "/txs/mempool"
61 | var result []*ElectTx
62 | err := client.RPCGet(&result, url)
63 | return result, err
64 | }
65 |
66 | // GetTransactionHistory call /address/{addr}/txs/chain
67 | func GetTransactionHistory(b tokens.CrossChainBridge, addr, lastSeenTxid string) ([]*ElectTx, error) {
68 | _, gateway := b.GetTokenAndGateway()
69 | url := gateway.APIAddress + "/address/" + addr + "/txs/chain"
70 | if lastSeenTxid != "" {
71 | url = url + "/" + lastSeenTxid
72 | }
73 | var result []*ElectTx
74 | err := client.RPCGet(&result, url)
75 | return result, err
76 | }
77 |
78 | // GetOutspend call /tx/{txHash}/outspend/{vout}
79 | func GetOutspend(b tokens.CrossChainBridge, txHash string, vout uint32) (*ElectOutspend, error) {
80 | _, gateway := b.GetTokenAndGateway()
81 | url := gateway.APIAddress + "/tx/" + txHash + "/outspend/" + fmt.Sprintf("%d", vout)
82 | var result ElectOutspend
83 | err := client.RPCGet(&result, url)
84 | return &result, err
85 | }
86 |
87 | // PostTransaction call post to /tx
88 | func PostTransaction(b tokens.CrossChainBridge, txHex string) (txHash string, err error) {
89 | _, gateway := b.GetTokenAndGateway()
90 | url := gateway.APIAddress + "/tx"
91 | return client.RPCRawPost(url, txHex)
92 | }
93 |
94 | // GetBlockHash call /block-height/{height}
95 | func GetBlockHash(b tokens.CrossChainBridge, height uint64) (string, error) {
96 | _, gateway := b.GetTokenAndGateway()
97 | url := gateway.APIAddress + "/block-height/" + fmt.Sprintf("%d", height)
98 | return client.RPCRawGet(url)
99 | }
100 |
101 | // GetBlockTxids call /block/{blockHash}/txids
102 | func GetBlockTxids(b tokens.CrossChainBridge, blockHash string) ([]string, error) {
103 | _, gateway := b.GetTokenAndGateway()
104 | url := gateway.APIAddress + "/block/" + blockHash + "/txids"
105 | var result []string
106 | err := client.RPCGet(&result, url)
107 | return result, err
108 | }
109 |
--------------------------------------------------------------------------------
/tokens/btc/electrs/types.go:
--------------------------------------------------------------------------------
1 | package electrs
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | // ElectTx struct
8 | type ElectTx struct {
9 | Txid *string `json:"txid"`
10 | Version *uint32 `json:"version"`
11 | Locktime *uint32 `json:"locktime"`
12 | Size *uint32 `json:"size"`
13 | Weight *uint32 `json:"weight"`
14 | Fee *uint64 `json:"fee"`
15 | Vin []*ElectTxin `json:"vin"`
16 | Vout []*ElectTxOut `json:"vout"`
17 | Status *ElectTxStatus `json:"status,omitempty"`
18 | }
19 |
20 | // ElectTxin struct
21 | type ElectTxin struct {
22 | Txid *string `json:"txid"`
23 | Vout *uint32 `json:"vout"`
24 | Scriptsig *string `json:"scriptsig"`
25 | ScriptsigAsm *string `json:"scriptsig_asm"`
26 | IsCoinbase *bool `json:"is_coinbase"`
27 | Sequence *uint32 `json:"sequence"`
28 | InnerRedeemscriptAsm *string `json:"inner_redeemscript_asm"`
29 | Prevout *ElectTxOut `json:"prevout"`
30 | }
31 |
32 | // ElectTxOut struct
33 | type ElectTxOut struct {
34 | Scriptpubkey *string `json:"scriptpubkey"`
35 | ScriptpubkeyAsm *string `json:"scriptpubkey_asm"`
36 | ScriptpubkeyType *string `json:"scriptpubkey_type"`
37 | ScriptpubkeyAddress *string `json:"scriptpubkey_address"`
38 | Value *uint64 `json:"value"`
39 | }
40 |
41 | // ElectOutspend struct
42 | type ElectOutspend struct {
43 | Spent *bool `json:"spent"`
44 | Txid *string `json:"txid"`
45 | Vin *ElectTxin `json:"vin"`
46 | Status *ElectTxStatus `json:"status,omitempty"`
47 | }
48 |
49 | // ElectTxStatus struct
50 | type ElectTxStatus struct {
51 | Confirmed *bool `json:"confirmed"`
52 | BlockHeight *uint64 `json:"block_height"`
53 | BlockHash *string `json:"block_hash"`
54 | BlockTime *uint64 `json:"block_time"`
55 | }
56 |
57 | // ElectUtxo struct
58 | type ElectUtxo struct {
59 | Txid *string `json:"txid"`
60 | Vout *uint32 `json:"vout"`
61 | Value *uint64 `json:"value"`
62 | Status *ElectTxStatus `json:"status"`
63 | }
64 |
65 | func (utxo *ElectUtxo) String() string {
66 | return fmt.Sprintf("txid %v vout %v value %v confirmed %v", *utxo.Txid, *utxo.Vout, *utxo.Value, *utxo.Status.Confirmed)
67 | }
68 |
69 | // SortableElectUtxoSlice sortable
70 | type SortableElectUtxoSlice []*ElectUtxo
71 |
72 | // Len impl Sortable
73 | func (s SortableElectUtxoSlice) Len() int {
74 | return len(s)
75 | }
76 |
77 | // Swap impl Sortable
78 | func (s SortableElectUtxoSlice) Swap(i, j int) {
79 | s[i], s[j] = s[j], s[i]
80 | }
81 |
82 | // Less impl Sortable
83 | // sort utxos
84 | // 1. confirmed fisrt
85 | // 2. value first
86 | func (s SortableElectUtxoSlice) Less(i, j int) bool {
87 | confirmed1 := *s[i].Status.Confirmed
88 | confirmed2 := *s[j].Status.Confirmed
89 | if confirmed1 != confirmed2 {
90 | return confirmed1
91 | }
92 | return *s[i].Value > *s[j].Value
93 | }
94 |
--------------------------------------------------------------------------------
/tokens/btc/p2shaddress.go:
--------------------------------------------------------------------------------
1 | package btc
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/btcsuite/btcd/chaincfg"
7 | "github.com/btcsuite/btcd/txscript"
8 | "github.com/btcsuite/btcutil"
9 | "github.com/fsn-dev/crossChain-Bridge/common"
10 | "github.com/fsn-dev/crossChain-Bridge/tokens"
11 | "github.com/fsn-dev/crossChain-Bridge/tokens/tools"
12 | )
13 |
14 | // GetP2shAddressWithMemo common
15 | func GetP2shAddressWithMemo(memo, pubKeyHash []byte, net *chaincfg.Params) (p2shAddress string, redeemScript []byte, err error) {
16 | redeemScript, err = txscript.NewScriptBuilder().
17 | AddData(memo).AddOp(txscript.OP_DROP).
18 | AddOp(txscript.OP_DUP).AddOp(txscript.OP_HASH160).AddData(pubKeyHash).
19 | AddOp(txscript.OP_EQUALVERIFY).AddOp(txscript.OP_CHECKSIG).
20 | Script()
21 | if err != nil {
22 | return
23 | }
24 | var addressScriptHash *btcutil.AddressScriptHash
25 | addressScriptHash, err = btcutil.NewAddressScriptHash(redeemScript, net)
26 | if err != nil {
27 | return
28 | }
29 | p2shAddress = addressScriptHash.EncodeAddress()
30 | return
31 | }
32 |
33 | // GetP2shAddress get p2sh address from bind address
34 | func (b *Bridge) GetP2shAddress(bindAddr string) (p2shAddress string, redeemScript []byte, err error) {
35 | if !tokens.GetCrossChainBridge(!b.IsSrc).IsValidAddress(bindAddr) {
36 | return "", nil, fmt.Errorf("invalid bind address %v", bindAddr)
37 | }
38 | memo := common.FromHex(bindAddr)
39 | net := b.GetChainConfig()
40 | dcrmAddress := b.TokenConfig.DcrmAddress
41 | address, _ := btcutil.DecodeAddress(dcrmAddress, net)
42 | pubKeyHash := address.ScriptAddress()
43 | return GetP2shAddressWithMemo(memo, pubKeyHash, net)
44 | }
45 |
46 | func (b *Bridge) getRedeemScriptByOutputScrpit(preScript []byte) ([]byte, error) {
47 | pkScript, err := txscript.ParsePkScript(preScript)
48 | if err != nil {
49 | return nil, err
50 | }
51 | p2shAddress, err := pkScript.Address(b.GetChainConfig())
52 | if err != nil {
53 | return nil, err
54 | }
55 | p2shAddr := p2shAddress.String()
56 | bindAddr := tools.GetP2shBindAddress(p2shAddr)
57 | if bindAddr == "" {
58 | return nil, fmt.Errorf("ps2h address %v is registered", p2shAddr)
59 | }
60 | var address string
61 | address, redeemScript, _ := b.GetP2shAddress(bindAddr)
62 | if address != p2shAddr {
63 | return nil, fmt.Errorf("ps2h address mismatch for bind address %v, have %v want %v", bindAddr, p2shAddr, address)
64 | }
65 | return redeemScript, nil
66 | }
67 |
68 | // GetP2shAddressByRedeemScript get p2sh address by redeem script
69 | func (b *Bridge) GetP2shAddressByRedeemScript(redeemScript []byte) (string, error) {
70 | net := b.GetChainConfig()
71 | addressScriptHash, err := btcutil.NewAddressScriptHash(redeemScript, net)
72 | if err != nil {
73 | return "", err
74 | }
75 | return addressScriptHash.EncodeAddress(), nil
76 | }
77 |
78 | // GetP2shSigScript get p2sh signature script
79 | func (b *Bridge) GetP2shSigScript(redeemScript []byte) ([]byte, error) {
80 | p2shAddr, err := b.GetP2shAddressByRedeemScript(redeemScript)
81 | if err != nil {
82 | return nil, err
83 | }
84 | return b.getPayToAddrScript(p2shAddr)
85 | }
86 |
--------------------------------------------------------------------------------
/tokens/btc/printtx.go:
--------------------------------------------------------------------------------
1 | package btc
2 |
3 | import (
4 | "bytes"
5 | "encoding/json"
6 |
7 | "github.com/btcsuite/btcd/txscript"
8 | "github.com/btcsuite/btcutil"
9 | "github.com/btcsuite/btcwallet/wallet/txauthor"
10 | "github.com/fsn-dev/crossChain-Bridge/common/hexutil"
11 | )
12 |
13 | // MarshalToJSON marshal to json
14 | func MarshalToJSON(obj interface{}, pretty bool) string {
15 | var jsdata []byte
16 | if pretty {
17 | jsdata, _ = json.MarshalIndent(obj, "", " ")
18 | } else {
19 | jsdata, _ = json.Marshal(obj)
20 | }
21 | return string(jsdata)
22 | }
23 |
24 | // AuthoredTxToString AuthoredTx to string
25 | func AuthoredTxToString(authtx interface{}, pretty bool) string {
26 | authoredTx, ok := authtx.(*txauthor.AuthoredTx)
27 | if !ok {
28 | return MarshalToJSON(authtx, pretty)
29 | }
30 |
31 | var encAuthTx EncAuthoredTx
32 |
33 | encAuthTx.ChangeIndex = authoredTx.ChangeIndex
34 | encAuthTx.TotalInput = authoredTx.TotalInput
35 |
36 | tx := authoredTx.Tx
37 | if tx == nil {
38 | return MarshalToJSON(encAuthTx, pretty)
39 | }
40 |
41 | buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize()))
42 | _ = tx.Serialize(buf)
43 | txid := tx.TxHash().String()
44 |
45 | var encTx EncMsgTx
46 |
47 | encTx.Txid = txid
48 | encTx.Version = tx.Version
49 | encTx.LockTime = tx.LockTime
50 |
51 | encTx.TxOut = make([]*EncTxOut, len(tx.TxOut))
52 | for i, txOut := range tx.TxOut {
53 | encTx.TxOut[i] = &EncTxOut{
54 | Value: txOut.Value,
55 | }
56 | encTx.TxOut[i].PkScript, _ = txscript.DisasmString(txOut.PkScript)
57 | }
58 |
59 | encTx.TxIn = make([]*EncTxIn, len(tx.TxIn))
60 | for i, txIn := range tx.TxIn {
61 | encTx.TxIn[i] = &EncTxIn{
62 | PreviousOutPoint: EncOutPoint{
63 | Hash: txIn.PreviousOutPoint.Hash.String(),
64 | Index: txIn.PreviousOutPoint.Index,
65 | },
66 | Sequence: txIn.Sequence,
67 | Value: authoredTx.PrevInputValues[i],
68 | }
69 | encTx.TxIn[i].SignatureScript, _ = txscript.DisasmString(txIn.SignatureScript)
70 | encTx.TxIn[i].Witness = make([]hexutil.Bytes, len(txIn.Witness))
71 | for j, witness := range txIn.Witness {
72 | encTx.TxIn[i].Witness[j] = hexutil.Bytes(witness)
73 | }
74 | }
75 |
76 | encAuthTx.Tx = &encTx
77 | return MarshalToJSON(encAuthTx, pretty)
78 | }
79 |
80 | // EncAuthoredTx stuct
81 | type EncAuthoredTx struct {
82 | Tx *EncMsgTx
83 | TotalInput btcutil.Amount
84 | ChangeIndex int
85 | }
86 |
87 | // EncMsgTx struct
88 | type EncMsgTx struct {
89 | Txid string
90 | Version int32
91 | TxIn []*EncTxIn
92 | TxOut []*EncTxOut
93 | LockTime uint32
94 | }
95 |
96 | // EncTxOut struct
97 | type EncTxOut struct {
98 | PkScript string
99 | Value int64
100 | }
101 |
102 | // EncOutPoint struct
103 | type EncOutPoint struct {
104 | Hash string
105 | Index uint32
106 | }
107 |
108 | // EncTxIn struct
109 | type EncTxIn struct {
110 | PreviousOutPoint EncOutPoint
111 | SignatureScript string
112 | Witness []hexutil.Bytes
113 | Sequence uint32
114 | Value btcutil.Amount
115 | }
116 |
--------------------------------------------------------------------------------
/tokens/btc/processtx.go:
--------------------------------------------------------------------------------
1 | package btc
2 |
3 | import (
4 | "github.com/fsn-dev/crossChain-Bridge/log"
5 | "github.com/fsn-dev/crossChain-Bridge/tokens"
6 | "github.com/fsn-dev/crossChain-Bridge/tokens/tools"
7 | )
8 |
9 | func (b *Bridge) processTransaction(txid string) {
10 | _ = b.processSwapin(txid)
11 | _ = b.processP2shSwapin(txid)
12 | }
13 |
14 | func (b *Bridge) processSwapin(txid string) error {
15 | if tools.IsSwapinExist(txid) {
16 | return nil
17 | }
18 | swapInfo, err := b.VerifyTransaction(txid, true)
19 | if !tokens.ShouldRegisterSwapForError(err) {
20 | return err
21 | }
22 | err = tools.RegisterSwapin(txid, swapInfo.Bind)
23 | if err != nil {
24 | log.Trace("[scan] processSwapin", "txid", txid, "err", err)
25 | }
26 | return err
27 | }
28 |
29 | func (b *Bridge) processP2shSwapin(txid string) error {
30 | if tools.IsSwapinExist(txid) {
31 | return nil
32 | }
33 | swapInfo, err := b.checkP2shTransaction(txid, true)
34 | if !tokens.ShouldRegisterSwapForError(err) {
35 | return err
36 | }
37 | err = tools.RegisterP2shSwapin(txid, swapInfo.Bind)
38 | if err != nil {
39 | log.Trace("[scan] processP2shSwapin", "txid", txid, "err", err)
40 | }
41 | return err
42 | }
43 |
44 | func (b *Bridge) checkP2shTransaction(txHash string, allowUnstable bool) (*tokens.TxSwapInfo, error) {
45 | tx, err := b.GetTransactionByHash(txHash)
46 | if err != nil {
47 | log.Debug(b.TokenConfig.BlockChain+" Bridge::GetTransaction fail", "tx", txHash, "err", err)
48 | return nil, tokens.ErrTxNotFound
49 | }
50 | var bindAddress, p2shAddress string
51 | for _, output := range tx.Vout {
52 | if *output.ScriptpubkeyType == p2shType {
53 | p2shAddress = *output.ScriptpubkeyAddress
54 | bindAddress = tools.GetP2shBindAddress(p2shAddress)
55 | if bindAddress != "" {
56 | break
57 | }
58 | }
59 | }
60 | if bindAddress == "" {
61 | return nil, tokens.ErrTxWithWrongReceiver
62 | }
63 | return b.VerifyP2shTransaction(txHash, bindAddress, allowUnstable)
64 | }
65 |
--------------------------------------------------------------------------------
/tokens/btc/scanchaintx.go:
--------------------------------------------------------------------------------
1 | package btc
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/fsn-dev/crossChain-Bridge/log"
7 | "github.com/fsn-dev/crossChain-Bridge/tokens/tools"
8 | )
9 |
10 | var (
11 | scannedBlocks = tools.NewCachedScannedBlocks(13)
12 | )
13 |
14 | // StartChainTransactionScanJob scan job
15 | func (b *Bridge) StartChainTransactionScanJob() {
16 | log.Info("[scanchain] start scan chain tx job", "isSrc", b.IsSrc)
17 |
18 | startHeight := tools.GetLatestScanHeight(b.IsSrc)
19 | confirmations := *b.TokenConfig.Confirmations
20 | initialHeight := b.TokenConfig.InitialHeight
21 |
22 | var height uint64
23 | switch {
24 | case startHeight != 0:
25 | height = startHeight
26 | case initialHeight != 0:
27 | height = initialHeight
28 | default:
29 | latest := tools.LoopGetLatestBlockNumber(b)
30 | if latest > confirmations {
31 | height = latest - confirmations
32 | }
33 | }
34 | if height < initialHeight {
35 | height = initialHeight
36 | }
37 | _ = tools.UpdateLatestScanInfo(b.IsSrc, height)
38 | log.Info("[scanchain] start scan tx history loop", "isSrc", b.IsSrc, "start", height)
39 |
40 | for {
41 | latest := tools.LoopGetLatestBlockNumber(b)
42 | for h := height + 1; h <= latest; {
43 | blockHash, err := b.GetBlockHash(h)
44 | if err != nil {
45 | log.Error("[scanchain] get block hash failed", "isSrc", b.IsSrc, "height", h, "err", err)
46 | time.Sleep(retryIntervalInScanJob)
47 | continue
48 | }
49 | if scannedBlocks.IsBlockScanned(blockHash) {
50 | h++
51 | continue
52 | }
53 | txids, err := b.GetBlockTxids(blockHash)
54 | if err != nil {
55 | log.Error("[scanchain] get block txids failed", "isSrc", b.IsSrc, "height", h, "blockHash", blockHash, "err", err)
56 | time.Sleep(retryIntervalInScanJob)
57 | continue
58 | }
59 | for _, txid := range txids {
60 | b.processTransaction(txid)
61 | }
62 | scannedBlocks.CacheScannedBlock(blockHash, h)
63 | log.Info("[scanchain] scanned tx history", "isSrc", b.IsSrc, "blockHash", blockHash, "height", h, "txs", len(txids))
64 | h++
65 | }
66 | if latest > confirmations {
67 | latestStable := latest - confirmations
68 | if height < latestStable {
69 | height = latestStable
70 | _ = tools.UpdateLatestScanInfo(b.IsSrc, height)
71 | }
72 | }
73 | time.Sleep(restIntervalInScanJob)
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/tokens/btc/scanpooltx.go:
--------------------------------------------------------------------------------
1 | package btc
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/fsn-dev/crossChain-Bridge/log"
7 | "github.com/fsn-dev/crossChain-Bridge/tokens/tools"
8 | )
9 |
10 | var (
11 | scannedTxs = tools.NewCachedScannedTxs(100)
12 | )
13 |
14 | // StartPoolTransactionScanJob scan job
15 | func (b *Bridge) StartPoolTransactionScanJob() {
16 | log.Info("[scanpool] start scan pool tx job", "isSrc", b.IsSrc)
17 | for {
18 | txids, err := b.GetPoolTxidList()
19 | if err != nil {
20 | log.Error("[scanpool] get pool tx list error", "isSrc", b.IsSrc, "err", err)
21 | time.Sleep(retryIntervalInScanJob)
22 | continue
23 | }
24 | log.Info("[scanpool] scan pool tx", "isSrc", b.IsSrc, "txs", len(txids))
25 | for _, txid := range txids {
26 | if scannedTxs.IsTxScanned(txid) {
27 | continue
28 | }
29 | b.processTransaction(txid)
30 | scannedTxs.CacheScannedTx(txid)
31 | }
32 | time.Sleep(restIntervalInScanJob)
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/tokens/btc/scanswaphistory.go:
--------------------------------------------------------------------------------
1 | package btc
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/fsn-dev/crossChain-Bridge/log"
7 | "github.com/fsn-dev/crossChain-Bridge/tokens/tools"
8 | )
9 |
10 | var (
11 | maxScanLifetime = int64(3 * 24 * 3600)
12 | retryIntervalInScanJob = 3 * time.Second
13 | restIntervalInScanJob = 3 * time.Second
14 | )
15 |
16 | // StartSwapHistoryScanJob scan job
17 | func (b *Bridge) StartSwapHistoryScanJob() {
18 | log.Info("[scanhistory] start scan swap history job", "isSrc", b.IsSrc)
19 |
20 | isProcessed := func(txid string) bool {
21 | if b.IsSrc {
22 | return tools.IsSwapinExist(txid)
23 | }
24 | return tools.IsSwapoutExist(txid)
25 | }
26 |
27 | go b.scanFirstLoop(isProcessed)
28 |
29 | b.scanTransactionHistory(isProcessed)
30 | }
31 |
32 | func (b *Bridge) scanFirstLoop(isProcessed func(string) bool) {
33 | // first loop process all tx history no matter whether processed before
34 | log.Info("[scanhistory] start first scan loop", "isSrc", b.IsSrc)
35 | var (
36 | nowTime = time.Now().Unix()
37 | lastSeenTxid = ""
38 | initialHeight = b.TokenConfig.InitialHeight
39 | )
40 |
41 | isTooOld := func(time *uint64) bool {
42 | return time != nil && int64(*time)+maxScanLifetime < nowTime
43 | }
44 |
45 | FIRST_LOOP:
46 | for {
47 | txHistory, err := b.GetTransactionHistory(b.TokenConfig.DcrmAddress, lastSeenTxid)
48 | if err != nil {
49 | time.Sleep(retryIntervalInScanJob)
50 | continue
51 | }
52 | if len(txHistory) == 0 {
53 | break
54 | }
55 | for _, tx := range txHistory {
56 | if tx.Status.BlockHeight != nil && *tx.Status.BlockHeight < initialHeight {
57 | break FIRST_LOOP
58 | }
59 | if isTooOld(tx.Status.BlockTime) {
60 | break FIRST_LOOP
61 | }
62 | txid := *tx.Txid
63 | if !isProcessed(txid) {
64 | _ = b.processSwapin(txid)
65 | }
66 | }
67 | lastSeenTxid = *txHistory[len(txHistory)-1].Txid
68 | }
69 |
70 | log.Info("[scanhistory] finish first scan loop", "isSrc", b.IsSrc)
71 | }
72 |
73 | func (b *Bridge) scanTransactionHistory(isProcessed func(string) bool) {
74 | log.Info("[scanhistory] start scan swap history loop", "isSrc", b.IsSrc)
75 | var (
76 | lastSeenTxid = ""
77 | rescan = true
78 | initialHeight = b.TokenConfig.InitialHeight
79 | )
80 |
81 | for {
82 | txHistory, err := b.GetTransactionHistory(b.TokenConfig.DcrmAddress, lastSeenTxid)
83 | if err != nil {
84 | log.Error("[scanhistory] get tx history error", "isSrc", b.IsSrc, "err", err)
85 | time.Sleep(retryIntervalInScanJob)
86 | continue
87 | }
88 | if len(txHistory) == 0 {
89 | rescan = true
90 | } else if rescan {
91 | rescan = false
92 | }
93 | log.Info("[scanhistory] scan swap history", "isSrc", b.IsSrc, "count", len(txHistory))
94 | for _, tx := range txHistory {
95 | if tx.Status.BlockHeight != nil && *tx.Status.BlockHeight < initialHeight {
96 | rescan = true
97 | break
98 | }
99 | txid := *tx.Txid
100 | if isProcessed(txid) {
101 | rescan = true
102 | break // rescan if already processed
103 | }
104 | _ = b.processSwapin(txid)
105 | }
106 | if rescan {
107 | lastSeenTxid = ""
108 | time.Sleep(restIntervalInScanJob)
109 | } else {
110 | lastSeenTxid = *txHistory[len(txHistory)-1].Txid
111 | }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/tokens/btc/sendtx.go:
--------------------------------------------------------------------------------
1 | package btc
2 |
3 | import (
4 | "bytes"
5 | "encoding/hex"
6 |
7 | "github.com/btcsuite/btcwallet/wallet/txauthor"
8 | "github.com/fsn-dev/crossChain-Bridge/log"
9 | "github.com/fsn-dev/crossChain-Bridge/tokens"
10 | )
11 |
12 | // SendTransaction send signed tx
13 | func (b *Bridge) SendTransaction(signedTx interface{}) (txHash string, err error) {
14 | authoredTx, ok := signedTx.(*txauthor.AuthoredTx)
15 | if !ok {
16 | return "", tokens.ErrWrongRawTx
17 | }
18 |
19 | tx := authoredTx.Tx
20 | if tx == nil {
21 | return "", tokens.ErrWrongRawTx
22 | }
23 |
24 | buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize()))
25 | err = tx.Serialize(buf)
26 | if err != nil {
27 | return "", err
28 | }
29 | txHex := hex.EncodeToString(buf.Bytes())
30 | log.Info("Bridge send tx", "hash", tx.TxHash())
31 |
32 | return b.PostTransaction(txHex)
33 | }
34 |
--------------------------------------------------------------------------------
/tokens/btc/verifyp2shtx.go:
--------------------------------------------------------------------------------
1 | package btc
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/fsn-dev/crossChain-Bridge/common"
7 | "github.com/fsn-dev/crossChain-Bridge/log"
8 | "github.com/fsn-dev/crossChain-Bridge/tokens"
9 | )
10 |
11 | // VerifyP2shTransaction verify p2sh tx
12 | func (b *Bridge) VerifyP2shTransaction(txHash, bindAddress string, allowUnstable bool) (*tokens.TxSwapInfo, error) {
13 | swapInfo := &tokens.TxSwapInfo{}
14 | swapInfo.Hash = txHash // Hash
15 | if !b.IsSrc {
16 | return swapInfo, tokens.ErrBridgeDestinationNotSupported
17 | }
18 | p2shAddress, _, err := b.GetP2shAddress(bindAddress)
19 | if err != nil {
20 | return swapInfo, fmt.Errorf("verify p2sh tx, wrong bind address %v", bindAddress)
21 | }
22 | if !allowUnstable && !b.checkStable(txHash) {
23 | return swapInfo, tokens.ErrTxNotStable
24 | }
25 | tx, err := b.GetTransactionByHash(txHash)
26 | if err != nil {
27 | log.Debug(b.TokenConfig.BlockChain+" Bridge::GetTransaction fail", "tx", txHash, "err", err)
28 | return swapInfo, tokens.ErrTxNotFound
29 | }
30 | txStatus := tx.Status
31 | if txStatus.BlockHeight != nil {
32 | swapInfo.Height = *txStatus.BlockHeight // Height
33 | }
34 | if txStatus.BlockTime != nil {
35 | swapInfo.Timestamp = *txStatus.BlockTime // Timestamp
36 | }
37 | value, _, rightReceiver := b.getReceivedValue(tx.Vout, p2shAddress)
38 | if !rightReceiver {
39 | return swapInfo, tokens.ErrTxWithWrongReceiver
40 | }
41 | swapInfo.To = p2shAddress // To
42 | swapInfo.Bind = bindAddress // Bind
43 | swapInfo.Value = common.BigFromUint64(value) // Value
44 |
45 | swapInfo.From = getTxFrom(tx.Vin) // From
46 |
47 | // check sender
48 | if swapInfo.From == swapInfo.To {
49 | return swapInfo, tokens.ErrTxWithWrongSender
50 | }
51 |
52 | if !tokens.CheckSwapValue(swapInfo.Value, b.IsSrc) {
53 | return swapInfo, tokens.ErrTxWithWrongValue
54 | }
55 |
56 | if !allowUnstable {
57 | log.Debug("verify p2sh swapin pass", "from", swapInfo.From, "to", swapInfo.To, "bind", swapInfo.Bind, "value", swapInfo.Value, "txid", swapInfo.Hash, "height", swapInfo.Height, "timestamp", swapInfo.Timestamp)
58 | }
59 | return swapInfo, nil
60 | }
61 |
--------------------------------------------------------------------------------
/tokens/eth/abipack.go:
--------------------------------------------------------------------------------
1 | package eth
2 |
3 | import (
4 | "fmt"
5 | "math/big"
6 |
7 | "github.com/fsn-dev/crossChain-Bridge/common"
8 | )
9 |
10 | // PackDataWithFuncHash pack data with func hash
11 | func PackDataWithFuncHash(funcHash []byte, args ...interface{}) []byte {
12 | packData := PackData(args...)
13 |
14 | bs := make([]byte, 4+len(packData))
15 |
16 | copy(bs[:4], funcHash)
17 | copy(bs[4:], packData)
18 |
19 | return bs
20 | }
21 |
22 | // PackData pack data
23 | func PackData(args ...interface{}) []byte {
24 | lenArgs := len(args)
25 | bs := make([]byte, lenArgs*32)
26 | for i, arg := range args {
27 | switch v := arg.(type) {
28 | case common.Hash:
29 | copy(bs[i*32:], packHash(v))
30 | case common.Address:
31 | copy(bs[i*32:], packAddress(v))
32 | case *big.Int:
33 | copy(bs[i*32:(i+1)*32], packBigInt(v))
34 | case string:
35 | offset := big.NewInt(int64(len(bs)))
36 | copy(bs[i*32:], packBigInt(offset))
37 | bs = append(bs, packString(v)...)
38 | case uint64:
39 | copy(bs[i*32:], packBigInt(new(big.Int).SetUint64(v)))
40 | case int64:
41 | copy(bs[i*32:], packBigInt(big.NewInt(v)))
42 | case int:
43 | copy(bs[i*32:], packBigInt(big.NewInt(int64(v))))
44 | default:
45 | panic(fmt.Sprintf("unsupported to pack %v (%T)", v, v))
46 | }
47 | }
48 | return bs
49 | }
50 |
51 | func packHash(hash common.Hash) []byte {
52 | return hash.Bytes()
53 | }
54 |
55 | func packAddress(address common.Address) []byte {
56 | return address.Hash().Bytes()
57 | }
58 |
59 | func packBigInt(bi *big.Int) []byte {
60 | return common.LeftPadBytes(bi.Bytes(), 32)
61 | }
62 |
63 | func packString(str string) []byte {
64 | strLen := len(str)
65 | paddedStrLen := (strLen + 31) / 32 * 32
66 |
67 | bs := make([]byte, 32+paddedStrLen)
68 |
69 | copy(bs[:32], packBigInt(big.NewInt(int64(strLen))))
70 | copy(bs[32:], str)
71 |
72 | return bs
73 | }
74 |
--------------------------------------------------------------------------------
/tokens/eth/address.go:
--------------------------------------------------------------------------------
1 | package eth
2 |
3 | import (
4 | "github.com/fsn-dev/crossChain-Bridge/common"
5 | )
6 |
7 | // IsValidAddress check address
8 | func (b *Bridge) IsValidAddress(address string) bool {
9 | if !common.IsHexAddress(address) {
10 | return false
11 | }
12 | unprefixedHex, ok, hasUpperChar := common.GetUnprefixedHex(address)
13 | if hasUpperChar {
14 | // valid checksum
15 | if unprefixedHex != common.HexToAddress(address).String()[2:] {
16 | return false
17 | }
18 | }
19 | return ok
20 | }
21 |
--------------------------------------------------------------------------------
/tokens/eth/bridge.go:
--------------------------------------------------------------------------------
1 | package eth
2 |
3 | import (
4 | "fmt"
5 | "math/big"
6 | "strings"
7 | "time"
8 |
9 | "github.com/fsn-dev/crossChain-Bridge/log"
10 | "github.com/fsn-dev/crossChain-Bridge/tokens"
11 | "github.com/fsn-dev/crossChain-Bridge/types"
12 | )
13 |
14 | const (
15 | netMainnet = "mainnet"
16 | netRinkeby = "rinkeby"
17 | netCustom = "custom"
18 | )
19 |
20 | // Bridge eth bridge
21 | type Bridge struct {
22 | *tokens.CrossChainBridgeBase
23 | Signer types.Signer
24 | }
25 |
26 | // NewCrossChainBridge new bridge
27 | func NewCrossChainBridge(isSrc bool) *Bridge {
28 | return &Bridge{CrossChainBridgeBase: tokens.NewCrossChainBridgeBase(isSrc)}
29 | }
30 |
31 | // SetTokenAndGateway set token and gateway config
32 | func (b *Bridge) SetTokenAndGateway(tokenCfg *tokens.TokenConfig, gatewayCfg *tokens.GatewayConfig) {
33 | b.CrossChainBridgeBase.SetTokenAndGateway(tokenCfg, gatewayCfg)
34 | b.VerifyChainID()
35 | b.VerifyTokenCofig()
36 | b.InitLatestBlockNumber()
37 | }
38 |
39 | // VerifyChainID verify chain id
40 | func (b *Bridge) VerifyChainID() {
41 | tokenCfg := b.TokenConfig
42 | gatewayCfg := b.GatewayConfig
43 |
44 | networkID := strings.ToLower(tokenCfg.NetID)
45 |
46 | switch networkID {
47 | case netMainnet, netRinkeby:
48 | case netCustom:
49 | return
50 | default:
51 | panic(fmt.Sprintf("unsupported ethereum network: %v", tokenCfg.NetID))
52 | }
53 |
54 | var (
55 | chainID *big.Int
56 | err error
57 | )
58 |
59 | for {
60 | chainID, err = b.ChainID()
61 | if err == nil {
62 | break
63 | }
64 | log.Errorf("can not get gateway chainID. %v", err)
65 | log.Println("retry query gateway", gatewayCfg.APIAddress)
66 | time.Sleep(3 * time.Second)
67 | }
68 |
69 | panicMismatchChainID := func() {
70 | panic(fmt.Sprintf("gateway chainID %v is not %v", chainID, tokenCfg.NetID))
71 | }
72 |
73 | switch networkID {
74 | case netMainnet:
75 | if chainID.Uint64() != 1 {
76 | panicMismatchChainID()
77 | }
78 | case netRinkeby:
79 | if chainID.Uint64() != 4 {
80 | panicMismatchChainID()
81 | }
82 | default:
83 | panic("unsupported ethereum network")
84 | }
85 |
86 | b.Signer = types.MakeSigner("EIP155", chainID)
87 |
88 | log.Info("VerifyChainID succeed", "networkID", networkID, "chainID", chainID)
89 | }
90 |
91 | // VerifyTokenCofig verify token config
92 | func (b *Bridge) VerifyTokenCofig() {
93 | tokenCfg := b.TokenConfig
94 | if !b.IsValidAddress(tokenCfg.DcrmAddress) {
95 | log.Fatal("invalid dcrm address", "address", tokenCfg.DcrmAddress)
96 | }
97 |
98 | configedDecimals := *tokenCfg.Decimals
99 | switch strings.ToUpper(tokenCfg.Symbol) {
100 | case "ETH", "FSN":
101 | if configedDecimals != 18 {
102 | log.Fatal("invalid decimals for ETH", "configed", configedDecimals, "want", 18)
103 | }
104 | log.Info(tokenCfg.Symbol+" verify decimals success", "decimals", configedDecimals)
105 | }
106 |
107 | if tokenCfg.ContractAddress != "" {
108 | if !b.IsValidAddress(tokenCfg.ContractAddress) {
109 | log.Fatal("invalid contract address", "address", tokenCfg.ContractAddress)
110 | }
111 | switch {
112 | case !b.IsSrc:
113 | if err := b.VerifyMbtcContractAddress(tokenCfg.ContractAddress); err != nil {
114 | log.Fatal("wrong contract address", "address", tokenCfg.ContractAddress, "err", err)
115 | }
116 | case tokenCfg.IsErc20():
117 | if err := b.VerifyErc20ContractAddress(tokenCfg.ContractAddress); err != nil {
118 | log.Fatal("wrong contract address", "address", tokenCfg.ContractAddress, "err", err)
119 | }
120 | default:
121 | log.Fatal("unsupported type of contract address in source chain, please assign SrcToken.ID (eg. ERC20) in config file", "address", tokenCfg.ContractAddress)
122 | }
123 | log.Info("verify contract address pass", "address", tokenCfg.ContractAddress)
124 | }
125 |
126 | if tokenCfg.IsErc20() {
127 | for {
128 | decimals, err := b.GetErc20Decimals(tokenCfg.ContractAddress)
129 | if err == nil {
130 | if decimals != configedDecimals {
131 | log.Fatal("invalid decimals for "+tokenCfg.Symbol, "configed", configedDecimals, "want", decimals)
132 | }
133 | log.Info(tokenCfg.Symbol+" verify decimals success", "decimals", configedDecimals)
134 | break
135 | }
136 | log.Error("get erc20 decimals failed", "err", err)
137 | time.Sleep(3 * time.Second)
138 | }
139 | }
140 | }
141 |
142 | // InitLatestBlockNumber init latest block number
143 | func (b *Bridge) InitLatestBlockNumber() {
144 | var (
145 | tokenCfg = b.TokenConfig
146 | gatewayCfg = b.GatewayConfig
147 | latest uint64
148 | err error
149 | )
150 |
151 | for {
152 | latest, err = b.GetLatestBlockNumber()
153 | if err == nil {
154 | tokens.SetLatestBlockHeight(latest, b.IsSrc)
155 | log.Info("get latst block number succeed.", "number", latest, "BlockChain", tokenCfg.BlockChain, "NetID", tokenCfg.NetID)
156 | break
157 | }
158 | log.Error("get latst block number failed.", "BlockChain", tokenCfg.BlockChain, "NetID", tokenCfg.NetID, "err", err)
159 | log.Println("retry query gateway", gatewayCfg.APIAddress)
160 | time.Sleep(3 * time.Second)
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/tokens/eth/buildswapouttx.go:
--------------------------------------------------------------------------------
1 | package eth
2 |
3 | import (
4 | "fmt"
5 | "math/big"
6 |
7 | "github.com/fsn-dev/crossChain-Bridge/log"
8 | "github.com/fsn-dev/crossChain-Bridge/tokens"
9 | "github.com/fsn-dev/crossChain-Bridge/types"
10 | )
11 |
12 | // BuildSwapoutTx build swapout tx
13 | func (b *Bridge) BuildSwapoutTx(from, contract string, extraArgs *tokens.EthExtraArgs, swapoutVal *big.Int, bindAddr string) (*types.Transaction, error) {
14 | if swapoutVal == nil || swapoutVal.Sign() == 0 {
15 | return nil, fmt.Errorf("swapout value must be greater than zero")
16 | }
17 | balance, err := b.GetErc20Balance(contract, from)
18 | if err != nil {
19 | return nil, err
20 | }
21 | if balance.Cmp(swapoutVal) < 0 {
22 | return nil, fmt.Errorf("not enough balance, %v < %v", balance, swapoutVal)
23 | }
24 | token := b.TokenConfig
25 | if token != nil && !tokens.CheckSwapValue(swapoutVal, b.IsSrc) {
26 | decimals := *token.Decimals
27 | minValue := tokens.ToBits(*token.MinimumSwap, decimals)
28 | maxValue := tokens.ToBits(*token.MaximumSwap, decimals)
29 | return nil, fmt.Errorf("wrong swapout value, not in range [%v, %v]", minValue, maxValue)
30 | }
31 | if tokens.SrcBridge != nil && !tokens.SrcBridge.IsValidAddress(bindAddr) {
32 | return nil, fmt.Errorf("wrong swapout bind address %v", bindAddr)
33 | }
34 | input, err := BuildSwapoutTxInput(swapoutVal, bindAddr)
35 | if err != nil {
36 | return nil, err
37 | }
38 | args := &tokens.BuildTxArgs{
39 | From: from,
40 | To: contract,
41 | Value: big.NewInt(0),
42 | Input: &input,
43 | }
44 | if extraArgs != nil {
45 | args.Extra = &tokens.AllExtras{
46 | EthExtra: extraArgs,
47 | }
48 | }
49 | rawtx, err := b.BuildRawTransaction(args)
50 | if err != nil {
51 | return nil, err
52 | }
53 | tx, ok := rawtx.(*types.Transaction)
54 | if !ok {
55 | return nil, tokens.ErrWrongRawTx
56 | }
57 | return tx, nil
58 | }
59 |
60 | // BuildSwapoutTxInput build swapout tx input
61 | func BuildSwapoutTxInput(swapoutVal *big.Int, bindAddr string) ([]byte, error) {
62 | input := PackDataWithFuncHash(getSwapoutFuncHash(), swapoutVal, bindAddr)
63 |
64 | // verify input
65 | bindAddress, swapoutvalue, err := parseSwapoutTxInput(&input)
66 | if err != nil {
67 | log.Error("parseSwapoutTxInput error", "err", err)
68 | return nil, err
69 | }
70 | log.Info("parseSwapoutTxInput", "bindAddress", bindAddress, "swapoutvalue", swapoutvalue)
71 |
72 | return input, nil
73 | }
74 |
--------------------------------------------------------------------------------
/tokens/eth/callcontract.go:
--------------------------------------------------------------------------------
1 | package eth
2 |
3 | import (
4 | "math/big"
5 |
6 | "github.com/fsn-dev/crossChain-Bridge/common"
7 | "github.com/fsn-dev/crossChain-Bridge/common/hexutil"
8 | )
9 |
10 | // GetErc20Balance get erc20 balacne of address
11 | func (b *Bridge) GetErc20Balance(contract, address string) (*big.Int, error) {
12 | data := make(hexutil.Bytes, 36)
13 | copy(data[:4], erc20CodeParts["balanceOf"])
14 | copy(data[4:], common.HexToAddress(address).Hash().Bytes())
15 | result, err := b.CallContract(contract, data, "pending")
16 | if err != nil {
17 | return nil, err
18 | }
19 | return common.GetBigIntFromStr(result)
20 | }
21 |
22 | // GetErc20Decimals get erc20 decimals
23 | func (b *Bridge) GetErc20Decimals(contract string) (uint8, error) {
24 | data := make(hexutil.Bytes, 4)
25 | copy(data[:4], erc20CodeParts["decimals"])
26 | result, err := b.CallContract(contract, data, "latest")
27 | if err != nil {
28 | return 0, err
29 | }
30 | decimals, err := common.GetUint64FromStr(result)
31 | return uint8(decimals), err
32 | }
33 |
--------------------------------------------------------------------------------
/tokens/eth/processtx.go:
--------------------------------------------------------------------------------
1 | package eth
2 |
3 | import (
4 | "github.com/fsn-dev/crossChain-Bridge/tokens"
5 | "github.com/fsn-dev/crossChain-Bridge/tokens/tools"
6 | )
7 |
8 | func (b *Bridge) processTransaction(txid string) {
9 | if b.IsSrc {
10 | _ = b.processSwapin(txid)
11 | } else {
12 | _ = b.processSwapout(txid)
13 | }
14 | }
15 |
16 | func (b *Bridge) processSwapin(txid string) error {
17 | if tools.IsSwapinExist(txid) {
18 | return nil
19 | }
20 | swapInfo, err := b.VerifyTransaction(txid, true)
21 | if !tokens.ShouldRegisterSwapForError(err) {
22 | return err
23 | }
24 | return tools.RegisterSwapin(txid, swapInfo.Bind)
25 | }
26 |
27 | func (b *Bridge) processSwapout(txid string) error {
28 | if tools.IsSwapoutExist(txid) {
29 | return nil
30 | }
31 | swapInfo, err := b.VerifyTransaction(txid, true)
32 | if !tokens.ShouldRegisterSwapForError(err) {
33 | return err
34 | }
35 | return tools.RegisterSwapout(txid, swapInfo.Bind)
36 | }
37 |
--------------------------------------------------------------------------------
/tokens/eth/scanchaintx.go:
--------------------------------------------------------------------------------
1 | package eth
2 |
3 | import (
4 | "math/big"
5 | "time"
6 |
7 | "github.com/fsn-dev/crossChain-Bridge/log"
8 | "github.com/fsn-dev/crossChain-Bridge/tokens/tools"
9 | )
10 |
11 | var (
12 | scannedBlocks = tools.NewCachedScannedBlocks(67)
13 | )
14 |
15 | // StartChainTransactionScanJob scan job
16 | func (b *Bridge) StartChainTransactionScanJob() {
17 | log.Info("[scanchain] start scan chain job", "isSrc", b.IsSrc)
18 |
19 | startHeight := tools.GetLatestScanHeight(b.IsSrc)
20 | confirmations := *b.TokenConfig.Confirmations
21 | initialHeight := b.TokenConfig.InitialHeight
22 |
23 | var height uint64
24 | switch {
25 | case startHeight != 0:
26 | height = startHeight
27 | case initialHeight != 0:
28 | height = initialHeight
29 | default:
30 | latest := tools.LoopGetLatestBlockNumber(b)
31 | if latest > confirmations {
32 | height = latest - confirmations
33 | }
34 | }
35 | if height < initialHeight {
36 | height = initialHeight
37 | }
38 | _ = tools.UpdateLatestScanInfo(b.IsSrc, height)
39 | log.Info("[scanchain] start scan chain loop", "isSrc", b.IsSrc, "start", height)
40 |
41 | for {
42 | latest := tools.LoopGetLatestBlockNumber(b)
43 | for h := height + 1; h <= latest; {
44 | block, err := b.GetBlockByNumber(new(big.Int).SetUint64(h))
45 | if err != nil {
46 | log.Error("[scanchain] get block failed", "isSrc", b.IsSrc, "height", h, "err", err)
47 | time.Sleep(retryIntervalInScanJob)
48 | continue
49 | }
50 | blockHash := block.Hash.String()
51 | if scannedBlocks.IsBlockScanned(blockHash) {
52 | h++
53 | continue
54 | }
55 | for _, tx := range block.Transactions {
56 | b.processTransaction(tx.String())
57 | }
58 | scannedBlocks.CacheScannedBlock(blockHash, h)
59 | log.Info("[scanchain] scanned chain", "isSrc", b.IsSrc, "blockHash", blockHash, "height", h, "txs", len(block.Transactions))
60 | h++
61 | }
62 | if latest > confirmations {
63 | latestStable := latest - confirmations
64 | if height < latestStable {
65 | height = latestStable
66 | _ = tools.UpdateLatestScanInfo(b.IsSrc, height)
67 | }
68 | }
69 | time.Sleep(restIntervalInScanJob)
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/tokens/eth/scanpooltx.go:
--------------------------------------------------------------------------------
1 | package eth
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/fsn-dev/crossChain-Bridge/log"
7 | "github.com/fsn-dev/crossChain-Bridge/tokens/tools"
8 | )
9 |
10 | var (
11 | scannedTxs = tools.NewCachedScannedTxs(300)
12 | )
13 |
14 | // StartPoolTransactionScanJob scan job
15 | func (b *Bridge) StartPoolTransactionScanJob() {
16 | log.Info("[scanpool] start scan tx pool loop", "isSrc", b.IsSrc)
17 | for {
18 | txs, err := b.GetPendingTransactions()
19 | if err != nil {
20 | log.Error("[scanpool] get pool txs error", "isSrc", b.IsSrc, "err", err)
21 | time.Sleep(retryIntervalInScanJob)
22 | continue
23 | }
24 | log.Info("[scanpool] scan pool tx", "isSrc", b.IsSrc, "txs", len(txs))
25 | for _, tx := range txs {
26 | txid := tx.Hash.String()
27 | if scannedTxs.IsTxScanned(txid) {
28 | continue
29 | }
30 | b.processTransaction(txid)
31 | scannedTxs.CacheScannedTx(txid)
32 | }
33 | time.Sleep(restIntervalInScanJob)
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/tokens/eth/scanswaphistory.go:
--------------------------------------------------------------------------------
1 | package eth
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/fsn-dev/crossChain-Bridge/common"
7 | "github.com/fsn-dev/crossChain-Bridge/log"
8 | "github.com/fsn-dev/crossChain-Bridge/tokens/tools"
9 | "github.com/fsn-dev/crossChain-Bridge/types"
10 | )
11 |
12 | var (
13 | maxScanHeight = uint64(15000)
14 | retryIntervalInScanJob = 3 * time.Second
15 | restIntervalInScanJob = 3 * time.Second
16 | )
17 |
18 | // StartSwapHistoryScanJob scan job
19 | func (b *Bridge) StartSwapHistoryScanJob() {
20 | if b.TokenConfig.ContractAddress == "" {
21 | return
22 | }
23 | log.Info("[swaphistory] start scan swap history job", "isSrc", b.IsSrc)
24 |
25 | isProcessed := func(txid string) bool {
26 | if b.IsSrc {
27 | return tools.IsSwapinExist(txid)
28 | }
29 | return tools.IsSwapoutExist(txid)
30 | }
31 |
32 | go b.scanFirstLoop(isProcessed)
33 |
34 | b.scanTransactionHistory(isProcessed)
35 | }
36 |
37 | func (b *Bridge) getSwapLogs(blockHeight uint64) ([]*types.RPCLog, error) {
38 | token := b.TokenConfig
39 | contractAddress := token.ContractAddress
40 | var logTopic string
41 | if b.IsSrc {
42 | logTopic = common.ToHex(getLogSwapinTopic())
43 | } else {
44 | logTopic = common.ToHex(getLogSwapoutTopic())
45 | }
46 | return b.GetContractLogs(contractAddress, logTopic, blockHeight)
47 | }
48 |
49 | func (b *Bridge) scanFirstLoop(isProcessed func(string) bool) {
50 | // first loop process all tx history no matter whether processed before
51 | log.Info("[scanhistory] start first scan loop", "isSrc", b.IsSrc)
52 | initialHeight := b.TokenConfig.InitialHeight
53 | latest := tools.LoopGetLatestBlockNumber(b)
54 | for height := latest; height+maxScanHeight > latest && height >= initialHeight; {
55 | logs, err := b.getSwapLogs(height)
56 | if err != nil {
57 | log.Error("[scanhistory] get swap logs error", "isSrc", b.IsSrc, "height", height, "err", err)
58 | time.Sleep(retryIntervalInScanJob)
59 | continue
60 | }
61 | for _, log := range logs {
62 | txid := log.TxHash.String()
63 | if !isProcessed(txid) {
64 | b.processTransaction(txid)
65 | }
66 | }
67 | if height > 0 {
68 | height--
69 | } else {
70 | break
71 | }
72 | }
73 |
74 | log.Info("[scanhistory] finish first scan loop", "isSrc", b.IsSrc)
75 | }
76 |
77 | func (b *Bridge) scanTransactionHistory(isProcessed func(string) bool) {
78 | log.Info("[scanhistory] start scan swap history loop")
79 | var (
80 | height uint64
81 | rescan = true
82 | initialHeight = b.TokenConfig.InitialHeight
83 | )
84 | for {
85 | if rescan || height < initialHeight || height == 0 {
86 | height = tools.LoopGetLatestBlockNumber(b)
87 | }
88 | logs, err := b.getSwapLogs(height)
89 | if err != nil {
90 | log.Error("[swaphistory] get swap logs error", "isSrc", b.IsSrc, "height", height, "err", err)
91 | time.Sleep(retryIntervalInScanJob)
92 | continue
93 | }
94 | log.Info("[scanhistory] scan swap history", "isSrc", b.IsSrc, "height", height, "count", len(logs))
95 | for _, log := range logs {
96 | txid := log.TxHash.String()
97 | if isProcessed(txid) {
98 | rescan = true
99 | break // rescan if already processed
100 | }
101 | b.processTransaction(txid)
102 | }
103 | if rescan {
104 | time.Sleep(restIntervalInScanJob)
105 | } else if height > 0 {
106 | height--
107 | }
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/tokens/eth/sendtx.go:
--------------------------------------------------------------------------------
1 | package eth
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 |
7 | "github.com/fsn-dev/crossChain-Bridge/log"
8 | "github.com/fsn-dev/crossChain-Bridge/types"
9 | )
10 |
11 | // SendTransaction send signed tx
12 | func (b *Bridge) SendTransaction(signedTx interface{}) (txHash string, err error) {
13 | tx, ok := signedTx.(*types.Transaction)
14 | if !ok {
15 | fmt.Printf("signed tx is %+v\n", signedTx)
16 | return "", errors.New("wrong signed transaction type")
17 | }
18 | err = b.SendSignedTransaction(tx)
19 | if err != nil {
20 | log.Info("SendTransaction failed", "hash", tx.Hash().String(), "err", err)
21 | return "", err
22 | }
23 | log.Info("SendTransaction success", "hash", tx.Hash().String())
24 | return tx.Hash().String(), nil
25 | }
26 |
--------------------------------------------------------------------------------
/tokens/eth/signtx.go:
--------------------------------------------------------------------------------
1 | package eth
2 |
3 | import (
4 | "encoding/json"
5 | "errors"
6 | "fmt"
7 | "time"
8 |
9 | "github.com/fsn-dev/crossChain-Bridge/common"
10 | "github.com/fsn-dev/crossChain-Bridge/dcrm"
11 | "github.com/fsn-dev/crossChain-Bridge/log"
12 | "github.com/fsn-dev/crossChain-Bridge/tokens"
13 | "github.com/fsn-dev/crossChain-Bridge/tools/crypto"
14 | "github.com/fsn-dev/crossChain-Bridge/types"
15 | )
16 |
17 | var (
18 | retryCount = 15
19 | retryInterval = 10 * time.Second
20 | waitInterval = 10 * time.Second
21 | )
22 |
23 | // DcrmSignTransaction dcrm sign raw tx
24 | func (b *Bridge) DcrmSignTransaction(rawTx interface{}, args *tokens.BuildTxArgs) (signTx interface{}, txHash string, err error) {
25 | swapinNonce--
26 | tx, ok := rawTx.(*types.Transaction)
27 | if !ok {
28 | return nil, "", errors.New("wrong raw tx param")
29 | }
30 | signer := b.Signer
31 | msgHash := signer.Hash(tx)
32 | jsondata, _ := json.Marshal(args)
33 | msgContext := string(jsondata)
34 | keyID, err := dcrm.DoSignOne(msgHash.String(), msgContext)
35 | if err != nil {
36 | return nil, "", err
37 | }
38 | log.Info(b.TokenConfig.BlockChain+" DcrmSignTransaction start", "keyID", keyID, "msghash", msgHash.String(), "txid", args.SwapID)
39 | time.Sleep(waitInterval)
40 |
41 | var rsv string
42 | i := 0
43 | for ; i < retryCount; i++ {
44 | signStatus, err2 := dcrm.GetSignStatus(keyID)
45 | if err2 == nil {
46 | if len(signStatus.Rsv) != 1 {
47 | return nil, "", fmt.Errorf("get sign status require one rsv but have %v (keyID = %v)", len(signStatus.Rsv), keyID)
48 | }
49 |
50 | rsv = signStatus.Rsv[0]
51 | break
52 | }
53 | switch err2 {
54 | case dcrm.ErrGetSignStatusFailed, dcrm.ErrGetSignStatusTimeout:
55 | return nil, "", err2
56 | }
57 | log.Warn("retry get sign status as error", "err", err2, "txid", args.SwapID)
58 | time.Sleep(retryInterval)
59 | }
60 | if i == retryCount || rsv == "" {
61 | return nil, "", errors.New("get sign status failed")
62 | }
63 |
64 | log.Trace(b.TokenConfig.BlockChain+" DcrmSignTransaction get rsv success", "keyID", keyID, "rsv", rsv)
65 |
66 | signature := common.FromHex(rsv)
67 |
68 | if len(signature) != crypto.SignatureLength {
69 | log.Error("DcrmSignTransaction wrong length of signature")
70 | return nil, "", errors.New("wrong signature of keyID " + keyID)
71 | }
72 |
73 | signedTx, err := tx.WithSignature(signer, signature)
74 | if err != nil {
75 | return nil, "", err
76 | }
77 |
78 | sender, err := types.Sender(signer, signedTx)
79 | if err != nil {
80 | return nil, "", err
81 | }
82 |
83 | token := b.TokenConfig
84 | if sender.String() != token.DcrmAddress {
85 | log.Error("DcrmSignTransaction verify sender failed", "have", sender.String(), "want", token.DcrmAddress)
86 | return nil, "", errors.New("wrong sender address")
87 | }
88 | txHash = signedTx.Hash().String()
89 | swapinNonce++
90 | log.Info(b.TokenConfig.BlockChain+" DcrmSignTransaction success", "keyID", keyID, "txhash", txHash, "nonce", swapinNonce)
91 | return signedTx, txHash, err
92 | }
93 |
--------------------------------------------------------------------------------
/tokens/eth/verifycontractaddress.go:
--------------------------------------------------------------------------------
1 | package eth
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "time"
7 |
8 | "github.com/fsn-dev/crossChain-Bridge/common"
9 | "github.com/fsn-dev/crossChain-Bridge/log"
10 | "github.com/fsn-dev/crossChain-Bridge/tokens/btc"
11 | )
12 |
13 | var (
14 | // ExtCodeParts extended func hashes and log topics
15 | ExtCodeParts map[string][]byte
16 |
17 | // first 4 bytes of `Keccak256Hash([]byte("Swapin(bytes32,address,uint256)"))`
18 | swapinFuncHash = common.FromHex("0xec126c77")
19 | logSwapinTopic = common.FromHex("0x05d0634fe981be85c22e2942a880821b70095d84e152c3ea3c17a4e4250d9d61")
20 |
21 | // first 4 bytes of `Keccak256Hash([]byte("Swapout(uint256,string)"))`
22 | mBTCSwapoutFuncHash = common.FromHex("0xad54056d")
23 | mBTCLogSwapoutTopic = common.FromHex("0x9c92ad817e5474d30a4378deface765150479363a897b0590fbb12ae9d89396b")
24 |
25 | // first 4 bytes of `Keccak256Hash([]byte("Swapout(uint256)"))`
26 | mETHSwapoutFuncHash = common.FromHex("0xf1337b76")
27 | mETHLogSwapoutTopic = common.FromHex("0x9711511ecf3840282a7a2f2bd0f1dcc30c8cf0913c9575c8089a8d018a2099ff")
28 | )
29 |
30 | var mBTCExtCodeParts = map[string][]byte{
31 | // Extended interfaces
32 | "SwapinFuncHash": swapinFuncHash,
33 | "LogSwapinTopic": logSwapinTopic,
34 | "SwapoutFuncHash": mBTCSwapoutFuncHash,
35 | "LogSwapoutTopic": mBTCLogSwapoutTopic,
36 | }
37 |
38 | var mETHExtCodeParts = map[string][]byte{
39 | // Extended interfaces
40 | "SwapinFuncHash": swapinFuncHash,
41 | "LogSwapinTopic": logSwapinTopic,
42 | "SwapoutFuncHash": mETHSwapoutFuncHash,
43 | "LogSwapoutTopic": mETHLogSwapoutTopic,
44 | }
45 |
46 | var erc20CodeParts = map[string][]byte{
47 | // Erc20 interfaces
48 | "name": common.FromHex("0x06fdde03"),
49 | "symbol": common.FromHex("0x95d89b41"),
50 | "decimals": common.FromHex("0x313ce567"),
51 | "totalSupply": common.FromHex("0x18160ddd"),
52 | "balanceOf": common.FromHex("0x70a08231"),
53 | "transfer": common.FromHex("0xa9059cbb"),
54 | "transferFrom": common.FromHex("0x23b872dd"),
55 | "approve": common.FromHex("0x095ea7b3"),
56 | "allowance": common.FromHex("0xdd62ed3e"),
57 | "LogTransfer": common.FromHex("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"),
58 | "LogApproval": common.FromHex("0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"),
59 | }
60 |
61 | // VerifyContractCode verify contract code
62 | func (b *Bridge) VerifyContractCode(contract string, codePartsSlice ...map[string][]byte) (err error) {
63 | var code []byte
64 | retryCount := 3
65 | for i := 0; i < retryCount; i++ {
66 | code, err = b.GetCode(contract)
67 | if err == nil {
68 | break
69 | }
70 | log.Warn("get contract code failed", "contract", contract, "err", err)
71 | time.Sleep(1 * time.Second)
72 | }
73 | for _, codeParts := range codePartsSlice {
74 | for key, part := range codeParts {
75 | if !bytes.Contains(code, part) {
76 | return fmt.Errorf("contract byte code miss '%v' bytes '%x'", key, part)
77 | }
78 | }
79 | }
80 | return nil
81 | }
82 |
83 | // VerifyErc20ContractAddress verify erc20 contract
84 | func (b *Bridge) VerifyErc20ContractAddress(contract string) (err error) {
85 | return b.VerifyContractCode(contract, erc20CodeParts)
86 | }
87 |
88 | // VerifyMbtcContractAddress verify mbtc contract
89 | func (b *Bridge) VerifyMbtcContractAddress(contract string) (err error) {
90 | return b.VerifyContractCode(contract, ExtCodeParts, erc20CodeParts)
91 | }
92 |
93 | // InitExtCodeParts int extended code parts
94 | func InitExtCodeParts() {
95 | switch {
96 | case isMbtcSwapout():
97 | ExtCodeParts = mBTCExtCodeParts
98 | default:
99 | ExtCodeParts = mETHExtCodeParts
100 | }
101 | log.Info("init extented code parts", "isMBTC", isMbtcSwapout())
102 | }
103 |
104 | func isMbtcSwapout() bool {
105 | return btc.BridgeInstance != nil
106 | }
107 |
108 | func getSwapinFuncHash() []byte {
109 | return ExtCodeParts["SwapinFuncHash"]
110 | }
111 |
112 | func getLogSwapinTopic() []byte {
113 | return ExtCodeParts["LogSwapinTopic"]
114 | }
115 |
116 | func getSwapoutFuncHash() []byte {
117 | return ExtCodeParts["SwapoutFuncHash"]
118 | }
119 |
120 | func getLogSwapoutTopic() []byte {
121 | return ExtCodeParts["LogSwapoutTopic"]
122 | }
123 |
--------------------------------------------------------------------------------
/tokens/eth/verifytx.go:
--------------------------------------------------------------------------------
1 | package eth
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 |
7 | "github.com/fsn-dev/crossChain-Bridge/common"
8 | "github.com/fsn-dev/crossChain-Bridge/log"
9 | "github.com/fsn-dev/crossChain-Bridge/tokens"
10 | "github.com/fsn-dev/crossChain-Bridge/types"
11 | )
12 |
13 | // GetTransaction impl
14 | func (b *Bridge) GetTransaction(txHash string) (interface{}, error) {
15 | return b.GetTransactionByHash(txHash)
16 | }
17 |
18 | // GetTransactionStatus impl
19 | func (b *Bridge) GetTransactionStatus(txHash string) *tokens.TxStatus {
20 | var txStatus tokens.TxStatus
21 | txr, err := b.GetTransactionReceipt(txHash)
22 | if err != nil {
23 | log.Debug("GetTransactionReceipt fail", "hash", txHash, "err", err)
24 | return &txStatus
25 | }
26 | if *txr.Status != 1 {
27 | log.Debug("transaction with wrong receipt status", "hash", txHash, "status", txr.Status)
28 | }
29 | txStatus.BlockHeight = txr.BlockNumber.ToInt().Uint64()
30 | txStatus.BlockHash = txr.BlockHash.String()
31 | block, err := b.GetBlockByHash(txStatus.BlockHash)
32 | if err == nil {
33 | txStatus.BlockTime = block.Time.ToInt().Uint64()
34 | } else {
35 | log.Debug("GetBlockByHash fail", "hash", txStatus.BlockHash, "err", err)
36 | }
37 | if *txr.Status == 1 {
38 | latest, err := b.GetLatestBlockNumber()
39 | if err == nil {
40 | txStatus.Confirmations = latest - txStatus.BlockHeight
41 | } else {
42 | log.Debug("GetLatestBlockNumber fail", "err", err)
43 | }
44 | }
45 | txStatus.Receipt = txr
46 | return &txStatus
47 | }
48 |
49 | // VerifyMsgHash verify msg hash
50 | func (b *Bridge) VerifyMsgHash(rawTx interface{}, msgHashes []string, extra interface{}) error {
51 | tx, ok := rawTx.(*types.Transaction)
52 | if !ok {
53 | return tokens.ErrWrongRawTx
54 | }
55 | if len(msgHashes) != 1 {
56 | return fmt.Errorf("require one msgHash but have %v", len(msgHashes))
57 | }
58 | msgHash := msgHashes[0]
59 | signer := b.Signer
60 | sigHash := signer.Hash(tx)
61 | if sigHash.String() != msgHash {
62 | return tokens.ErrMsgHashMismatch
63 | }
64 | return nil
65 | }
66 |
67 | // VerifyTransaction impl
68 | func (b *Bridge) VerifyTransaction(txHash string, allowUnstable bool) (*tokens.TxSwapInfo, error) {
69 | if !b.IsSrc {
70 | return b.verifySwapoutTx(txHash, allowUnstable)
71 | }
72 | return b.verifySwapinTx(txHash, allowUnstable)
73 | }
74 |
75 | func (b *Bridge) verifySwapinTx(txHash string, allowUnstable bool) (*tokens.TxSwapInfo, error) {
76 | if b.TokenConfig.IsErc20() {
77 | return b.verifyErc20SwapinTx(txHash, allowUnstable)
78 | }
79 |
80 | swapInfo := &tokens.TxSwapInfo{}
81 | swapInfo.Hash = txHash // Hash
82 | token := b.TokenConfig
83 | dcrmAddress := token.DcrmAddress
84 |
85 | tx, err := b.GetTransactionByHash(txHash)
86 | if err != nil {
87 | log.Debug(b.TokenConfig.BlockChain+" Bridge::GetTransaction fail", "tx", txHash, "err", err)
88 | return swapInfo, tokens.ErrTxNotFound
89 | }
90 | if tx.BlockNumber != nil {
91 | swapInfo.Height = tx.BlockNumber.ToInt().Uint64() // Height
92 | }
93 | if tx.Recipient != nil {
94 | swapInfo.To = strings.ToLower(tx.Recipient.String()) // To
95 | }
96 | swapInfo.From = strings.ToLower(tx.From.String()) // From
97 | swapInfo.Bind = swapInfo.From // Bind
98 | swapInfo.Value = tx.Amount.ToInt() // Value
99 |
100 | if !allowUnstable {
101 | txStatus := b.GetTransactionStatus(txHash)
102 | swapInfo.Height = txStatus.BlockHeight // Height
103 | swapInfo.Timestamp = txStatus.BlockTime // Timestamp
104 | receipt, ok := txStatus.Receipt.(*types.RPCTxReceipt)
105 | if !ok || receipt == nil {
106 | return swapInfo, tokens.ErrTxNotStable
107 | }
108 | if *receipt.Status != 1 {
109 | return swapInfo, tokens.ErrTxWithWrongReceipt
110 | }
111 | if txStatus.BlockHeight == 0 ||
112 | txStatus.Confirmations < *token.Confirmations {
113 | return swapInfo, tokens.ErrTxNotStable
114 | }
115 | }
116 |
117 | if !common.IsEqualIgnoreCase(swapInfo.To, dcrmAddress) {
118 | return swapInfo, tokens.ErrTxWithWrongReceiver
119 | }
120 |
121 | // check sender
122 | if swapInfo.From == swapInfo.To {
123 | return swapInfo, tokens.ErrTxWithWrongSender
124 | }
125 |
126 | if !tokens.CheckSwapValue(swapInfo.Value, b.IsSrc) {
127 | return swapInfo, tokens.ErrTxWithWrongValue
128 | }
129 |
130 | log.Debug("verify swapin stable pass", "from", swapInfo.From, "to", swapInfo.To, "bind", swapInfo.Bind, "value", swapInfo.Value, "txid", txHash, "height", swapInfo.Height, "timestamp", swapInfo.Timestamp)
131 | return swapInfo, nil
132 | }
133 |
--------------------------------------------------------------------------------
/tokens/fsn/bridge.go:
--------------------------------------------------------------------------------
1 | package fsn
2 |
3 | import (
4 | "fmt"
5 | "math/big"
6 | "strings"
7 | "time"
8 |
9 | "github.com/fsn-dev/crossChain-Bridge/log"
10 | "github.com/fsn-dev/crossChain-Bridge/tokens"
11 | "github.com/fsn-dev/crossChain-Bridge/tokens/eth"
12 | "github.com/fsn-dev/crossChain-Bridge/types"
13 | )
14 |
15 | const (
16 | netMainnet = "mainnet"
17 | netTestnet = "testnet"
18 | netDevnet = "devnet"
19 | netCustom = "custom"
20 | )
21 |
22 | // Bridge fsn bridge inherit from eth bridge
23 | type Bridge struct {
24 | *eth.Bridge
25 | }
26 |
27 | // NewCrossChainBridge new fsn bridge
28 | func NewCrossChainBridge(isSrc bool) *Bridge {
29 | return &Bridge{Bridge: eth.NewCrossChainBridge(isSrc)}
30 | }
31 |
32 | // SetTokenAndGateway set token and gateway config
33 | func (b *Bridge) SetTokenAndGateway(tokenCfg *tokens.TokenConfig, gatewayCfg *tokens.GatewayConfig) {
34 | b.CrossChainBridgeBase.SetTokenAndGateway(tokenCfg, gatewayCfg)
35 | b.VerifyChainID()
36 | b.VerifyTokenCofig()
37 | b.InitLatestBlockNumber()
38 | }
39 |
40 | // VerifyChainID verify chain id
41 | func (b *Bridge) VerifyChainID() {
42 | tokenCfg := b.TokenConfig
43 | gatewayCfg := b.GatewayConfig
44 |
45 | networkID := strings.ToLower(tokenCfg.NetID)
46 |
47 | switch networkID {
48 | case netMainnet, netTestnet, netDevnet:
49 | case netCustom:
50 | return
51 | default:
52 | panic(fmt.Sprintf("unsupported fusion network: %v", tokenCfg.NetID))
53 | }
54 |
55 | var (
56 | chainID *big.Int
57 | err error
58 | )
59 |
60 | for {
61 | chainID, err = b.ChainID()
62 | if err == nil {
63 | break
64 | }
65 | log.Errorf("can not get gateway chainID. %v", err)
66 | log.Println("retry query gateway", gatewayCfg.APIAddress)
67 | time.Sleep(3 * time.Second)
68 | }
69 |
70 | panicMismatchChainID := func() {
71 | panic(fmt.Sprintf("gateway chainID %v is not %v", chainID, tokenCfg.NetID))
72 | }
73 |
74 | switch networkID {
75 | case netMainnet:
76 | if chainID.Uint64() != 32659 {
77 | panicMismatchChainID()
78 | }
79 | case netTestnet:
80 | if chainID.Uint64() != 46688 {
81 | panicMismatchChainID()
82 | }
83 | case netDevnet:
84 | if chainID.Uint64() != 55555 {
85 | panicMismatchChainID()
86 | }
87 | default:
88 | panic("unsupported fusion network")
89 | }
90 |
91 | b.Signer = types.MakeSigner("EIP155", chainID)
92 |
93 | log.Info("VerifyChainID succeed", "networkID", networkID, "chainID", chainID)
94 | }
95 |
--------------------------------------------------------------------------------
/tokens/fsn/callapi.go:
--------------------------------------------------------------------------------
1 | package fsn
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "math/big"
7 |
8 | "github.com/fsn-dev/crossChain-Bridge/rpc/client"
9 | "github.com/fsn-dev/crossChain-Bridge/types"
10 | )
11 |
12 | // GetTransactionAndReceipt get tx and receipt (fsn special)
13 | func (b *Bridge) GetTransactionAndReceipt(txHash string) (*types.RPCTxAndReceipt, error) {
14 | gateway := b.GatewayConfig
15 | url := gateway.APIAddress
16 | var result *types.RPCTxAndReceipt
17 | err := client.RPCPost(&result, url, "fsn_getTransactionAndReceipt", txHash)
18 | if err != nil {
19 | return nil, err
20 | }
21 | if result == nil {
22 | return nil, errors.New("tx and receipt not found")
23 | }
24 | return result, nil
25 | }
26 |
27 | // ChainID get chain id use net_version (eth_chainId does not work)
28 | func (b *Bridge) ChainID() (*big.Int, error) {
29 | gateway := b.GatewayConfig
30 | url := gateway.APIAddress
31 | var result string
32 | err := client.RPCPost(&result, url, "net_version")
33 | if err != nil {
34 | return nil, err
35 | }
36 | version := new(big.Int)
37 | if _, ok := version.SetString(result, 10); !ok {
38 | return nil, fmt.Errorf("invalid net_version result %q", result)
39 | }
40 | return version, nil
41 | }
42 |
--------------------------------------------------------------------------------
/tokens/tools/cachedscanblocks.go:
--------------------------------------------------------------------------------
1 | package tools
2 |
3 | // NewCachedScannedBlocks new cached scanned blocks
4 | func NewCachedScannedBlocks(capacity int) *CachedScannedBlocks {
5 | return &CachedScannedBlocks{
6 | nextIndex: 0,
7 | capacity: capacity,
8 | blocks: make([]cachedScannedBlockRecord, capacity),
9 | }
10 | }
11 |
12 | // CachedScannedBlocks cached scanned blocks
13 | type CachedScannedBlocks struct {
14 | nextIndex int
15 | capacity int
16 | blocks []cachedScannedBlockRecord
17 | }
18 |
19 | type cachedScannedBlockRecord struct {
20 | hash string
21 | height uint64
22 | }
23 |
24 | // CacheScannedBlock add cache block
25 | func (c *CachedScannedBlocks) CacheScannedBlock(hash string, height uint64) {
26 | c.blocks[c.nextIndex] = cachedScannedBlockRecord{
27 | hash: hash,
28 | height: height,
29 | }
30 | c.nextIndex = (c.nextIndex + 1) % c.capacity
31 | }
32 |
33 | // IsBlockScanned return if cache block exist
34 | func (c *CachedScannedBlocks) IsBlockScanned(blockHash string) bool {
35 | for _, block := range c.blocks {
36 | if block.hash == blockHash {
37 | return true
38 | }
39 | }
40 | return false
41 | }
42 |
--------------------------------------------------------------------------------
/tokens/tools/cachedscantxs.go:
--------------------------------------------------------------------------------
1 | package tools
2 |
3 | // NewCachedScannedTxs new cached scanned txs
4 | func NewCachedScannedTxs(capacity int) *CachedScannedTxs {
5 | return &CachedScannedTxs{
6 | nextIndex: 0,
7 | capacity: capacity,
8 | txs: make([]cachedScannedTxRecord, capacity),
9 | }
10 | }
11 |
12 | // CachedScannedTxs cached scanned txs
13 | type CachedScannedTxs struct {
14 | nextIndex int
15 | capacity int
16 | txs []cachedScannedTxRecord
17 | }
18 |
19 | type cachedScannedTxRecord struct {
20 | hash string
21 | }
22 |
23 | // CacheScannedTx add cache tx
24 | func (c *CachedScannedTxs) CacheScannedTx(hash string) {
25 | c.txs[c.nextIndex] = cachedScannedTxRecord{
26 | hash: hash,
27 | }
28 | c.nextIndex = (c.nextIndex + 1) % c.capacity
29 | }
30 |
31 | // IsTxScanned return if cache tx exist
32 | func (c *CachedScannedTxs) IsTxScanned(txHash string) bool {
33 | for _, tx := range c.txs {
34 | if tx.hash == txHash {
35 | return true
36 | }
37 | }
38 | return false
39 | }
40 |
--------------------------------------------------------------------------------
/tools/crypto/signature.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 The go-ethereum Authors
2 | // This file is part of the go-ethereum library.
3 | //
4 | // The go-ethereum library is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU Lesser General Public License as published by
6 | // the Free Software Foundation, either version 3 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // The go-ethereum library is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU Lesser General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU Lesser General Public License
15 | // along with the go-ethereum library. If not, see .
16 |
17 | package crypto
18 |
19 | import (
20 | "crypto/ecdsa"
21 | "crypto/elliptic"
22 | "errors"
23 | "fmt"
24 | "math/big"
25 |
26 | "github.com/btcsuite/btcd/btcec"
27 | )
28 |
29 | // Ecrecover returns the uncompressed public key that created the given signature.
30 | func Ecrecover(hash, sig []byte) ([]byte, error) {
31 | pub, err := SigToPub(hash, sig)
32 | if err != nil {
33 | return nil, err
34 | }
35 | bytes := (*btcec.PublicKey)(pub).SerializeUncompressed()
36 | return bytes, err
37 | }
38 |
39 | // SigToPub returns the public key that created the given signature.
40 | func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) {
41 | // Convert to btcec input format with 'recovery id' v at the beginning.
42 | btcsig := make([]byte, SignatureLength)
43 | btcsig[0] = sig[64] + 27
44 | copy(btcsig[1:], sig)
45 |
46 | pub, _, err := btcec.RecoverCompact(btcec.S256(), btcsig, hash)
47 | return (*ecdsa.PublicKey)(pub), err
48 | }
49 |
50 | // Sign calculates an ECDSA signature.
51 | //
52 | // This function is susceptible to chosen plaintext attacks that can leak
53 | // information about the private key that is used for signing. Callers must
54 | // be aware that the given hash cannot be chosen by an adversery. Common
55 | // solution is to hash any input before calculating the signature.
56 | //
57 | // The produced signature is in the [R || S || V] format where V is 0 or 1.
58 | func Sign(hash []byte, prv *ecdsa.PrivateKey) ([]byte, error) {
59 | if len(hash) != 32 {
60 | return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(hash))
61 | }
62 | if prv.Curve != btcec.S256() {
63 | return nil, fmt.Errorf("private key curve is not secp256k1")
64 | }
65 | sig, err := btcec.SignCompact(btcec.S256(), (*btcec.PrivateKey)(prv), hash, false)
66 | if err != nil {
67 | return nil, err
68 | }
69 | // Convert to Ethereum signature format with 'recovery id' v at the end.
70 | v := sig[0] - 27
71 | copy(sig, sig[1:])
72 | sig[64] = v
73 | return sig, nil
74 | }
75 |
76 | // VerifySignature checks that the given public key created signature over hash.
77 | // The public key should be in compressed (33 bytes) or uncompressed (65 bytes) format.
78 | // The signature should have the 64 byte [R || S] format.
79 | func VerifySignature(pubkey, hash, signature []byte) bool {
80 | if len(signature) != 64 {
81 | return false
82 | }
83 | sig := &btcec.Signature{R: new(big.Int).SetBytes(signature[:32]), S: new(big.Int).SetBytes(signature[32:])}
84 | key, err := btcec.ParsePubKey(pubkey, btcec.S256())
85 | if err != nil {
86 | return false
87 | }
88 | // Reject malleable signatures. libsecp256k1 does this check but btcec doesn't.
89 | if sig.S.Cmp(secp256k1halfN) > 0 {
90 | return false
91 | }
92 | return sig.Verify(hash, key)
93 | }
94 |
95 | // DecompressPubkey parses a public key in the 33-byte compressed format.
96 | func DecompressPubkey(pubkey []byte) (*ecdsa.PublicKey, error) {
97 | if len(pubkey) != 33 {
98 | return nil, errors.New("invalid compressed public key length")
99 | }
100 | key, err := btcec.ParsePubKey(pubkey, btcec.S256())
101 | if err != nil {
102 | return nil, err
103 | }
104 | return key.ToECDSA(), nil
105 | }
106 |
107 | // CompressPubkey encodes a public key to the 33-byte compressed format.
108 | func CompressPubkey(pubkey *ecdsa.PublicKey) []byte {
109 | return (*btcec.PublicKey)(pubkey).SerializeCompressed()
110 | }
111 |
112 | // S256 returns an instance of the secp256k1 curve.
113 | func S256() elliptic.Curve {
114 | return btcec.S256()
115 | }
116 |
--------------------------------------------------------------------------------
/tools/keystore/aes.go:
--------------------------------------------------------------------------------
1 | // Copyright 2016 The go-ethereum Authors
2 | // This file is part of the go-ethereum library.
3 | //
4 | // The go-ethereum library is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU Lesser General Public License as published by
6 | // the Free Software Foundation, either version 3 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // The go-ethereum library is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU Lesser General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU Lesser General Public License
15 | // along with the go-ethereum library. If not, see .
16 |
17 | package keystore
18 |
19 | import (
20 | "crypto/aes"
21 | "crypto/cipher"
22 | "errors"
23 | )
24 |
25 | var (
26 | // ErrDecrypt decrypt error
27 | ErrDecrypt = errors.New("could not decrypt key with given password")
28 | )
29 |
30 | func aesCTRXOR(key, inText, iv []byte) ([]byte, error) {
31 | // AES-128 is selected due to size of encryptKey.
32 | aesBlock, err := aes.NewCipher(key)
33 | if err != nil {
34 | return nil, err
35 | }
36 | stream := cipher.NewCTR(aesBlock, iv)
37 | outText := make([]byte, len(inText))
38 | stream.XORKeyStream(outText, inText)
39 | return outText, err
40 | }
41 |
42 | func aesCBCDecrypt(key, cipherText, iv []byte) ([]byte, error) {
43 | aesBlock, err := aes.NewCipher(key)
44 | if err != nil {
45 | return nil, err
46 | }
47 | decrypter := cipher.NewCBCDecrypter(aesBlock, iv)
48 | paddedPlaintext := make([]byte, len(cipherText))
49 | decrypter.CryptBlocks(paddedPlaintext, cipherText)
50 | plaintext := pkcs7Unpad(paddedPlaintext)
51 | if plaintext == nil {
52 | return nil, ErrDecrypt
53 | }
54 | return plaintext, err
55 | }
56 |
57 | // From https://leanpub.com/gocrypto/read#leanpub-auto-block-cipher-modes
58 | func pkcs7Unpad(in []byte) []byte {
59 | if len(in) == 0 {
60 | return nil
61 | }
62 |
63 | padding := in[len(in)-1]
64 | if int(padding) > len(in) || padding > aes.BlockSize {
65 | return nil
66 | } else if padding == 0 {
67 | return nil
68 | }
69 |
70 | for i := len(in) - 1; i > len(in)-int(padding)-1; i-- {
71 | if in[i] != padding {
72 | return nil
73 | }
74 | }
75 | return in[:len(in)-int(padding)]
76 | }
77 |
--------------------------------------------------------------------------------
/tools/keystore/key.go:
--------------------------------------------------------------------------------
1 | package keystore
2 |
3 | import (
4 | "crypto/ecdsa"
5 | "encoding/hex"
6 | "encoding/json"
7 |
8 | "github.com/fsn-dev/crossChain-Bridge/common"
9 | "github.com/fsn-dev/crossChain-Bridge/tools/crypto"
10 | "github.com/pborman/uuid"
11 | )
12 |
13 | const (
14 | version = 3
15 | )
16 |
17 | // Key struct
18 | type Key struct {
19 | ID uuid.UUID // Version 4 "random" for unique id not derived from key data
20 | // to simplify lookups we also store the address
21 | Address common.Address
22 | // we only store privkey as pubkey/address can be derived from it
23 | // privkey in this struct is always in plaintext
24 | PrivateKey *ecdsa.PrivateKey
25 | }
26 |
27 | type plainKeyJSON struct {
28 | Address string `json:"address"`
29 | PrivateKey string `json:"privatekey"`
30 | ID string `json:"id"`
31 | Version int `json:"version"`
32 | }
33 |
34 | type encryptedKeyJSONV3 struct {
35 | Address string `json:"address"`
36 | Crypto CryptoJSON `json:"crypto"`
37 | ID string `json:"id"`
38 | Version int `json:"version"`
39 | }
40 |
41 | type encryptedKeyJSONV1 struct {
42 | Address string `json:"address"`
43 | Crypto CryptoJSON `json:"crypto"`
44 | ID string `json:"id"`
45 | Version string `json:"version"`
46 | }
47 |
48 | // CryptoJSON struct
49 | type CryptoJSON struct {
50 | Cipher string `json:"cipher"`
51 | CipherText string `json:"ciphertext"`
52 | CipherParams cipherparamsJSON `json:"cipherparams"`
53 | KDF string `json:"kdf"`
54 | KDFParams map[string]interface{} `json:"kdfparams"`
55 | MAC string `json:"mac"`
56 | }
57 |
58 | type cipherparamsJSON struct {
59 | IV string `json:"iv"`
60 | }
61 |
62 | // MarshalJSON marshal json
63 | func (k *Key) MarshalJSON() (j []byte, err error) {
64 | jStruct := plainKeyJSON{
65 | hex.EncodeToString(k.Address[:]),
66 | hex.EncodeToString(crypto.FromECDSA(k.PrivateKey)),
67 | k.ID.String(),
68 | version,
69 | }
70 | j, err = json.Marshal(jStruct)
71 | return j, err
72 | }
73 |
74 | // UnmarshalJSON unmarshal json
75 | func (k *Key) UnmarshalJSON(j []byte) (err error) {
76 | keyJSON := new(plainKeyJSON)
77 | err = json.Unmarshal(j, &keyJSON)
78 | if err != nil {
79 | return err
80 | }
81 |
82 | u := new(uuid.UUID)
83 | *u = uuid.Parse(keyJSON.ID)
84 | k.ID = *u
85 | addr, err := hex.DecodeString(keyJSON.Address)
86 | if err != nil {
87 | return err
88 | }
89 | privkey, err := crypto.HexToECDSA(keyJSON.PrivateKey)
90 | if err != nil {
91 | return err
92 | }
93 |
94 | k.Address = common.BytesToAddress(addr)
95 | k.PrivateKey = privkey
96 |
97 | return nil
98 | }
99 |
--------------------------------------------------------------------------------
/tools/loadkeystore.go:
--------------------------------------------------------------------------------
1 | package tools
2 |
3 | import (
4 | "fmt"
5 | "io/ioutil"
6 | "strings"
7 |
8 | "github.com/fsn-dev/crossChain-Bridge/tools/keystore"
9 | )
10 |
11 | // LoadKeyStore load keystore from keyfile and passfile
12 | func LoadKeyStore(keyfile, passfile string) (*keystore.Key, error) {
13 | keyjson, err := ioutil.ReadFile(keyfile)
14 | if err != nil {
15 | return nil, fmt.Errorf("read keystore fail %v", err)
16 | }
17 | passdata, err := ioutil.ReadFile(passfile)
18 | if err != nil {
19 | return nil, fmt.Errorf("read password fail %v", err)
20 | }
21 | passwd := strings.TrimSpace(string(passdata))
22 | key, err := keystore.DecryptKey(keyjson, passwd)
23 | if err != nil {
24 | return nil, fmt.Errorf("decrypt key fail %v", err)
25 | }
26 | return key, nil
27 | }
28 |
--------------------------------------------------------------------------------
/tools/rlp/decode_tail_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2015 The go-ethereum Authors
2 | // This file is part of the go-ethereum library.
3 | //
4 | // The go-ethereum library is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU Lesser General Public License as published by
6 | // the Free Software Foundation, either version 3 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // The go-ethereum library is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU Lesser General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU Lesser General Public License
15 | // along with the go-ethereum library. If not, see .
16 |
17 | package rlp
18 |
19 | import (
20 | "bytes"
21 | "fmt"
22 | )
23 |
24 | type structWithTail struct {
25 | A, B uint
26 | C []uint `rlp:"tail"`
27 | }
28 |
29 | func ExampleDecode_structTagTail() {
30 | // In this example, the "tail" struct tag is used to decode lists of
31 | // differing length into a struct.
32 | var val structWithTail
33 |
34 | err := Decode(bytes.NewReader([]byte{0xC4, 0x01, 0x02, 0x03, 0x04}), &val)
35 | fmt.Printf("with 4 elements: err=%v val=%v\n", err, val)
36 |
37 | err = Decode(bytes.NewReader([]byte{0xC6, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06}), &val)
38 | fmt.Printf("with 6 elements: err=%v val=%v\n", err, val)
39 |
40 | // Note that at least two list elements must be present to
41 | // fill fields A and B:
42 | err = Decode(bytes.NewReader([]byte{0xC1, 0x01}), &val)
43 | fmt.Printf("with 1 element: err=%q\n", err)
44 |
45 | // Output:
46 | // with 4 elements: err= val={1 2 [3 4]}
47 | // with 6 elements: err= val={1 2 [3 4 5 6]}
48 | // with 1 element: err="rlp: too few elements for rlp.structWithTail"
49 | }
50 |
--------------------------------------------------------------------------------
/tools/rlp/encoder_example_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2014 The go-ethereum Authors
2 | // This file is part of the go-ethereum library.
3 | //
4 | // The go-ethereum library is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU Lesser General Public License as published by
6 | // the Free Software Foundation, either version 3 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // The go-ethereum library is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU Lesser General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU Lesser General Public License
15 | // along with the go-ethereum library. If not, see .
16 |
17 | package rlp
18 |
19 | import (
20 | "fmt"
21 | "io"
22 | )
23 |
24 | type MyCoolType struct {
25 | Name string
26 | a, b uint
27 | }
28 |
29 | // EncodeRLP writes x as RLP list [a, b] that omits the Name field.
30 | func (x *MyCoolType) EncodeRLP(w io.Writer) (err error) {
31 | return Encode(w, []uint{x.a, x.b})
32 | }
33 |
34 | func ExampleEncoder() {
35 | var t *MyCoolType // t is nil pointer to MyCoolType
36 | bytes, _ := EncodeToBytes(t)
37 | fmt.Printf("%v → %X\n", t, bytes)
38 |
39 | t = &MyCoolType{Name: "foobar", a: 5, b: 6}
40 | bytes, _ = EncodeToBytes(t)
41 | fmt.Printf("%v → %X\n", t, bytes)
42 |
43 | // Output:
44 | // → C0
45 | // &{foobar 5 6} → C20506
46 | }
47 |
--------------------------------------------------------------------------------
/tools/rlp/raw.go:
--------------------------------------------------------------------------------
1 | // Copyright 2015 The go-ethereum Authors
2 | // This file is part of the go-ethereum library.
3 | //
4 | // The go-ethereum library is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU Lesser General Public License as published by
6 | // the Free Software Foundation, either version 3 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // The go-ethereum library is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU Lesser General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU Lesser General Public License
15 | // along with the go-ethereum library. If not, see .
16 |
17 | package rlp
18 |
19 | import (
20 | "io"
21 | "reflect"
22 | )
23 |
24 | // RawValue represents an encoded RLP value and can be used to delay
25 | // RLP decoding or to precompute an encoding. Note that the decoder does
26 | // not verify whether the content of RawValues is valid RLP.
27 | type RawValue []byte
28 |
29 | var rawValueType = reflect.TypeOf(RawValue{})
30 |
31 | // ListSize returns the encoded size of an RLP list with the given
32 | // content size.
33 | func ListSize(contentSize uint64) uint64 {
34 | return uint64(headsize(contentSize)) + contentSize
35 | }
36 |
37 | // Split returns the content of first RLP value and any
38 | // bytes after the value as subslices of b.
39 | func Split(b []byte) (k Kind, content, rest []byte, err error) {
40 | k, ts, cs, err := readKind(b)
41 | if err != nil {
42 | return 0, nil, b, err
43 | }
44 | return k, b[ts : ts+cs], b[ts+cs:], nil
45 | }
46 |
47 | // SplitString splits b into the content of an RLP string
48 | // and any remaining bytes after the string.
49 | func SplitString(b []byte) (content, rest []byte, err error) {
50 | k, content, rest, err := Split(b)
51 | if err != nil {
52 | return nil, b, err
53 | }
54 | if k == List {
55 | return nil, b, ErrExpectedString
56 | }
57 | return content, rest, nil
58 | }
59 |
60 | // SplitList splits b into the content of a list and any remaining
61 | // bytes after the list.
62 | func SplitList(b []byte) (content, rest []byte, err error) {
63 | k, content, rest, err := Split(b)
64 | if err != nil {
65 | return nil, b, err
66 | }
67 | if k != List {
68 | return nil, b, ErrExpectedList
69 | }
70 | return content, rest, nil
71 | }
72 |
73 | // CountValues counts the number of encoded values in b.
74 | func CountValues(b []byte) (int, error) {
75 | i := 0
76 | for ; len(b) > 0; i++ {
77 | _, tagsize, size, err := readKind(b)
78 | if err != nil {
79 | return 0, err
80 | }
81 | b = b[tagsize+size:]
82 | }
83 | return i, nil
84 | }
85 |
86 | func readKind(buf []byte) (k Kind, tagsize, contentsize uint64, err error) {
87 | if len(buf) == 0 {
88 | return 0, 0, 0, io.ErrUnexpectedEOF
89 | }
90 | b := buf[0]
91 | switch {
92 | case b < 0x80:
93 | k = Byte
94 | tagsize = 0
95 | contentsize = 1
96 | case b < 0xB8:
97 | k = String
98 | tagsize = 1
99 | contentsize = uint64(b - 0x80)
100 | // Reject strings that should've been single bytes.
101 | if contentsize == 1 && len(buf) > 1 && buf[1] < 128 {
102 | return 0, 0, 0, ErrCanonSize
103 | }
104 | case b < 0xC0:
105 | k = String
106 | tagsize = uint64(b-0xB7) + 1
107 | contentsize, err = readSize(buf[1:], b-0xB7)
108 | case b < 0xF8:
109 | k = List
110 | tagsize = 1
111 | contentsize = uint64(b - 0xC0)
112 | default:
113 | k = List
114 | tagsize = uint64(b-0xF7) + 1
115 | contentsize, err = readSize(buf[1:], b-0xF7)
116 | }
117 | if err != nil {
118 | return 0, 0, 0, err
119 | }
120 | // Reject values larger than the input slice.
121 | if contentsize > uint64(len(buf))-tagsize {
122 | return 0, 0, 0, ErrValueTooLarge
123 | }
124 | return k, tagsize, contentsize, err
125 | }
126 |
127 | func readSize(b []byte, slen byte) (uint64, error) {
128 | if int(slen) > len(b) {
129 | return 0, io.ErrUnexpectedEOF
130 | }
131 | var s uint64
132 | switch slen {
133 | case 1:
134 | s = uint64(b[0])
135 | case 2:
136 | s = uint64(b[0])<<8 | uint64(b[1])
137 | case 3:
138 | s = uint64(b[0])<<16 | uint64(b[1])<<8 | uint64(b[2])
139 | case 4:
140 | s = uint64(b[0])<<24 | uint64(b[1])<<16 | uint64(b[2])<<8 | uint64(b[3])
141 | case 5:
142 | s = uint64(b[0])<<32 | uint64(b[1])<<24 | uint64(b[2])<<16 | uint64(b[3])<<8 | uint64(b[4])
143 | case 6:
144 | s = uint64(b[0])<<40 | uint64(b[1])<<32 | uint64(b[2])<<24 | uint64(b[3])<<16 | uint64(b[4])<<8 | uint64(b[5])
145 | case 7:
146 | s = uint64(b[0])<<48 | uint64(b[1])<<40 | uint64(b[2])<<32 | uint64(b[3])<<24 | uint64(b[4])<<16 | uint64(b[5])<<8 | uint64(b[6])
147 | case 8:
148 | s = uint64(b[0])<<56 | uint64(b[1])<<48 | uint64(b[2])<<40 | uint64(b[3])<<32 | uint64(b[4])<<24 | uint64(b[5])<<16 | uint64(b[6])<<8 | uint64(b[7])
149 | }
150 | // Reject sizes < 56 (shouldn't have separate size) and sizes with
151 | // leading zero bytes.
152 | if s < 56 || b[0] == 0 {
153 | return 0, ErrCanonSize
154 | }
155 | return s, nil
156 | }
157 |
--------------------------------------------------------------------------------
/types/gen_tx_json.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | "encoding/json"
5 | "errors"
6 | "fmt"
7 | "math/big"
8 |
9 | "github.com/fsn-dev/crossChain-Bridge/common"
10 | "github.com/fsn-dev/crossChain-Bridge/common/hexutil"
11 | "github.com/fsn-dev/crossChain-Bridge/tools/rlp"
12 | )
13 |
14 | // MarshalJSON marshals as JSON.
15 | func (t *txdata) MarshalJSON() ([]byte, error) {
16 | type txdata struct {
17 | AccountNonce hexutil.Uint64 `json:"nonce" gencodec:"required"`
18 | Price *hexutil.Big `json:"gasPrice" gencodec:"required"`
19 | GasLimit hexutil.Uint64 `json:"gas" gencodec:"required"`
20 | Recipient *common.Address `json:"to" rlp:"nil"`
21 | Amount *hexutil.Big `json:"value" gencodec:"required"`
22 | Payload hexutil.Bytes `json:"input" gencodec:"required"`
23 | V *hexutil.Big `json:"v" gencodec:"required"`
24 | R *hexutil.Big `json:"r" gencodec:"required"`
25 | S *hexutil.Big `json:"s" gencodec:"required"`
26 | Hash *common.Hash `json:"hash" rlp:"-"`
27 | }
28 | var enc txdata
29 | enc.AccountNonce = hexutil.Uint64(t.AccountNonce)
30 | enc.Price = (*hexutil.Big)(t.Price)
31 | enc.GasLimit = hexutil.Uint64(t.GasLimit)
32 | enc.Recipient = t.Recipient
33 | enc.Amount = (*hexutil.Big)(t.Amount)
34 | enc.Payload = t.Payload
35 | enc.V = (*hexutil.Big)(t.V)
36 | enc.R = (*hexutil.Big)(t.R)
37 | enc.S = (*hexutil.Big)(t.S)
38 | enc.Hash = t.Hash
39 | return json.Marshal(&enc)
40 | }
41 |
42 | // UnmarshalJSON unmarshals from JSON.
43 | func (t *txdata) UnmarshalJSON(input []byte) error {
44 | type txdata struct {
45 | AccountNonce *hexutil.Uint64 `json:"nonce" gencodec:"required"`
46 | Price *hexutil.Big `json:"gasPrice" gencodec:"required"`
47 | GasLimit *hexutil.Uint64 `json:"gas" gencodec:"required"`
48 | Recipient *common.Address `json:"to" rlp:"nil"`
49 | Amount *hexutil.Big `json:"value" gencodec:"required"`
50 | Payload *hexutil.Bytes `json:"input" gencodec:"required"`
51 | V *hexutil.Big `json:"v" gencodec:"required"`
52 | R *hexutil.Big `json:"r" gencodec:"required"`
53 | S *hexutil.Big `json:"s" gencodec:"required"`
54 | Hash *common.Hash `json:"hash" rlp:"-"`
55 | }
56 | var dec txdata
57 | if err := json.Unmarshal(input, &dec); err != nil {
58 | return err
59 | }
60 | if dec.AccountNonce == nil {
61 | return errors.New("missing required field 'nonce' for txdata")
62 | }
63 | t.AccountNonce = uint64(*dec.AccountNonce)
64 | if dec.Price == nil {
65 | return errors.New("missing required field 'gasPrice' for txdata")
66 | }
67 | t.Price = (*big.Int)(dec.Price)
68 | if dec.GasLimit == nil {
69 | return errors.New("missing required field 'gas' for txdata")
70 | }
71 | t.GasLimit = uint64(*dec.GasLimit)
72 | if dec.Recipient != nil {
73 | t.Recipient = dec.Recipient
74 | }
75 | if dec.Amount == nil {
76 | return errors.New("missing required field 'value' for txdata")
77 | }
78 | t.Amount = (*big.Int)(dec.Amount)
79 | if dec.Payload == nil {
80 | return errors.New("missing required field 'input' for txdata")
81 | }
82 | t.Payload = *dec.Payload
83 | if dec.V == nil {
84 | return errors.New("missing required field 'v' for txdata")
85 | }
86 | t.V = (*big.Int)(dec.V)
87 | if dec.R == nil {
88 | return errors.New("missing required field 'r' for txdata")
89 | }
90 | t.R = (*big.Int)(dec.R)
91 | if dec.S == nil {
92 | return errors.New("missing required field 's' for txdata")
93 | }
94 | t.S = (*big.Int)(dec.S)
95 | if dec.Hash != nil {
96 | t.Hash = dec.Hash
97 | }
98 | return nil
99 | }
100 |
101 | // PrintPretty print pretty (json)
102 | func (tx *Transaction) PrintPretty() {
103 | bs, _ := json.MarshalIndent(tx, "", " ")
104 | fmt.Println(string(bs))
105 | }
106 |
107 | // PrintRaw print raw encoded (hex string)
108 | func (tx *Transaction) PrintRaw() {
109 | bs, _ := rlp.EncodeToBytes(tx)
110 | fmt.Println(hexutil.Bytes(bs))
111 | }
112 |
--------------------------------------------------------------------------------
/types/rpctypes.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | "fmt"
5 | "math/big"
6 |
7 | "github.com/fsn-dev/crossChain-Bridge/common"
8 | "github.com/fsn-dev/crossChain-Bridge/common/hexutil"
9 | )
10 |
11 | // RPCBlock struct
12 | type RPCBlock struct {
13 | Hash *common.Hash `json:"hash"`
14 | ParentHash *common.Hash `json:"parentHash"`
15 | UncleHash *common.Hash `json:"sha3Uncles"`
16 | Coinbase *common.Address `json:"miner"`
17 | Root *common.Hash `json:"stateRoot"`
18 | TxHash *common.Hash `json:"transactionsRoot"`
19 | ReceiptHash *common.Hash `json:"receiptsRoot"`
20 | Bloom *hexutil.Bytes `json:"logsBloom"`
21 | Difficulty *hexutil.Big `json:"difficulty"`
22 | Number *hexutil.Big `json:"number"`
23 | GasLimit *hexutil.Uint64 `json:"gasLimit"`
24 | GasUsed *hexutil.Uint64 `json:"gasUsed"`
25 | Time *hexutil.Big `json:"timestamp"`
26 | Extra *hexutil.Bytes `json:"extraData"`
27 | MixDigest *common.Hash `json:"mixHash"`
28 | Nonce *hexutil.Bytes `json:"nonce"`
29 | Size *string `json:"size"`
30 | TotalDifficulty *hexutil.Big `json:"totalDifficulty"`
31 | Transactions []*common.Hash `json:"transactions"`
32 | Uncles []*common.Hash `json:"uncles"`
33 | }
34 |
35 | // RPCTransaction struct
36 | type RPCTransaction struct {
37 | Hash *common.Hash `json:"hash"`
38 | TransactionIndex *hexutil.Uint `json:"transactionIndex"`
39 | BlockNumber *hexutil.Big `json:"blockNumber,omitempty"`
40 | BlockHash *common.Hash `json:"blockHash,omitempty"`
41 | From *common.Address `json:"from,omitempty"`
42 | AccountNonce *hexutil.Uint64 `json:"nonce"`
43 | Price *hexutil.Big `json:"gasPrice"`
44 | GasLimit *hexutil.Uint64 `json:"gas"`
45 | Recipient *common.Address `json:"to"`
46 | Amount *hexutil.Big `json:"value"`
47 | Payload *hexutil.Bytes `json:"input"`
48 | V *hexutil.Big `json:"v"`
49 | R *hexutil.Big `json:"r"`
50 | S *hexutil.Big `json:"s"`
51 | }
52 |
53 | // RPCLog struct
54 | type RPCLog struct {
55 | Address *common.Address `json:"address"`
56 | Topics []common.Hash `json:"topics"`
57 | Data *hexutil.Bytes `json:"data"`
58 | BlockNumber *hexutil.Uint64 `json:"blockNumber"`
59 | TxHash *common.Hash `json:"transactionHash"`
60 | TxIndex *hexutil.Uint `json:"transactionIndex"`
61 | BlockHash *common.Hash `json:"blockHash"`
62 | Index *hexutil.Uint `json:"logIndex"`
63 | Removed *bool `json:"removed"`
64 | }
65 |
66 | // RPCTxReceipt struct
67 | type RPCTxReceipt struct {
68 | TxHash *common.Hash `json:"transactionHash"`
69 | TxIndex *hexutil.Uint `json:"transactionIndex"`
70 | BlockNumber *hexutil.Big `json:"blockNumber"`
71 | BlockHash *common.Hash `json:"blockHash"`
72 | PostState *hexutil.Bytes `json:"root"`
73 | Status *hexutil.Uint64 `json:"status"`
74 | From *common.Address `json:"from"`
75 | Recipient *common.Address `json:"to"`
76 | GasUsed *hexutil.Uint64 `json:"gasUsed"`
77 | CumulativeGasUsed *hexutil.Uint64 `json:"cumulativeGasUsed"`
78 | ContractAddress *common.Address `json:"contractAddress,omitempty"`
79 | Bloom *hexutil.Bytes `json:"logsBloom"`
80 | FsnLogTopic *string `json:"fsnLogTopic,omitempty"`
81 | FsnLogData interface{} `json:"fsnLogData,omitempty"`
82 | Logs []*RPCLog `json:"logs"`
83 | }
84 |
85 | // RPCTxAndReceipt struct
86 | type RPCTxAndReceipt struct {
87 | FsnTxInput interface{} `json:"fsnTxInput,omitempty"`
88 | Tx *RPCTransaction `json:"tx"`
89 | Receipt *RPCTxReceipt `json:"receipt"`
90 | ReceiptFound *bool `json:"receiptFound"`
91 | }
92 |
93 | // FilterQuery struct
94 | type FilterQuery struct {
95 | BlockHash *common.Hash
96 | FromBlock *big.Int
97 | ToBlock *big.Int
98 | Addresses []common.Address
99 | Topics [][]common.Hash
100 | }
101 |
102 | // ToFilterArg query to filter arg
103 | func ToFilterArg(q *FilterQuery) (interface{}, error) {
104 | arg := map[string]interface{}{
105 | "address": q.Addresses,
106 | "topics": q.Topics,
107 | }
108 | if q.BlockHash != nil {
109 | arg["blockHash"] = *q.BlockHash
110 | if q.FromBlock != nil || q.ToBlock != nil {
111 | return nil, fmt.Errorf("cannot specify both BlockHash and FromBlock/ToBlock")
112 | }
113 | } else {
114 | if q.FromBlock == nil {
115 | arg["fromBlock"] = "0x0"
116 | } else {
117 | arg["fromBlock"] = ToBlockNumArg(q.FromBlock)
118 | }
119 | arg["toBlock"] = ToBlockNumArg(q.ToBlock)
120 | }
121 | return arg, nil
122 | }
123 |
124 | // ToBlockNumArg to block number arg
125 | func ToBlockNumArg(number *big.Int) string {
126 | if number == nil {
127 | return "latest"
128 | }
129 | return hexutil.EncodeBig(number)
130 | }
131 |
--------------------------------------------------------------------------------
/worker/aggregate.go:
--------------------------------------------------------------------------------
1 | package worker
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/fsn-dev/crossChain-Bridge/mongodb"
7 | "github.com/fsn-dev/crossChain-Bridge/tokens"
8 | "github.com/fsn-dev/crossChain-Bridge/tokens/btc"
9 | "github.com/fsn-dev/crossChain-Bridge/tokens/btc/electrs"
10 | )
11 |
12 | var (
13 | utxoPageLimit = 100
14 |
15 | aggSumVal uint64
16 | aggAddrs []string
17 | aggUtxos []*electrs.ElectUtxo
18 | aggOffset int
19 | aggInterval = 300 * time.Second
20 | )
21 |
22 | // StartAggregateJob aggregate job
23 | func StartAggregateJob() {
24 | if btc.BridgeInstance == nil {
25 | return
26 | }
27 |
28 | for loop := 1; ; loop++ {
29 | logWorker("aggregate", "start aggregate job", "loop", loop)
30 | doAggregateJob()
31 | logWorker("aggregate", "finish aggregate job", "loop", loop)
32 | time.Sleep(aggInterval)
33 | }
34 | }
35 |
36 | func doAggregateJob() {
37 | aggOffset = 0
38 | for {
39 | p2shAddrs, err := mongodb.FindP2shAddresses(aggOffset, utxoPageLimit)
40 | if err != nil {
41 | logWorkerError("aggregate", "FindP2shAddresses failed", err, "offset", aggOffset, "limit", utxoPageLimit)
42 | time.Sleep(3 * time.Second)
43 | continue
44 | }
45 | for _, p2shAddr := range p2shAddrs {
46 | findUtxosAndAggregate(p2shAddr.P2shAddress)
47 | }
48 | if len(p2shAddrs) < utxoPageLimit {
49 | break
50 | }
51 | aggOffset += utxoPageLimit
52 | }
53 | }
54 |
55 | func findUtxosAndAggregate(addr string) {
56 | findUtxos, _ := btc.BridgeInstance.FindUtxos(addr)
57 | for _, utxo := range findUtxos {
58 | if utxo.Value == nil || *utxo.Value == 0 {
59 | continue
60 | }
61 | logWorker("aggregate", "find utxo", "address", addr, "utxo", utxo.String())
62 |
63 | aggSumVal += *utxo.Value
64 | aggAddrs = append(aggAddrs, addr)
65 | aggUtxos = append(aggUtxos, utxo)
66 |
67 | if shouldAggregate() {
68 | aggregate()
69 | }
70 | }
71 | }
72 |
73 | func shouldAggregate() bool {
74 | if len(aggUtxos) >= tokens.BtcUtxoAggregateMinCount {
75 | return true
76 | }
77 | if aggSumVal >= tokens.BtcUtxoAggregateMinValue {
78 | return true
79 | }
80 | return false
81 | }
82 |
83 | func aggregate() {
84 | txHash, err := btc.BridgeInstance.AggregateUtxos(aggAddrs, aggUtxos)
85 | if err != nil {
86 | logWorkerError("aggregate", "AggregateUtxos failed", err)
87 | } else {
88 | logWorker("aggregate", "AggregateUtxos succeed", "txHash", txHash, "utxos", len(aggUtxos), "sumVal", aggSumVal)
89 | }
90 | aggSumVal = 0
91 | aggAddrs = nil
92 | aggUtxos = nil
93 | }
94 |
--------------------------------------------------------------------------------
/worker/common.go:
--------------------------------------------------------------------------------
1 | package worker
2 |
3 | import (
4 | "github.com/fsn-dev/crossChain-Bridge/mongodb"
5 | "github.com/fsn-dev/crossChain-Bridge/tokens"
6 | )
7 |
8 | // MatchTx struct
9 | type MatchTx struct {
10 | SwapTx string
11 | SwapHeight uint64
12 | SwapTime uint64
13 | SwapValue string
14 | SwapType tokens.SwapType
15 | }
16 |
17 | func addInitialSwapinResult(tx *tokens.TxSwapInfo, status mongodb.SwapStatus) error {
18 | return addInitialSwapResult(tx, status, true)
19 | }
20 |
21 | func addInitialSwapoutResult(tx *tokens.TxSwapInfo, status mongodb.SwapStatus) error {
22 | return addInitialSwapResult(tx, status, false)
23 | }
24 |
25 | func addInitialSwapResult(tx *tokens.TxSwapInfo, status mongodb.SwapStatus, isSwapin bool) (err error) {
26 | txid := tx.Hash
27 | var swapType tokens.SwapType
28 | if isSwapin {
29 | swapType = tokens.SwapinType
30 | } else {
31 | swapType = tokens.SwapoutType
32 | }
33 | swapResult := &mongodb.MgoSwapResult{
34 | Key: txid,
35 | TxID: txid,
36 | TxHeight: tx.Height,
37 | TxTime: tx.Timestamp,
38 | From: tx.From,
39 | To: tx.To,
40 | Bind: tx.Bind,
41 | Value: tx.Value.String(),
42 | SwapTx: "",
43 | SwapHeight: 0,
44 | SwapTime: 0,
45 | SwapValue: "0",
46 | SwapType: uint32(swapType),
47 | Status: status,
48 | Timestamp: now(),
49 | Memo: "",
50 | }
51 | if isSwapin {
52 | err = mongodb.AddSwapinResult(swapResult)
53 | } else {
54 | err = mongodb.AddSwapoutResult(swapResult)
55 | }
56 | if err != nil {
57 | logWorkerError("add", "addInitialSwapResult", err, "txid", txid)
58 | } else {
59 | logWorker("add", "addInitialSwapResult", "txid", txid)
60 | }
61 | return err
62 | }
63 |
64 | func updateSwapinResult(key string, mtx *MatchTx) error {
65 | return updateSwapResult(key, mtx)
66 | }
67 |
68 | func updateSwapoutResult(key string, mtx *MatchTx) error {
69 | return updateSwapResult(key, mtx)
70 | }
71 |
72 | func updateSwapResult(key string, mtx *MatchTx) (err error) {
73 | updates := &mongodb.SwapResultUpdateItems{
74 | Status: mongodb.MatchTxNotStable,
75 | Timestamp: now(),
76 | }
77 | if mtx.SwapTx != "" {
78 | updates.SwapTx = mtx.SwapTx
79 | updates.SwapValue = mtx.SwapValue
80 | updates.SwapHeight = 0
81 | updates.SwapTime = 0
82 | } else {
83 | updates.SwapHeight = mtx.SwapHeight
84 | updates.SwapTime = mtx.SwapTime
85 | }
86 | switch mtx.SwapType {
87 | case tokens.SwapRecallType:
88 | updates.SwapType = uint32(mtx.SwapType)
89 | fallthrough
90 | case tokens.SwapinType:
91 | err = mongodb.UpdateSwapinResult(key, updates)
92 | case tokens.SwapoutType:
93 | err = mongodb.UpdateSwapoutResult(key, updates)
94 | default:
95 | err = tokens.ErrUnknownSwapType
96 | }
97 | if err != nil {
98 | logWorkerError("update", "updateSwapResult", err, "txid", key, "swaptx", mtx.SwapTx, "swapheight", mtx.SwapHeight, "swaptime", mtx.SwapTime, "swapvalue", mtx.SwapValue, "swaptype", mtx.SwapType)
99 | } else {
100 | logWorker("update", "updateSwapResult", "txid", key, "swaptx", mtx.SwapTx, "swapheight", mtx.SwapHeight, "swaptime", mtx.SwapTime, "swapvalue", mtx.SwapValue, "swaptype", mtx.SwapType)
101 | }
102 | return err
103 | }
104 |
105 | func markSwapinResultStable(key string) error {
106 | return markSwapResultStable(key, true)
107 | }
108 |
109 | func markSwapoutResultStable(key string) error {
110 | return markSwapResultStable(key, false)
111 | }
112 |
113 | func markSwapResultStable(key string, isSwapin bool) (err error) {
114 | status := mongodb.MatchTxStable
115 | timestamp := now()
116 | memo := "" // unchange
117 | if isSwapin {
118 | err = mongodb.UpdateSwapinResultStatus(key, status, timestamp, memo)
119 | } else {
120 | err = mongodb.UpdateSwapoutResultStatus(key, status, timestamp, memo)
121 | }
122 | if err != nil {
123 | logWorkerError("stable", "markSwapResultStable", err, "txid", key, "isSwapin", isSwapin)
124 | } else {
125 | logWorker("stable", "markSwapResultStable", "txid", key, "isSwapin", isSwapin)
126 | }
127 | return err
128 | }
129 |
--------------------------------------------------------------------------------
/worker/recall.go:
--------------------------------------------------------------------------------
1 | package worker
2 |
3 | import (
4 | "fmt"
5 | "sync"
6 | "time"
7 |
8 | "github.com/fsn-dev/crossChain-Bridge/common"
9 | "github.com/fsn-dev/crossChain-Bridge/mongodb"
10 | "github.com/fsn-dev/crossChain-Bridge/tokens"
11 | )
12 |
13 | var (
14 | swapinRecallStarter sync.Once
15 | )
16 |
17 | // StartRecallJob recall job
18 | func StartRecallJob() {
19 | go startSwapinRecallJob()
20 | }
21 |
22 | func startSwapinRecallJob() {
23 | swapinRecallStarter.Do(func() {
24 | logWorker("recall", "start swapin recall job")
25 | for {
26 | res, err := findSwapinsToRecall()
27 | if err != nil {
28 | logWorkerError("recall", "find recalls error", err)
29 | }
30 | if len(res) > 0 {
31 | logWorker("recall", "find recalls to recall", "count", len(res))
32 | }
33 | for _, swap := range res {
34 | err = processRecallSwapin(swap)
35 | if err != nil {
36 | logWorkerError("recall", "process recall error", err, "txid", swap.TxID)
37 | }
38 | }
39 | restInJob(restIntervalInRecallJob)
40 | }
41 | })
42 | }
43 |
44 | func findSwapinsToRecall() ([]*mongodb.MgoSwap, error) {
45 | status := mongodb.TxToBeRecall
46 | septime := getSepTimeInFind(maxRecallLifetime)
47 | return mongodb.FindSwapinsWithStatus(status, septime)
48 | }
49 |
50 | func processRecallSwapin(swap *mongodb.MgoSwap) (err error) {
51 | txid := swap.TxID
52 | res, err := mongodb.FindSwapinResult(txid)
53 | if err != nil {
54 | return err
55 | }
56 | if res.SwapTx != "" {
57 | if res.Status == mongodb.TxToBeRecall {
58 | _ = mongodb.UpdateSwapinStatus(txid, mongodb.TxProcessed, now(), "")
59 | }
60 | return fmt.Errorf("%v already swapped to %v", txid, res.SwapTx)
61 | }
62 |
63 | value, err := common.GetBigIntFromStr(res.Value)
64 | if err != nil {
65 | return fmt.Errorf("wrong value %v", res.Value)
66 | }
67 |
68 | args := &tokens.BuildTxArgs{
69 | SwapInfo: tokens.SwapInfo{
70 | SwapID: res.TxID,
71 | SwapType: tokens.SwapRecallType,
72 | TxType: tokens.SwapTxType(swap.TxType),
73 | Bind: swap.Bind,
74 | },
75 | To: res.Bind,
76 | Value: value,
77 | Memo: fmt.Sprintf("%s%s", tokens.RecallMemoPrefix, res.TxID),
78 | }
79 | bridge := tokens.SrcBridge
80 | rawTx, err := bridge.BuildRawTransaction(args)
81 | if err != nil {
82 | logWorkerError("recall", "BuildRawTransaction failed", err, "txid", txid)
83 | return err
84 | }
85 |
86 | signedTx, txHash, err := bridge.DcrmSignTransaction(rawTx, args.GetExtraArgs())
87 | if err != nil {
88 | logWorkerError("recall", "DcrmSignTransaction failed", err, "txid", txid)
89 | return err
90 | }
91 |
92 | // update database before sending transaction
93 | matchTx := &MatchTx{
94 | SwapTx: txHash,
95 | SwapValue: tokens.CalcSwappedValue(value, false).String(),
96 | SwapType: tokens.SwapRecallType,
97 | }
98 | err = updateSwapinResult(txid, matchTx)
99 | if err != nil {
100 | return err
101 | }
102 | err = mongodb.UpdateSwapinStatus(txid, mongodb.TxProcessed, now(), "")
103 | if err != nil {
104 | return err
105 | }
106 |
107 | for i := 0; i < retrySendTxCount; i++ {
108 | if _, err = bridge.SendTransaction(signedTx); err == nil {
109 | if tx, _ := bridge.GetTransaction(txHash); tx != nil {
110 | break
111 | }
112 | }
113 | time.Sleep(retrySendTxInterval)
114 | }
115 | if err != nil {
116 | _ = mongodb.UpdateSwapinStatus(txid, mongodb.TxRecallFailed, now(), err.Error())
117 | _ = mongodb.UpdateSwapinResultStatus(txid, mongodb.TxRecallFailed, now(), err.Error())
118 | return err
119 | }
120 | return nil
121 | }
122 |
--------------------------------------------------------------------------------
/worker/scan.go:
--------------------------------------------------------------------------------
1 | package worker
2 |
3 | import (
4 | "github.com/fsn-dev/crossChain-Bridge/tokens"
5 | )
6 |
7 | // StartScanJob scan job
8 | func StartScanJob(isServer bool) {
9 | go tokens.SrcBridge.StartPoolTransactionScanJob()
10 | go tokens.SrcBridge.StartChainTransactionScanJob()
11 | go tokens.SrcBridge.StartSwapHistoryScanJob()
12 |
13 | go tokens.DstBridge.StartPoolTransactionScanJob()
14 | go tokens.DstBridge.StartChainTransactionScanJob()
15 | go tokens.DstBridge.StartSwapHistoryScanJob()
16 | }
17 |
--------------------------------------------------------------------------------
/worker/stable.go:
--------------------------------------------------------------------------------
1 | package worker
2 |
3 | import (
4 | "fmt"
5 | "sync"
6 |
7 | "github.com/fsn-dev/crossChain-Bridge/mongodb"
8 | "github.com/fsn-dev/crossChain-Bridge/tokens"
9 | )
10 |
11 | var (
12 | swapinStableStarter sync.Once
13 | swapoutStableStarter sync.Once
14 | )
15 |
16 | // StartStableJob stable job
17 | func StartStableJob() {
18 | go startSwapinStableJob()
19 | go startSwapoutStableJob()
20 | }
21 |
22 | func startSwapinStableJob() {
23 | swapinStableStarter.Do(func() {
24 | logWorker("stable", "start update swapin stable job")
25 | for {
26 | res, err := findSwapinResultsToStable()
27 | if err != nil {
28 | logWorkerError("stable", "find swapin results error", err)
29 | }
30 | if len(res) > 0 {
31 | logWorker("stable", "find swapin results to stable", "count", len(res))
32 | }
33 | for _, swap := range res {
34 | err = processSwapinStable(swap)
35 | if err != nil {
36 | logWorkerError("stable", "process swapin stable error", err)
37 | }
38 | }
39 | restInJob(restIntervalInStableJob)
40 | }
41 | })
42 | }
43 |
44 | func startSwapoutStableJob() {
45 | swapoutStableStarter.Do(func() {
46 | logWorker("stable", "start update swapout stable job")
47 | for {
48 | res, err := findSwapoutResultsToStable()
49 | if err != nil {
50 | logWorkerError("stable", "find swapout results error", err)
51 | }
52 | if len(res) > 0 {
53 | logWorker("stable", "find swapout results to stable", "count", len(res))
54 | }
55 | for _, swap := range res {
56 | err = processSwapoutStable(swap)
57 | if err != nil {
58 | logWorkerError("recall", "process swapout stable error", err)
59 | }
60 | }
61 | restInJob(restIntervalInStableJob)
62 | }
63 | })
64 | }
65 |
66 | func findSwapinResultsToStable() ([]*mongodb.MgoSwapResult, error) {
67 | status := mongodb.MatchTxNotStable
68 | septime := getSepTimeInFind(maxStableLifetime)
69 | return mongodb.FindSwapinResultsWithStatus(status, septime)
70 | }
71 |
72 | func findSwapoutResultsToStable() ([]*mongodb.MgoSwapResult, error) {
73 | status := mongodb.MatchTxNotStable
74 | septime := getSepTimeInFind(maxStableLifetime)
75 | return mongodb.FindSwapoutResultsWithStatus(status, septime)
76 | }
77 |
78 | func processSwapinStable(swap *mongodb.MgoSwapResult) error {
79 | swapTxID := swap.SwapTx
80 | logWorker("stable", "start processSwapinStable", "swaptxid", swapTxID, "status", swap.Status)
81 | var (
82 | txStatus *tokens.TxStatus
83 | confirmations uint64
84 | )
85 | if swap.SwapType == uint32(tokens.SwapRecallType) {
86 | txStatus = tokens.SrcBridge.GetTransactionStatus(swapTxID)
87 | token, _ := tokens.SrcBridge.GetTokenAndGateway()
88 | confirmations = *token.Confirmations
89 | } else {
90 | txStatus = tokens.DstBridge.GetTransactionStatus(swapTxID)
91 | token, _ := tokens.DstBridge.GetTokenAndGateway()
92 | confirmations = *token.Confirmations
93 | }
94 |
95 | if txStatus == nil {
96 | return fmt.Errorf("[processSwapinStable] tx status is empty, swapTxID=%v", swapTxID)
97 | }
98 |
99 | if txStatus.BlockHeight == 0 {
100 | return nil
101 | }
102 |
103 | if swap.SwapHeight != 0 {
104 | if txStatus.Confirmations >= confirmations {
105 | return markSwapinResultStable(swap.Key)
106 | }
107 | return nil
108 | }
109 |
110 | matchTx := &MatchTx{
111 | SwapHeight: txStatus.BlockHeight,
112 | SwapTime: txStatus.BlockTime,
113 | SwapType: tokens.SwapinType,
114 | }
115 | return updateSwapinResult(swap.Key, matchTx)
116 | }
117 |
118 | func processSwapoutStable(swap *mongodb.MgoSwapResult) (err error) {
119 | swapTxID := swap.SwapTx
120 | logWorker("stable", "start processSwapoutStable", "swaptxid", swapTxID, "status", swap.Status)
121 |
122 | var txStatus *tokens.TxStatus
123 | var confirmations uint64
124 |
125 | txStatus = tokens.SrcBridge.GetTransactionStatus(swapTxID)
126 | token, _ := tokens.SrcBridge.GetTokenAndGateway()
127 | confirmations = *token.Confirmations
128 |
129 | if txStatus == nil {
130 | return fmt.Errorf("[processSwapoutStable] tx status is empty, swapTxID=%v", swapTxID)
131 | }
132 |
133 | if txStatus.BlockHeight == 0 {
134 | return nil
135 | }
136 |
137 | if swap.SwapHeight != 0 {
138 | if txStatus.Confirmations >= confirmations {
139 | return markSwapoutResultStable(swap.Key)
140 | }
141 | return nil
142 | }
143 |
144 | matchTx := &MatchTx{
145 | SwapHeight: txStatus.BlockHeight,
146 | SwapTime: txStatus.BlockTime,
147 | SwapType: tokens.SwapoutType,
148 | }
149 | return updateSwapoutResult(swap.Key, matchTx)
150 | }
151 |
--------------------------------------------------------------------------------
/worker/updatelatest.go:
--------------------------------------------------------------------------------
1 | package worker
2 |
3 | import (
4 | "sync"
5 | "time"
6 |
7 | "github.com/fsn-dev/crossChain-Bridge/tokens"
8 | )
9 |
10 | var (
11 | updateLatestBlockHeightStarter sync.Once
12 | updateLatestBlockHeightInterval = 5 * time.Second
13 | )
14 |
15 | // StartUpdateLatestBlockHeightJob update latest block height job
16 | func StartUpdateLatestBlockHeightJob() {
17 | updateLatestBlockHeightStarter.Do(func() {
18 | logWorker("updatelatest", "start update latest block height job")
19 | for {
20 | updateSrcLatestBlockHeight()
21 | updateDstLatestBlockHeight()
22 | time.Sleep(updateLatestBlockHeightInterval)
23 | }
24 | })
25 | }
26 |
27 | func updateSrcLatestBlockHeight() {
28 | srcLatest, err := tokens.SrcBridge.GetLatestBlockNumber()
29 | if err != nil {
30 | logWorkerError("updatelatest", "get src latest block number error", err)
31 | return
32 | }
33 | if tokens.SrcLatestBlockHeight != srcLatest {
34 | tokens.SrcLatestBlockHeight = srcLatest
35 | logWorker("updatelatest", "update src latest block number", "latest", srcLatest)
36 | }
37 | }
38 |
39 | func updateDstLatestBlockHeight() {
40 | dstLatest, err := tokens.DstBridge.GetLatestBlockNumber()
41 | if err != nil {
42 | logWorkerError("updatelatest", "get dest latest block number error", err)
43 | return
44 | }
45 | if tokens.DstLatestBlockHeight != dstLatest {
46 | tokens.DstLatestBlockHeight = dstLatest
47 | logWorker("updatelatest", "update dest latest block number", "latest", dstLatest)
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/worker/utils.go:
--------------------------------------------------------------------------------
1 | package worker
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/fsn-dev/crossChain-Bridge/log"
7 | )
8 |
9 | var (
10 | maxRecallLifetime = int64(10 * 24 * 3600)
11 | restIntervalInRecallJob = 3 * time.Second
12 |
13 | maxVerifyLifetime = int64(7 * 24 * 3600)
14 | restIntervalInVerifyJob = 3 * time.Second
15 |
16 | maxDoSwapLifetime = int64(7 * 24 * 3600)
17 | restIntervalInDoSwapJob = 3 * time.Second
18 |
19 | maxStableLifetime = int64(7 * 24 * 3600)
20 | restIntervalInStableJob = 3 * time.Second
21 |
22 | retrySendTxCount = 3
23 | retrySendTxInterval = 1 * time.Second
24 | )
25 |
26 | func now() int64 {
27 | return time.Now().Unix()
28 | }
29 |
30 | func logWorker(job, subject string, context ...interface{}) {
31 | log.Info("["+job+"] "+subject, context...)
32 | }
33 |
34 | func logWorkerError(job, subject string, err error, context ...interface{}) {
35 | fields := []interface{}{"err", err}
36 | fields = append(fields, context...)
37 | log.Error("["+job+"] "+subject, fields...)
38 | }
39 |
40 | func logWorkerTrace(job, subject string, context ...interface{}) {
41 | log.Trace("["+job+"] "+subject, context...)
42 | }
43 |
44 | func getSepTimeInFind(dist int64) int64 {
45 | return now() - dist
46 | }
47 |
48 | func restInJob(duration time.Duration) {
49 | time.Sleep(duration)
50 | }
51 |
--------------------------------------------------------------------------------
/worker/verify.go:
--------------------------------------------------------------------------------
1 | package worker
2 |
3 | import (
4 | "sync"
5 |
6 | "github.com/fsn-dev/crossChain-Bridge/mongodb"
7 | "github.com/fsn-dev/crossChain-Bridge/tokens"
8 | "github.com/fsn-dev/crossChain-Bridge/tokens/btc"
9 | )
10 |
11 | var (
12 | swapinVerifyStarter sync.Once
13 | swapoutVerifyStarter sync.Once
14 | )
15 |
16 | // StartVerifyJob verify job
17 | func StartVerifyJob() {
18 | go startSwapinVerifyJob()
19 | go startSwapoutVerifyJob()
20 | }
21 |
22 | func startSwapinVerifyJob() {
23 | swapinVerifyStarter.Do(func() {
24 | logWorker("verify", "start swapin verify job")
25 | for {
26 | res, err := findSwapinsToVerify()
27 | if err != nil {
28 | logWorkerError("verify", "find swapins error", err)
29 | }
30 | if len(res) > 0 {
31 | logWorker("verify", "find swapins to verify", "count", len(res))
32 | }
33 | for _, swap := range res {
34 | err = processSwapinVerify(swap)
35 | switch err {
36 | case nil, tokens.ErrTxNotStable, tokens.ErrTxNotFound:
37 | default:
38 | logWorkerError("verify", "process swapin verify error", err, "txid", swap.TxID)
39 | }
40 | }
41 | restInJob(restIntervalInVerifyJob)
42 | }
43 | })
44 | }
45 |
46 | func startSwapoutVerifyJob() {
47 | swapoutVerifyStarter.Do(func() {
48 | logWorker("verify", "start swapout verify job")
49 | for {
50 | res, err := findSwapoutsToVerify()
51 | if err != nil {
52 | logWorkerError("verify", "find swapouts error", err)
53 | }
54 | if len(res) > 0 {
55 | logWorker("verify", "find swapouts to verify", "count", len(res))
56 | }
57 | for _, swap := range res {
58 | err = processSwapoutVerify(swap)
59 | switch err {
60 | case nil, tokens.ErrTxNotStable, tokens.ErrTxNotFound:
61 | default:
62 | logWorkerError("verify", "process swapout verify error", err, "txid", swap.TxID)
63 | }
64 | }
65 | restInJob(restIntervalInVerifyJob)
66 | }
67 | })
68 | }
69 |
70 | func findSwapinsToVerify() ([]*mongodb.MgoSwap, error) {
71 | status := mongodb.TxNotStable
72 | septime := getSepTimeInFind(maxVerifyLifetime)
73 | return mongodb.FindSwapinsWithStatus(status, septime)
74 | }
75 |
76 | func findSwapoutsToVerify() ([]*mongodb.MgoSwap, error) {
77 | status := mongodb.TxNotStable
78 | septime := getSepTimeInFind(maxVerifyLifetime)
79 | return mongodb.FindSwapoutsWithStatus(status, septime)
80 | }
81 |
82 | func processSwapinVerify(swap *mongodb.MgoSwap) (err error) {
83 | txid := swap.TxID
84 | var swapInfo *tokens.TxSwapInfo
85 | switch tokens.SwapTxType(swap.TxType) {
86 | case tokens.SwapinTx:
87 | swapInfo, err = tokens.SrcBridge.VerifyTransaction(txid, false)
88 | case tokens.P2shSwapinTx:
89 | if btc.BridgeInstance == nil {
90 | return tokens.ErrWrongP2shSwapin
91 | }
92 | swapInfo, err = btc.BridgeInstance.VerifyP2shTransaction(txid, swap.Bind, false)
93 | default:
94 | return tokens.ErrWrongSwapinTxType
95 | }
96 | if swapInfo.Height != 0 &&
97 | swapInfo.Height < tokens.GetTokenConfig(true).InitialHeight {
98 | err = tokens.ErrTxBeforeInitialHeight
99 | }
100 |
101 | resultStatus := mongodb.MatchTxEmpty
102 |
103 | switch err {
104 | case tokens.ErrTxNotStable, tokens.ErrTxNotFound:
105 | return err
106 | case tokens.ErrTxWithWrongMemo:
107 | resultStatus = mongodb.TxWithWrongMemo
108 | err = mongodb.UpdateSwapinStatus(txid, mongodb.TxCanRecall, now(), err.Error())
109 | case nil:
110 | err = mongodb.UpdateSwapinStatus(txid, mongodb.TxNotSwapped, now(), "")
111 | default:
112 | return mongodb.UpdateSwapinStatus(txid, mongodb.TxVerifyFailed, now(), err.Error())
113 | }
114 |
115 | if err != nil {
116 | logWorkerError("verify", "processSwapinVerify", err, "txid", txid)
117 | return err
118 | }
119 | return addInitialSwapinResult(swapInfo, resultStatus)
120 | }
121 |
122 | func processSwapoutVerify(swap *mongodb.MgoSwap) error {
123 | txid := swap.TxID
124 | swapInfo, err := tokens.DstBridge.VerifyTransaction(txid, false)
125 | if swapInfo.Height != 0 &&
126 | swapInfo.Height < tokens.GetTokenConfig(false).InitialHeight {
127 | err = tokens.ErrTxBeforeInitialHeight
128 | }
129 |
130 | resultStatus := mongodb.MatchTxEmpty
131 |
132 | switch err {
133 | case tokens.ErrTxNotStable, tokens.ErrTxNotFound:
134 | return err
135 | case tokens.ErrTxWithWrongMemo:
136 | resultStatus = mongodb.TxWithWrongMemo
137 | err = mongodb.UpdateSwapoutStatus(txid, mongodb.TxCanRecall, now(), err.Error())
138 | case nil:
139 | err = mongodb.UpdateSwapoutStatus(txid, mongodb.TxNotSwapped, now(), "")
140 | default:
141 | return mongodb.UpdateSwapoutStatus(txid, mongodb.TxVerifyFailed, now(), err.Error())
142 | }
143 |
144 | if err != nil {
145 | logWorkerError("verify", "processSwapoutVerify", err, "txid", txid)
146 | return err
147 | }
148 | return addInitialSwapoutResult(swapInfo, resultStatus)
149 | }
150 |
--------------------------------------------------------------------------------
/worker/worker.go:
--------------------------------------------------------------------------------
1 | package worker
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/fsn-dev/crossChain-Bridge/rpc/client"
7 | "github.com/fsn-dev/crossChain-Bridge/tokens/bridge"
8 | )
9 |
10 | const interval = 10 * time.Millisecond
11 |
12 | // StartWork start swap server work
13 | func StartWork(isServer bool) {
14 | logWorker("worker", "start server worker")
15 |
16 | client.InitHTTPClient()
17 | bridge.InitCrossChainBridge(isServer)
18 |
19 | go StartScanJob(isServer)
20 | time.Sleep(interval)
21 |
22 | if !isServer {
23 | go StartAcceptSignJob()
24 | return
25 | }
26 |
27 | go StartUpdateLatestBlockHeightJob()
28 | time.Sleep(interval)
29 |
30 | go StartVerifyJob()
31 | time.Sleep(interval)
32 |
33 | go StartSwapJob()
34 | time.Sleep(interval)
35 |
36 | go StartStableJob()
37 | time.Sleep(interval)
38 |
39 | go StartAggregateJob()
40 | }
41 |
--------------------------------------------------------------------------------