Unverified Commit 4b5cd0a0 authored by vipwzw's avatar vipwzw Committed by GitHub

Merge pull request #849 from zhengjunhe/x2eth_200507

X2eth 200507
parents a3abe8fc 42c84150
......@@ -42,6 +42,12 @@ function down() {
docker-compose down --rmi local
fi
# shellcheck disable=SC2155
local isGanacheExit=$(docker ps | grep ganachetest)
if [[ ${isGanacheExit} != "" ]]; then
docker stop ganachetest
docker rm ganachetest
fi
}
# run script
......
......@@ -73,7 +73,6 @@ echo "CLI=$CLI"
####################
function base_init() {
# update test environment
sed -i $sedfix 's/^Title.*/Title="local"/g' chain33.toml
sed -i $sedfix 's/^TestNet=.*/TestNet=true/g' chain33.toml
......@@ -116,6 +115,11 @@ function base_init() {
#autonomy
sed -i $sedfix 's/^useBalance=.*/useBalance=true/g' chain33.toml
sed -i $sedfix 's/^total="16htvcBNS.*/total="1Q9sQwothzM1gKSzkVZ8Dt1tqKX1uzSagx"/g' chain33.toml
if [ "$DAPP" == "x2ethereum" ]; then
sed -i $sedfix 's/^enableReduceLocaldb=.*/enableReduceLocaldb=false/g' chain33.toml
sed -i $sedfix 's/^enablePushSubscribe=.*/enablePushSubscribe=true/g' chain33.toml
fi
}
function start() {
......
......@@ -7,19 +7,25 @@ require (
github.com/BurntSushi/toml v0.3.1
github.com/NebulousLabs/Sia v1.3.7
github.com/beorn7/perks v1.0.1 // indirect
github.com/bitly/go-simplejson v0.5.0
github.com/btcsuite/btcd v0.20.1-beta
github.com/coreos/etcd v3.3.15+incompatible
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f // indirect
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect
github.com/davecgh/go-spew v1.1.1
github.com/ethereum/go-ethereum v1.9.9
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
github.com/golang/protobuf v1.3.4
github.com/hashicorp/golang-lru v0.5.3
github.com/huin/goupnp v1.0.0
github.com/jackpal/go-nat-pmp v1.0.1
github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458
github.com/miguelmota/go-solidity-sha3 v0.1.0
github.com/mr-tron/base58 v1.1.3
github.com/pborman/uuid v1.2.0
github.com/pkg/errors v0.8.1
github.com/prometheus/client_golang v0.9.2 // indirect
github.com/prometheus/common v0.4.1 // indirect
github.com/prometheus/common v0.4.1
github.com/prometheus/procfs v0.0.3 // indirect
github.com/robertkrimen/otto v0.0.0-20180617131154-15f95af6e78d
github.com/rs/cors v1.6.0
......@@ -35,5 +41,4 @@ require (
golang.org/x/net v0.0.0-20200301022130-244492dfa37a
golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0 // indirect
google.golang.org/grpc v1.28.0
gopkg.in/sourcemap.v1 v1.0.5 // indirect
)
This diff is collapsed.
......@@ -30,4 +30,5 @@ import (
_ "github.com/33cn/plugin/plugin/dapp/trade" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/unfreeze" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/valnode" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/x2ethereum" //auto gen
)
# golang1.12 or latest
# 1. make help
# 2. make dep
# 3. make build
# ...
SRC_EBCLI := github.com/33cn/plugin/plugin/dapp/x2ethereum/ebcli
SRC_EBRELAYER := github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer
CLI_A := build/ebcli_A
CLI_B := build/ebcli_B
CLI_C := build/ebcli_C
CLI_D := build/ebcli_D
EBRELAER := build/ebrelayer ##通过配置文件启动不同的ebrelayer
LDFLAGS := -ldflags "-w -s"
proj := "build"
.PHONY: default dep all build release cli linter race test fmt vet bench msan coverage coverhtml docker docker-compose protobuf clean help autotest
default: build
build:
@go build -v -i -o $(EBRELAER) $(SRC_EBRELAYER)
@go build -v -i -o $(CLI_A) $(SRC_EBCLI)
@go build -v -i -o $(CLI_B) -ldflags "-X $(SRC_EBCLI)/buildflags.RPCAddr=http://localhost:9902" $(SRC_EBCLI)
@go build -v -i -o $(CLI_C) -ldflags "-X $(SRC_EBCLI)/buildflags.RPCAddr=http://localhost:9903" $(SRC_EBCLI)
@go build -v -i -o $(CLI_D) -ldflags "-X $(SRC_EBCLI)/buildflags.RPCAddr=http://localhost:9904" $(SRC_EBCLI)
@cp ebrelayer/relayer.toml build/
rebuild:
make -C ebrelayer/ethcontract
make build
cli:
@go build -v -i -o $(CLI_A) $(SRC_EBCLI)
@go build -v -i -o $(CLI_B) -ldflags "-X $(SRC_EBCLI)/buildflags.RPCAddr=http://localhost:9902" $(SRC_EBCLI)
@go build -v -i -o $(CLI_C) -ldflags "-X $(SRC_EBCLI)/buildflags.RPCAddr=http://localhost:9903" $(SRC_EBCLI)
@go build -v -i -o $(CLI_D) -ldflags "-X $(SRC_EBCLI)/buildflags.RPCAddr=http://localhost:9904" $(SRC_EBCLI)
build_ci: depends ## Build the binary file for CI
@go build -v -i -o $(CLI) $(SRC_EBCLI)
@go build $(BUILD_FLAGS) -v -o $(APP)
@cp chain33.toml build/
para:
@go build -v -o build/$(NAME) -ldflags "-X $(SRC_EBCLI)/buildflags.ParaName=user.p.$(NAME). -X $(SRC_EBCLI)/buildflags.RPCAddr=http://localhost:8901" $(SRC_EBCLI)
vet:
@go vet ${PKG_LIST_VET}
race: ## Run data race detector
@go test -race -short $(PKG_LIST)
test: ## Run unittests
@go test -race $(PKG_LIST)
fmt: fmt_proto fmt_shell ## go fmt
@go fmt ./...
@find . -name '*.go' -not -path "./vendor/*" | xargs goimports -l -w
.PHONY: fmt_proto fmt_shell
fmt_proto: ## go fmt protobuf file
#@find . -name '*.proto' -not -path "./vendor/*" | xargs clang-format -i
fmt_shell: ## check shell file
@find . -name '*.sh' -not -path "./vendor/*" | xargs shfmt -w -s -i 4 -ci -bn
fmt_go: fmt_shell ## go fmt
@go fmt ./...
@find . -name '*.go' -not -path "./vendor/*" | xargs goimports -l -w
docker: ## build docker image for chain33 run
@sudo docker build . -f ./build/Dockerfile-run -t chain33:latest
docker-compose: ## build docker-compose for chain33 run
@cd build && if ! [ -d ci ]; then \
make -C ../ ; \
fi; \
cp chain33* Dockerfile docker-compose* ci/ && cd ci/ && ./docker-compose-pre.sh run $(proj) $(dapp) && cd ../..
docker-compose-down: ## build docker-compose for chain33 run
@cd build && if [ -d ci ]; then \
cp chain33* Dockerfile docker-compose* ci/ && cd ci/ && ./docker-compose-pre.sh down $(proj) $(dapp) && cd .. ; \
fi; \
cd ..
clean: ## remove all the bins
@rm -rf $(CLI_A)
@rm -rf $(CLI_B)
@rm -rf $(CLI_C)
@rm -rf $(CLI_D)
@rm -rf $(EBRELAER)
@rm -rf build/*.toml
proto:protobuf
protobuf: ## Generate protbuf file of types package
# @cd ${CHAIN33_PATH}/types/proto && ./create_protobuf.sh && cd ../..
@find ./plugin/dapp -maxdepth 2 -type d -name proto -exec make -C {} \;
help: ## Display this help screen
@printf "Help doc:\nUsage: make [command]\n"
@printf "[command]\n"
@grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
cleandata:
rm -rf build/datadir/addrbook
rm -rf build/datadir/blockchain.db
rm -rf build/datadir/mavltree
rm -rf build/chain33.log
.PHONY: checkgofmt
checkgofmt: ## get all go files and run go fmt on them
@files=$$(find . -name '*.go' -not -path "./vendor/*" | xargs gofmt -l -s); if [ -n "$$files" ]; then \
echo "Error: 'make fmt' needs to be run on:"; \
echo "${files}"; \
exit 1; \
fi;
@files=$$(find . -name '*.go' -not -path "./vendor/*" | xargs goimports -l -w); if [ -n "$$files" ]; then \
echo "Error: 'make fmt' needs to be run on:"; \
echo "${files}"; \
exit 1; \
fi;
all:
chmod +x ./build.sh
./build.sh $(OUT) $(FLAG)
#!/usr/bin/env bash
# 官方ci集成脚本
strpwd=$(pwd)
strcmd=${strpwd##*dapp/}
strapp=${strcmd%/cmd*}
SRC_EBCLI=github.com/33cn/plugin/plugin/dapp/x2ethereum/ebcli
SRC_EBRELAYER=github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer
OUT_DIR="${1}/$strapp"
FLAG=$2
# shellcheck disable=SC2086,1072
go build -i ${FLAG} -v -o "${OUT_DIR}/ebrelayer" "${SRC_EBRELAYER}"
# shellcheck disable=SC2086,1072
go build -i ${FLAG} -v -o "${OUT_DIR}/ebcli_A" "${SRC_EBCLI}"
# shellcheck disable=SC2086,1072
go build -i ${FLAG} -v -o "${OUT_DIR}/ebcli_B" -ldflags "-X ${SRC_EBCLI}/buildflags.RPCAddr=http://localhost:9902" "${SRC_EBCLI}"
# shellcheck disable=SC2086,1072
go build -i ${FLAG} -v -o "${OUT_DIR}/ebcli_C" -ldflags "-X ${SRC_EBCLI}/buildflags.RPCAddr=http://localhost:9903" "${SRC_EBCLI}"
# shellcheck disable=SC2086,1072
go build -i ${FLAG} -v -o "${OUT_DIR}/ebcli_D" -ldflags "-X ${SRC_EBCLI}/buildflags.RPCAddr=http://localhost:9904" "${SRC_EBCLI}"
cp ../ebrelayer/relayer.toml "${OUT_DIR}/relayer.toml"
cp ./build/* "${OUT_DIR}"
OUT_TESTDIR="${1}/dapptest/$strapp"
mkdir -p "${OUT_TESTDIR}"
cp ./test/* "${OUT_TESTDIR}"
This diff is collapsed.
This diff is collapsed.
version: '3'
#services:
# ganachetest:
# build:
# context: .
This diff is collapsed.
This diff is collapsed.
#!/usr/bin/env bash
# shellcheck disable=SC2128
# shellcheck source=/dev/null
source "./allRelayerTest.sh"
source "./perf_test.sh"
function x2ethereum() {
if [ "${2}" == "init" ]; then
return
elif [ "${2}" == "config" ]; then
return
elif [ "${2}" == "test" ]; then
echo "========================== x2ethereum test =========================="
set +e
set -x
AllRelayerMainTest 1
perf_test_main 10
echo "========================== x2ethereum test end =========================="
fi
}
#!/usr/bin/env bash
# shellcheck disable=SC2128
# shellcheck source=/dev/null
#source ../dapp-test-common.sh
#
#set -x
#
#MAIN_HTTP=""
#chain33SenderAddr="14KEKbYtKKQm4wMthSK9J4La4nAiidGozt"
## validatorsAddr=["0x92c8b16afd6d423652559c6e266cbe1c29bfd84f", "0x0df9a824699bc5878232c9e612fe1a5346a5a368", "0xcb074cb21cdddf3ce9c3c0a7ac4497d633c9d9f1", "0xd9dab021e74ecf475788ed7b61356056b2095830"]
#ethValidatorAddrKeyA="3fa21584ae2e4fd74db9b58e2386f5481607dfa4d7ba0617aaa7858e5025dc1e"
#ethValidatorAddrKeyB="a5f3063552f4483cfc20ac4f40f45b798791379862219de9e915c64722c1d400"
#ethValidatorAddrKeyC="bbf5e65539e9af0eb0cfac30bad475111054b09c11d668fc0731d54ea777471e"
#ethValidatorAddrKeyD="c9fa31d7984edf81b8ef3b40c761f1847f6fcd5711ab2462da97dc458f1f896b"
## 新增地址 chain33 需要导入地址 转入 10 bty当收费费
#chain33Validator1="1GTxrmuWiXavhcvsaH5w9whgVxUrWsUMdV"
#chain33Validator2="155ooMPBTF8QQsGAknkK7ei5D78rwDEFe6"
#chain33Validator3="13zBdQwuyDh7cKN79oT2odkxYuDbgQiXFv"
#chain33Validator4="113ZzVamKfAtGt9dq45fX1mNsEoDiN95HG"
#chain33ValidatorKey1="0xd627968e445f2a41c92173225791bae1ba42126ae96c32f28f97ff8f226e5c68"
#chain33ValidatorKey2="0x9d539bc5fd084eb7fe86ad631dba9aa086dba38418725c38d9751459f567da66"
#chain33ValidatorKey3="0x0a6671f101e30a2cc2d79d77436b62cdf2664ed33eb631a9c9e3f3dd348a23be"
#chain33ValidatorKey4="0x3818b257b05ee75b6e43ee0e3cfc2d8502342cf67caed533e3756966690b62a5"
#ethReceiverAddr1="0xa4ea64a583f6e51c3799335b28a8f0529570a635"
#ethReceiverAddrKey1="355b876d7cbcb930d5dfab767f66336ce327e082cbaa1877210c1bae89b1df71"
#ethReceiverAddr2="0x0c05ba5c230fdaa503b53702af1962e08d0c60bf"
#ethReceiverAddrKey2="9dc6df3a8ab139a54d8a984f54958ae0661f880229bf3bdbb886b87d58b56a08"
#
#maturityDegree=10
#
#function init() {
# chain33_ImportPrivkey "${chain33ValidatorKey1}" "${chain33Validator1}" "tokenAddr" "${MAIN_HTTP}"
# chain33_ImportPrivkey "${chain33ValidatorKey2}" "${chain33Validator2}" "tokenAddr" "${MAIN_HTTP}"
# chain33_ImportPrivkey "${chain33ValidatorKey3}" "${chain33Validator3}" "tokenAddr" "${MAIN_HTTP}"
# chain33_ImportPrivkey "${chain33ValidatorKey4}" "${chain33Validator4}" "tokenAddr" "${MAIN_HTTP}"
#
# tx=$(curl -ksd '{"method":"Chain33.CreateTransaction","params":[{"execer":"x2ethereum","actionName":"SetConsensusThreshold","payload":{"consensusThreshold":"80", }}]}' ${MAIN_HTTP} | jq -r ".result")
# chain33_SignAndSendTxWait "$tx" "0x4257d8692ef7fe13c68b65d6a52f03933db2fa5ce8faf210b5b8b80c721ced01" ${MAIN_HTTP} "$FUNCNAME"
#}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package buildflags
var NodeAddr string
This diff is collapsed.
package commands
import (
"fmt"
"os"
"strings"
"github.com/33cn/chain33/rpc/jsonclient"
rpctypes "github.com/33cn/chain33/rpc/types"
"github.com/33cn/chain33/types"
"github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/utils"
types2 "github.com/33cn/plugin/plugin/dapp/x2ethereum/types"
"github.com/spf13/cobra"
)
func queryCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "query",
Short: "query x2ethereum",
Args: cobra.MinimumNArgs(1),
}
cmd.AddCommand(
queryEthProphecyCmd(),
queryValidatorsCmd(),
queryConsensusCmd(),
queryTotalPowerCmd(),
querySymbolTotalAmountByTxTypeCmd(),
)
return cmd
}
func queryEthProphecyCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "prophecy",
Short: "query prophecy",
Run: queryEthProphecy,
}
cmd.Flags().StringP("id", "i", "", "prophecy id")
_ = cmd.MarkFlagRequired("id")
return cmd
}
func queryEthProphecy(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
id, _ := cmd.Flags().GetString("id")
get := &types2.QueryEthProphecyParams{
ID: id,
}
payLoad, err := types.PBToJSON(get)
if err != nil {
_, _ = fmt.Fprintln(os.Stderr, "ErrPbToJson:"+err.Error())
return
}
query := rpctypes.Query4Jrpc{
Execer: types2.X2ethereumX,
FuncName: types2.FuncQueryEthProphecy,
Payload: payLoad,
}
channel := &types2.ReceiptEthProphecy{}
ctx := jsonclient.NewRPCCtx(rpcLaddr, "Chain33.Query", query, channel)
ctx.Run()
}
func queryValidatorsCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "validators",
Short: "query current validators",
Run: queryValidators,
}
cmd.Flags().StringP("validator", "v", "", "write if you want to check specific validator")
//_ = cmd.MarkFlagRequired("validator")
return cmd
}
func queryValidators(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
validator, _ := cmd.Flags().GetString("validator")
get := &types2.QueryValidatorsParams{
Validator: validator,
}
payLoad, err := types.PBToJSON(get)
if err != nil {
_, _ = fmt.Fprintln(os.Stderr, "ErrPbToJson:"+err.Error())
return
}
query := rpctypes.Query4Jrpc{
Execer: types2.X2ethereumX,
FuncName: types2.FuncQueryValidators,
Payload: payLoad,
}
channel := &types2.ReceiptQueryValidator{}
ctx := jsonclient.NewRPCCtx(rpcLaddr, "Chain33.Query", query, channel)
ctx.Run()
}
func queryConsensusCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "consensus",
Short: "query current consensus need",
Run: queryConsensus,
}
return cmd
}
func queryConsensus(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
query := rpctypes.Query4Jrpc{
Execer: types2.X2ethereumX,
FuncName: types2.FuncQueryConsensusThreshold,
}
channel := &types2.ReceiptQueryConsensusThreshold{}
ctx := jsonclient.NewRPCCtx(rpcLaddr, "Chain33.Query", query, channel)
ctx.Run()
}
func queryTotalPowerCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "totalpower",
Short: "query current total power",
Run: queryTotalPower,
}
return cmd
}
func queryTotalPower(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
query := rpctypes.Query4Jrpc{
Execer: types2.X2ethereumX,
FuncName: types2.FuncQueryTotalPower,
}
channel := &types2.ReceiptQueryTotalPower{}
ctx := jsonclient.NewRPCCtx(rpcLaddr, "Chain33.Query", query, channel)
ctx.Run()
}
func querySymbolTotalAmountByTxTypeCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "lockburnasset",
Short: "query current symbol total amount by tx type lock or withdraw",
Run: querySymbolTotalAmountByTxType,
}
cmd.Flags().StringP("symbol", "s", "", "token symbol")
_ = cmd.MarkFlagRequired("symbol")
cmd.Flags().Int64P("direction", "d", 0, "eth2chain33 = 1,chain33toeth = 2")
_ = cmd.MarkFlagRequired("direction")
cmd.Flags().Int64P("txtype", "t", 0, "lock = 1,burn = 2")
_ = cmd.MarkFlagRequired("txtype")
cmd.Flags().StringP("tokenaddress", "a", "", "token address,nil for all this token symbol,and if you want to query eth,you can also ignore it")
return cmd
}
func querySymbolTotalAmountByTxType(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
symbol, _ := cmd.Flags().GetString("symbol")
direction, _ := cmd.Flags().GetInt64("direction")
txType, _ := cmd.Flags().GetInt64("txtype")
contract, _ := cmd.Flags().GetString("tokenaddress")
nodeAddr, _ := cmd.Flags().GetString("node_addr")
decimal, err := utils.GetDecimalsFromNode(contract, nodeAddr)
if err != nil {
fmt.Println("get decimal error")
return
}
if strings.ToLower(symbol) == "eth" && contract == "" {
contract = "0x0000000000000000000000000000000000000000"
}
var txTypeStr string
if txType == 1 {
txTypeStr = "lock"
} else if txType == 2 {
txTypeStr = "withdraw"
}
get := &types2.QuerySymbolAssetsByTxTypeParams{
TokenSymbol: symbol,
Direction: direction,
TxType: txTypeStr,
TokenAddr: contract,
Decimal: decimal,
}
payLoad, err := types.PBToJSON(get)
if err != nil {
_, _ = fmt.Fprintln(os.Stderr, "ErrPbToJson:"+err.Error())
return
}
query := rpctypes.Query4Jrpc{
Execer: types2.X2ethereumX,
FuncName: types2.FuncQuerySymbolTotalAmountByTxType,
Payload: payLoad,
}
channel := &types2.ReceiptQuerySymbolAssets{}
ctx := jsonclient.NewRPCCtx(rpcLaddr, "Chain33.Query", query, channel)
ctx.Run()
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package buildflags
// RPCAddr rpc address
var RPCAddr string
var NodeAddr string
package main
import (
"fmt"
"github.com/33cn/chain33/rpc/jsonclient"
rpctypes "github.com/33cn/chain33/rpc/types"
ebTypes "github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/types"
"github.com/spf13/cobra"
)
// RelayerCmd command func
func Chain33RelayerCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "chain33 ",
Short: "Chain33 relayer ",
Args: cobra.MinimumNArgs(1),
}
cmd.AddCommand(
ImportPrivateKeyCmd(),
ShowValidatorAddrCmd(),
ShowTxsHashCmd(),
)
return cmd
}
// SetPwdCmd set password
func ImportPrivateKeyCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "import_privatekey",
Short: "import ethereum private key to sign txs to be submitted to ethereum",
Run: importPrivatekey,
}
addImportPrivateKeyFlags(cmd)
return cmd
}
func addImportPrivateKeyFlags(cmd *cobra.Command) {
cmd.Flags().StringP("key", "k", "", "ethereum private key")
cmd.MarkFlagRequired("key")
}
func importPrivatekey(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
privateKey, _ := cmd.Flags().GetString("key")
importKeyReq := ebTypes.ImportKeyReq{
PrivateKey: privateKey,
}
var res rpctypes.Reply
ctx := jsonclient.NewRPCCtx(rpcLaddr, "Manager.ImportChain33RelayerPrivateKey", importKeyReq, &res)
ctx.Run()
}
func ShowValidatorAddrCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "show_validator",
Short: "show me the validator",
Run: showValidatorAddr,
}
return cmd
}
func showValidatorAddr(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
var res string
ctx := jsonclient.NewRPCCtx(rpcLaddr, "Manager.ShowChain33RelayerValidator", nil, &res)
ctx.Run()
}
func ShowTxsHashCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "show_txhashes",
Short: "show me the tx hashes",
Run: showChain33Relayer2EthTxs,
}
return cmd
}
func showChain33Relayer2EthTxs(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
var res ebTypes.Txhashes
ctx := jsonclient.NewRPCCtx(rpcLaddr, "Manager.ShowChain33Relayer2EthTxs", nil, &res)
if _, err := ctx.RunResult(); nil != err {
errInfo := err.Error()
fmt.Println("errinfo:" + errInfo)
return
}
for _, hash := range res.Txhash {
fmt.Println(hash)
}
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"github.com/33cn/chain33/rpc/jsonclient"
rpctypes "github.com/33cn/chain33/rpc/types"
relayerTypes "github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/types"
"github.com/spf13/cobra"
)
// RelayerCmd command func
func RelayerCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "relayer",
Short: "relayer of Chain33 and Ethereum ",
Args: cobra.MinimumNArgs(1),
}
cmd.AddCommand(
SetPwdCmd(),
ChangePwdCmd(),
LockCmd(),
UnlockCmd(),
Chain33RelayerCmd(),
EthereumRelayerCmd(),
)
return cmd
}
// SetPwdCmd set password
func SetPwdCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "set_pwd",
Short: "Set password",
Run: setPwd,
}
addSetPwdFlags(cmd)
return cmd
}
func addSetPwdFlags(cmd *cobra.Command) {
cmd.Flags().StringP("password", "p", "", "password,[8-30]letter and digit")
cmd.MarkFlagRequired("password")
}
func setPwd(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
newPwd, _ := cmd.Flags().GetString("password")
params := relayerTypes.ReqSetPasswd{
Passphase: newPwd,
}
var res rpctypes.Reply
ctx := jsonclient.NewRPCCtx(rpcLaddr, "Manager.SetPassphase", params, &res)
ctx.Run()
}
// ChangePwdCmd set password
func ChangePwdCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "change_pwd",
Short: "Change password",
Run: changePwd,
}
addChangePwdFlags(cmd)
return cmd
}
func addChangePwdFlags(cmd *cobra.Command) {
cmd.Flags().StringP("old", "o", "", "old password")
cmd.MarkFlagRequired("old")
cmd.Flags().StringP("new", "n", "", "new password,[8-30]letter and digit")
cmd.MarkFlagRequired("new")
}
func changePwd(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
oldPwd, _ := cmd.Flags().GetString("old")
newPwd, _ := cmd.Flags().GetString("new")
params := relayerTypes.ReqChangePasswd{
OldPassphase: oldPwd,
NewPassphase: newPwd,
}
var res rpctypes.Reply
ctx := jsonclient.NewRPCCtx(rpcLaddr, "Manager.ChangePassphase", params, &res)
ctx.Run()
}
// LockCmd lock the relayer manager
func LockCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "lock",
Short: "Lock relayer manager",
Run: lock,
}
return cmd
}
func lock(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
var res rpctypes.Reply
ctx := jsonclient.NewRPCCtx(rpcLaddr, "Manager.Lock", nil, &res)
ctx.Run()
}
// UnlockCmd unlock the wallet
func UnlockCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "unlock",
Short: "Unlock relayer manager",
Run: unLock,
}
addUnlockFlags(cmd)
return cmd
}
func addUnlockFlags(cmd *cobra.Command) {
cmd.Flags().StringP("pwd", "p", "", "password needed to unlock")
cmd.MarkFlagRequired("pwd")
}
func unLock(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
pwd, _ := cmd.Flags().GetString("pwd")
params := pwd
var res rpctypes.Reply
ctx := jsonclient.NewRPCCtx(rpcLaddr, "Manager.Unlock", params, &res)
ctx.Run()
}
This diff is collapsed.
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"fmt"
"net/http"
"os"
"strings"
"github.com/33cn/chain33/common/log"
"github.com/33cn/chain33/pluginmgr"
"github.com/33cn/plugin/plugin/dapp/x2ethereum/ebcli/buildflags"
"github.com/spf13/cobra"
)
var rootCmd = &cobra.Command{
Use: "chain33xEth-relayer" + "-cli",
Short: "chain33xEth-relayer" + "client tools",
}
func init() {
rootCmd.AddCommand(
RelayerCmd(),
)
}
func testTLS(RPCAddr string) string {
rpcaddr := RPCAddr
if strings.HasPrefix(rpcaddr, "https://") {
return RPCAddr
}
if !strings.HasPrefix(rpcaddr, "http://") {
return RPCAddr
}
//test tls ok
if rpcaddr[len(rpcaddr)-1] != '/' {
rpcaddr += "/"
}
rpcaddr += "test"
resp, err := http.Get(rpcaddr)
if err != nil {
return "https://" + RPCAddr[7:]
}
defer resp.Body.Close()
if resp.StatusCode == 200 {
return RPCAddr
}
return "https://" + RPCAddr[7:]
}
//run :
func run(RPCAddr, NodeAddr string) {
//test tls is enable
RPCAddr = testTLS(RPCAddr)
pluginmgr.AddCmd(rootCmd)
log.SetLogLevel("error")
rootCmd.PersistentFlags().String("rpc_laddr", RPCAddr, "http url")
rootCmd.PersistentFlags().String("node_addr", NodeAddr, "eth node url")
//rootCmd.PersistentFlags().String("rpc_laddr", "", "http url")
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func main() {
if buildflags.RPCAddr == "" {
buildflags.RPCAddr = "http://localhost:9901"
}
if buildflags.NodeAddr == "" {
buildflags.NodeAddr = "http://127.0.0.1:7545"
}
run(buildflags.RPCAddr, buildflags.NodeAddr)
}
package main
import (
"github.com/33cn/chain33/rpc/jsonclient"
ebTypes "github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/types"
"github.com/spf13/cobra"
)
func StaticsCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "statics",
Short: "statics of lock/unlock Eth or ERC20,or deposit/burn chain33 asset ",
Args: cobra.MinimumNArgs(1),
}
cmd.AddCommand(
ShowLockStaticsCmd(),
ShowDepositStaticsCmd(),
)
return cmd
}
func ShowLockStaticsCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "lock",
Short: "show the lock statics of ETH or ERC20",
Run: ShowLockStatics,
}
ShowLockStaticsFlags(cmd)
return cmd
}
func ShowLockStaticsFlags(cmd *cobra.Command) {
cmd.Flags().StringP("token", "t", "", "token address, optional, nil for ETH")
}
func ShowLockStatics(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
tokenAddr, _ := cmd.Flags().GetString("token")
para := ebTypes.TokenStatics{
TokenAddr: tokenAddr,
}
var res ebTypes.StaticsLock
ctx := jsonclient.NewRPCCtx(rpcLaddr, "Manager.ShowLockStatics", para, &res)
ctx.Run()
}
func ShowDepositStaticsCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "deposit",
Short: "show the deposit statics of chain33 asset",
Run: ShowDepositStatics,
}
ShowDepositStaticsFlags(cmd)
return cmd
}
func ShowDepositStaticsFlags(cmd *cobra.Command) {
cmd.Flags().StringP("token", "t", "", "token address")
_ = cmd.MarkFlagRequired("token")
}
func ShowDepositStatics(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
tokenAddr, _ := cmd.Flags().GetString("token")
para := ebTypes.TokenStatics{
TokenAddr: tokenAddr,
}
var res ebTypes.StaticsDeposit
ctx := jsonclient.NewRPCCtx(rpcLaddr, "Manager.ShowDepositStatics", para, &res)
ctx.Run()
}
##
##编译solidity,并产生bin文件,abi文件,和相应的go文件
SRC_CONTRACT := sol_contracts
#SRC_CONTRACT := github.com/33cn/plugin/plugin/dapp/x2Ethereum/ebrelayer/ethcontract/sol_contracts
GO_OUT := generated
PACKAGE := generated
proj := "build"
.PHONY: default build clean registry bridgeBank setup
default: depends build
build: depends
@abigen --sol $(SRC_CONTRACT)/BridgeRegistry.sol --pkg $(PACKAGE) --out $(GO_OUT)/BridgeRegistry.go
@abigen --sol $(SRC_CONTRACT)/BridgeBank/BridgeBank.sol --pkg $(PACKAGE) --out $(GO_OUT)/BridgeBank.go
#@abigen --sol $(SRC_CONTRACT)/CosmosBridge.sol --pkg $(PACKAGE) --out $(GO_OUT)/CosmosBridge.go
#@abigen --sol $(SRC_CONTRACT)/Oracle.sol --pkg $(PACKAGE) --out $(GO_OUT)/Oracle.go
#@abigen --sol $(SRC_CONTRACT)/Valset.sol --pkg $(PACKAGE) --out $(GO_OUT)/Valset.go
clean:
@rm -fr $(GO_OUT)/*
registry:
@abigen --sol $(SRC_CONTRACT)/BridgeRegistry.sol --pkg $(PACKAGE) --out $(GO_OUT)/BridgeRegistry.go
bridgeBank:
@abigen --sol $(SRC_CONTRACT)/BridgeBank/BridgeBank.sol --pkg $(PACKAGE) --out $(GO_OUT)/BridgeBank.go
depends:
if ! [ -d openzeppelin-solidity ]; then \
echo "not exist openzeppelin-solidity and going to get" ; \
go get github.com/OpenZeppelin/openzeppelin-contracts@v2.5 ; \
mkdir openzeppelin-solidity ;\
cp -r ${GOPATH}/pkg/mod/github.com/\!open\!zeppelin/openzeppelin-contracts@v2.5.0+incompatible/contracts openzeppelin-solidity ; \
fi; \
This source diff could not be displayed because it is too large. You can view the blob instead.
pragma solidity ^0.5.0;
import "./Chain33Bank.sol";
import "./EthereumBank.sol";
import "../Oracle.sol";
import "../Chain33Bridge.sol";
/**
* @title BridgeBank
* @dev Bank contract which coordinates asset-related functionality.
* Chain33Bank manages the minting and burning of tokens which
* represent Chain33 based assets, while EthereumBank manages
* the locking and unlocking of Ethereum and ERC20 token assets
* based on Ethereum.
**/
contract BridgeBank is Chain33Bank, EthereumBank {
using SafeMath for uint256;
address public operator;
Oracle public oracle;
Chain33Bridge public chain33Bridge;
/*
* @dev: Constructor, sets operator
*/
constructor (
address _operatorAddress,
address _oracleAddress,
address _chain33BridgeAddress
)
public
{
operator = _operatorAddress;
oracle = Oracle(_oracleAddress);
chain33Bridge = Chain33Bridge(_chain33BridgeAddress);
}
/*
* @dev: Modifier to restrict access to operator
*/
modifier onlyOperator() {
require(
msg.sender == operator,
'Must be BridgeBank operator.'
);
_;
}
/*
* @dev: Modifier to restrict access to the oracle
*/
modifier onlyOracle()
{
require(
msg.sender == address(oracle),
"Access restricted to the oracle"
);
_;
}
/*
* @dev: Modifier to restrict access to the chain33 bridge
*/
modifier onlyChain33Bridge()
{
require(
msg.sender == address(chain33Bridge),
"Access restricted to the chain33 bridge"
);
_;
}
/*
* @dev: Fallback function allows operator to send funds to the bank directly
* This feature is used for testing and is available at the operator's own risk.
*/
function() external payable onlyOperator {}
/*
* @dev: Creates a new BridgeToken
*
* @param _symbol: The new BridgeToken's symbol
* @return: The new BridgeToken contract's address
*/
function createNewBridgeToken(
string memory _symbol
)
public
onlyOperator
returns(address)
{
return deployNewBridgeToken(_symbol);
}
/*
* @dev: Mints new BankTokens
*
* @param _chain33Sender: The sender's Chain33 address in bytes.
* @param _ethereumRecipient: The intended recipient's Ethereum address.
* @param _chain33TokenAddress: The currency type
* @param _symbol: chain33 token symbol
* @param _amount: number of chain33 tokens to be minted
*/
function mintBridgeTokens(
bytes memory _chain33Sender,
address payable _intendedRecipient,
address _bridgeTokenAddress,
string memory _symbol,
uint256 _amount
)
public
onlyChain33Bridge
{
return mintNewBridgeTokens(
_chain33Sender,
_intendedRecipient,
_bridgeTokenAddress,
_symbol,
_amount
);
}
/*
* @dev: Burns bank tokens
*
* @param _chain33Receiver: The _chain33 receiver address in bytes.
* @param _chain33TokenAddress: The currency type
* @param _amount: number of chain33 tokens to be burned
*/
function burnBridgeTokens(
bytes memory _chain33Receiver,
address _chain33TokenAddress,
uint256 _amount
)
public
{
return burnChain33Tokens(
msg.sender,
_chain33Receiver,
_chain33TokenAddress,
_amount
);
}
/*
* @dev: Locks received Ethereum funds.
*
* @param _recipient: bytes representation of destination address.
* @param _token: token address in origin chain (0x0 if ethereum)
* @param _amount: value of deposit
*/
function lock(
bytes memory _recipient,
address _token,
uint256 _amount
)
public
availableNonce()
payable
{
string memory symbol;
// Ethereum deposit
if (msg.value > 0) {
require(
_token == address(0),
"Ethereum deposits require the 'token' address to be the null address"
);
require(
msg.value == _amount,
"The transactions value must be equal the specified amount (in wei)"
);
// Set the the symbol to ETH
symbol = "ETH";
// ERC20 deposit
} else {
require(
BridgeToken(_token).transferFrom(msg.sender, address(this), _amount),
"Contract token allowances insufficient to complete this lock request"
);
// Set symbol to the ERC20 token's symbol
symbol = BridgeToken(_token).symbol();
}
lockFunds(
msg.sender,
_recipient,
_token,
symbol,
_amount
);
}
/*
* @dev: Unlocks Ethereum and ERC20 tokens held on the contract.
*
* @param _recipient: recipient's Ethereum address
* @param _token: token contract address
* @param _symbol: token symbol
* @param _amount: wei amount or ERC20 token count
\ */
function unlock(
address payable _recipient,
address _token,
string memory _symbol,
uint256 _amount
)
public
onlyChain33Bridge
hasLockedFunds(
_token,
_amount
)
canDeliver(
_token,
_amount
)
{
unlockFunds(
_recipient,
_token,
_symbol,
_amount
);
}
/*
* @dev: Exposes an item's current status.
*
* @param _id: The item in question.
* @return: Boolean indicating the lock status.
*/
function getChain33DepositStatus(
bytes32 _id
)
public
view
returns(bool)
{
return isLockedChain33Deposit(_id);
}
/*
* @dev: Allows access to a Chain33 deposit's information via its unique identifier.
*
* @param _id: The deposit to be viewed.
* @return: Original sender's Ethereum address.
* @return: Intended Chain33 recipient's address in bytes.
* @return: The lock deposit's currency, denoted by a token address.
* @return: The amount locked in the deposit.
* @return: The deposit's unique nonce.
*/
function viewChain33Deposit(
bytes32 _id
)
public
view
returns(bytes memory, address payable, address, uint256)
{
return getChain33Deposit(_id);
}
}
\ No newline at end of file
pragma solidity ^0.5.0;
import "../../openzeppelin-solidity/contracts/token/ERC20/ERC20Mintable.sol";
import "../../openzeppelin-solidity/contracts/token/ERC20/ERC20Burnable.sol";
import "../../openzeppelin-solidity/contracts/token/ERC20/ERC20Detailed.sol";
/**
* @title BridgeToken
* @dev Mintable, ERC20 compatible BankToken for use by BridgeBank
**/
contract BridgeToken is ERC20Mintable, ERC20Burnable, ERC20Detailed {
constructor(
string memory _symbol
)
public
ERC20Detailed(
_symbol,
_symbol,
8
)
{
// Intentionally left blank
}
}
\ No newline at end of file
pragma solidity ^0.5.0;
import "../../openzeppelin-solidity/contracts/math/SafeMath.sol";
import "./BridgeToken.sol";
/*
* @title: EthereumBank
* @dev: Ethereum bank which locks Ethereum/ERC20 token deposits, and unlocks
* Ethereum/ERC20 tokens once the prophecy has been successfully processed.
*/
contract EthereumBank {
using SafeMath for uint256;
uint256 public lockNonce;
mapping(address => uint256) public lockedFunds;
/*
* @dev: Event declarations
*/
event LogLock(
address _from,
bytes _to,
address _token,
string _symbol,
uint256 _value,
uint256 _nonce
);
event LogUnlock(
address _to,
address _token,
string _symbol,
uint256 _value
);
/*
* @dev: Modifier declarations
*/
modifier hasLockedFunds(
address _token,
uint256 _amount
) {
require(
lockedFunds[_token] >= _amount,
"The Bank does not hold enough locked tokens to fulfill this request."
);
_;
}
modifier canDeliver(
address _token,
uint256 _amount
)
{
if(_token == address(0)) {
require(
address(this).balance >= _amount,
'Insufficient ethereum balance for delivery.'
);
} else {
require(
BridgeToken(_token).balanceOf(address(this)) >= _amount,
'Insufficient ERC20 token balance for delivery.'
);
}
_;
}
modifier availableNonce() {
require(
lockNonce + 1 > lockNonce,
'No available nonces.'
);
_;
}
/*
* @dev: Constructor which sets the lock nonce
*/
constructor()
public
{
lockNonce = 0;
}
/*
* @dev: Creates a new Ethereum deposit with a unique id.
*
* @param _sender: The sender's ethereum address.
* @param _recipient: The intended recipient's chain33 address.
* @param _token: The currency type, either erc20 or ethereum.
* @param _amount: The amount of erc20 tokens/ ethereum (in wei) to be itemized.
*/
function lockFunds(
address payable _sender,
bytes memory _recipient,
address _token,
string memory _symbol,
uint256 _amount
)
internal
{
// Incerment the lock nonce
lockNonce = lockNonce.add(1);
// Increment locked funds by the amount of tokens to be locked
lockedFunds[_token] = lockedFunds[_token].add(_amount);
emit LogLock(
_sender,
_recipient,
_token,
_symbol,
_amount,
lockNonce
);
}
/*
* @dev: Unlocks funds held on contract and sends them to the
* intended recipient
*
* @param _recipient: recipient's Ethereum address
* @param _token: token contract address
* @param _symbol: token symbol
* @param _amount: wei amount or ERC20 token count
*/
function unlockFunds(
address payable _recipient,
address _token,
string memory _symbol,
uint256 _amount
)
internal
{
// Decrement locked funds mapping by the amount of tokens to be unlocked
lockedFunds[_token] = lockedFunds[_token].sub(_amount);
// Transfer funds to intended recipient
if (_token == address(0)) {
_recipient.transfer(_amount);
} else {
require(
BridgeToken(_token).transfer(_recipient, _amount),
"Token transfer failed"
);
}
emit LogUnlock(
_recipient,
_token,
_symbol,
_amount
);
}
}
pragma solidity ^0.5.0;
contract BridgeRegistry {
address public chain33Bridge;
address public bridgeBank;
address public oracle;
address public valset;
uint256 public deployHeight;
event LogContractsRegistered(
address _chain33Bridge,
address _bridgeBank,
address _oracle,
address _valset
);
constructor(
address _chain33Bridge,
address _bridgeBank,
address _oracle,
address _valset
)
public
{
chain33Bridge = _chain33Bridge;
bridgeBank = _bridgeBank;
oracle = _oracle;
valset = _valset;
deployHeight = block.number;
emit LogContractsRegistered(
chain33Bridge,
bridgeBank,
oracle,
valset
);
}
}
\ No newline at end of file
pragma solidity ^0.5.0;
import "../openzeppelin-solidity/contracts/math/SafeMath.sol";
import "./Valset.sol";
import "./BridgeBank/BridgeBank.sol";
contract Chain33Bridge {
using SafeMath for uint256;
/*
* @dev: Public variable declarations
*/
address public operator;
Valset public valset;
address public oracle;
bool public hasOracle;
BridgeBank public bridgeBank;
bool public hasBridgeBank;
uint256 public prophecyClaimCount;
mapping(bytes32 => ProphecyClaim) public prophecyClaims;
enum Status {
Null,
Pending,
Success,
Failed
}
enum ClaimType {
Unsupported,
Burn,
Lock
}
struct ProphecyClaim {
ClaimType claimType;
bytes chain33Sender;
address payable ethereumReceiver;
address originalValidator;
address tokenAddress;
string symbol;
uint256 amount;
Status status;
}
/*
* @dev: Event declarations
*/
event LogOracleSet(
address _oracle
);
event LogBridgeBankSet(
address _bridgeBank
);
event LogNewProphecyClaim(
uint256 _prophecyID,
ClaimType _claimType,
bytes _chain33Sender,
address payable _ethereumReceiver,
address _validatorAddress,
address _tokenAddress,
string _symbol,
uint256 _amount
);
event LogProphecyCompleted(
bytes32 _claimID,
ClaimType _claimType
);
/*
* @dev: Modifier which only allows access to currently pending prophecies
*/
modifier isPending(
bytes32 _claimID
)
{
require(
isProphecyClaimActive(_claimID),
"Prophecy claim is not active"
);
_;
}
/*
* @dev: Modifier to restrict access to the operator.
*/
modifier onlyOperator()
{
require(
msg.sender == operator,
'Must be the operator.'
);
_;
}
/*
* @dev: Modifier to restrict access to the oracle.
*/
modifier onlyOracle()
{
require(
msg.sender == oracle,
'Must be the oracle.'
);
_;
}
/*
* @dev: The bridge is not active until oracle and bridge bank are set
*/
modifier isActive()
{
require(
hasOracle == true && hasBridgeBank == true,
"The Operator must set the oracle and bridge bank for bridge activation"
);
_;
}
/*
* @dev: Modifier to make sure the claim type is valid
*/
modifier validClaimType(
ClaimType _claimType
)
{
require(
_claimType == ClaimType.Burn || _claimType == ClaimType.Lock,
"The claim type must be ClaimType.Burn or ClaimType.Lock"
);
_;
}
/*
* @dev: Constructor
*/
constructor(
address _operator,
address _valset
)
public
{
prophecyClaimCount = 0;
operator = _operator;
valset = Valset(_valset);
hasOracle = false;
hasBridgeBank = false;
}
/*
* @dev: setOracle
*/
function setOracle(
address _oracle
)
public
onlyOperator
{
require(
!hasOracle,
"The Oracle cannot be updated once it has been set"
);
hasOracle = true;
oracle = _oracle;
emit LogOracleSet(
oracle
);
}
/*
* @dev: setBridgeBank
*/
function setBridgeBank(
address payable _bridgeBank
)
public
onlyOperator
{
require(
!hasBridgeBank,
"The Bridge Bank cannot be updated once it has been set"
);
hasBridgeBank = true;
bridgeBank = BridgeBank(_bridgeBank);
emit LogBridgeBankSet(
address(bridgeBank)
);
}
/*
* @dev: setNewProphecyClaim
* Sets a new burn or lock prophecy claim, adding it to the prophecyClaims mapping.
* Lock claims can only be created for BridgeTokens on BridgeBank's whitelist. The operator
* is responsible for adding them, and lock claims will fail until the operator has done so.
*/
function setNewProphecyClaim(
bytes32 _claimID,
uint8 _claimType,
bytes memory _chain33Sender,
address payable _ethereumReceiver,
address _originalValidator,
address _tokenAddress,
string memory _symbol,
uint256 _amount
)
public
isActive
onlyOracle
{
// Increment the prophecy claim count
prophecyClaimCount = prophecyClaimCount.add(1);
ClaimType claimType = ClaimType(_claimType);
//overwrite the token address in case of lock
if (claimType == ClaimType.Lock) {
_tokenAddress = bridgeBank.getToken2address(_symbol);
}
// Create the new ProphecyClaim
ProphecyClaim memory prophecyClaim = ProphecyClaim(
claimType,
_chain33Sender,
_ethereumReceiver,
_originalValidator,
_tokenAddress,
_symbol,
_amount,
Status.Pending
);
// Add the new ProphecyClaim to the mapping
prophecyClaims[_claimID] = prophecyClaim;
emit LogNewProphecyClaim(
prophecyClaimCount,
claimType,
_chain33Sender,
_ethereumReceiver,
_originalValidator,
_tokenAddress,
_symbol,
_amount
);
}
/*
* @dev: completeClaim
* Allows for the completion of ProphecyClaims once processed by the Oracle.
* Burn claims unlock tokens stored by BridgeBank.
* Lock claims mint BridgeTokens on BridgeBank's token whitelist.
*/
function completeClaim(
bytes32 _claimID
)
public
isPending(_claimID)
{
require(
msg.sender == oracle,
"Only the Oracle may complete prophecies"
);
prophecyClaims[_claimID].status = Status.Success;
ClaimType claimType = prophecyClaims[_claimID].claimType;
if(claimType == ClaimType.Burn) {
unlockTokens(_claimID);
} else {
issueBridgeTokens(_claimID);
}
emit LogProphecyCompleted(
_claimID,
claimType
);
}
/*
* @dev: issueBridgeTokens
* Issues a request for the BridgeBank to mint new BridgeTokens
*/
function issueBridgeTokens(
bytes32 _claimID
)
internal
{
ProphecyClaim memory prophecyClaim = prophecyClaims[_claimID];
bridgeBank.mintBridgeTokens(
prophecyClaim.chain33Sender,
prophecyClaim.ethereumReceiver,
prophecyClaim.tokenAddress,
prophecyClaim.symbol,
prophecyClaim.amount
);
}
/*
* @dev: unlockTokens
* Issues a request for the BridgeBank to unlock funds held on contract
*/
function unlockTokens(
bytes32 _claimID
)
internal
{
ProphecyClaim memory prophecyClaim = prophecyClaims[_claimID];
bridgeBank.unlock(
prophecyClaim.ethereumReceiver,
prophecyClaim.tokenAddress,
prophecyClaim.symbol,
prophecyClaim.amount
);
}
/*
* @dev: isProphecyClaimActive
* Returns boolean indicating if the ProphecyClaim is active
*/
function isProphecyClaimActive(
bytes32 _claimID
)
public
view
returns(bool)
{
return prophecyClaims[_claimID].status == Status.Pending;
}
/*
* @dev: isProphecyValidatorActive
* Returns boolean indicating if the validator that originally
* submitted the ProphecyClaim is still an active validator
*/
function isProphecyClaimValidatorActive(
bytes32 _claimID
)
public
view
returns(bool)
{
return valset.isActiveValidator(
prophecyClaims[_claimID].originalValidator
);
}
/*
* @dev: Modifier to make sure the claim type is valid
*/
function isValidClaimType(uint8 _claimType) public pure returns(bool)
{
ClaimType claimType = ClaimType(_claimType);
if (claimType == ClaimType.Burn || claimType == ClaimType.Lock) {
return true;
}
return false;
}
}
pragma solidity ^0.5.0;
contract Migrations {
address public owner;
// A function with the signature `last_completed_migration()`, returning a uint, is required.
uint public last_completed_migration;
modifier restricted() {
if (msg.sender == owner) _;
}
constructor() public {
owner = msg.sender;
}
// A function with the signature `setCompleted(uint)` is required.
function setCompleted(uint completed) public restricted {
last_completed_migration = completed;
}
function upgrade(address new_address) public restricted {
Migrations upgraded = Migrations(new_address);
upgraded.setCompleted(last_completed_migration);
}
}
pragma solidity ^0.5.0;
import "../openzeppelin-solidity/contracts/math/SafeMath.sol";
import "./Valset.sol";
import "./Chain33Bridge.sol";
contract Oracle {
using SafeMath for uint256;
/*
* @dev: Public variable declarations
*/
Chain33Bridge public chain33Bridge;
Valset public valset;
address public operator;
// Tracks the number of OracleClaims made on an individual BridgeClaim
mapping(bytes32 => address[]) public oracleClaimValidators;
mapping(bytes32 => mapping(address => bool)) public hasMadeClaim;
enum ClaimType {
Unsupported,
Burn,
Lock
}
/*
* @dev: Event declarations
*/
event LogNewOracleClaim(
bytes32 _claimID,
address _validatorAddress,
bytes _signature
);
event LogProphecyProcessed(
bytes32 _claimID,
uint256 _weightedSignedPower,
uint256 _weightedTotalPower,
address _submitter
);
/*
* @dev: Modifier to restrict access to the operator.
*/
modifier onlyOperator()
{
require(
msg.sender == operator,
'Must be the operator.'
);
_;
}
/*
* @dev: Modifier to restrict access to current ValSet validators
*/
modifier onlyValidator()
{
require(
valset.isActiveValidator(msg.sender),
"Must be an active validator"
);
_;
}
/*
* @dev: Modifier to restrict access to current ValSet validators
*/
modifier isPending(
bytes32 _claimID
)
{
require(
chain33Bridge.isProphecyClaimActive(
_claimID
) == true,
"The prophecy must be pending for this operation"
);
_;
}
/*
* @dev: Modifier to restrict the claim type must be burn or lock
*/
modifier isValidClaimType(
ClaimType _claimType
)
{
require(
chain33Bridge.isValidClaimType(
uint8(_claimType)
) == true,
"The claim type must be burn or lock"
);
_;
}
/*
* @dev: Constructor
*/
constructor(
address _operator,
address _valset,
address _chain33Bridge
)
public
{
operator = _operator;
chain33Bridge = Chain33Bridge(_chain33Bridge);
valset = Valset(_valset);
}
/*
* @dev: newOracleClaim
* Allows validators to make new OracleClaims on chain33 lock/burn prophecy,
* if the required vote power reached,just make it processed
* @param _claimType: burn or lock,
* @param _chain33Sender: chain33 sender,
* @param _ethereumReceiver: receiver on ethereum
* @param _tokenAddress: token address
* @param _symbol: token symbol
* @param _amount: amount
* @param _claimID: claim id
* @param _message: message for verifying
* @param _signature: need to recover sender
*/
function newOracleClaim(
ClaimType _claimType,
bytes memory _chain33Sender,
address payable _ethereumReceiver,
address _tokenAddress,
string memory _symbol,
uint256 _amount,
bytes32 _claimID,
bytes memory _signature
)
public
onlyValidator
isValidClaimType(_claimType)
{
address validatorAddress = msg.sender;
// Validate the msg.sender's signature
require(
validatorAddress == valset.recover(
_claimID,
_signature
),
"Invalid _claimID signature."
);
// Confirm that this address has not already made an oracle claim on this _ClaimID
require(
!hasMadeClaim[_claimID][validatorAddress],
"Cannot make duplicate oracle claims from the same address."
);
if (oracleClaimValidators[_claimID].length == 0) {
chain33Bridge.setNewProphecyClaim(
_claimID,
uint8(_claimType),
_chain33Sender,
_ethereumReceiver,
validatorAddress,
_tokenAddress,
_symbol,
_amount);
}
hasMadeClaim[_claimID][validatorAddress] = true;
oracleClaimValidators[_claimID].push(validatorAddress);
emit LogNewOracleClaim(
_claimID,
validatorAddress,
_signature
);
(bool valid, uint256 weightedSignedPower, uint256 weightedTotalPower ) = getClaimThreshold(_claimID);
if (true == valid) {
//if processed already,just emit an event
if (chain33Bridge.isProphecyClaimActive(_claimID) == true) {
completeClaim(_claimID);
}
emit LogProphecyProcessed(
_claimID,
weightedSignedPower,
weightedTotalPower,
msg.sender
);
}
}
/*
* @dev: checkBridgeProphecy
* Operator accessor method which checks if a prophecy has passed
* the validity threshold, without actually completing the prophecy.
*/
function checkBridgeProphecy(
bytes32 _claimID
)
public
view
onlyOperator
isPending(_claimID)
returns(bool, uint256, uint256)
{
require(
chain33Bridge.isProphecyClaimActive(
_claimID
) == true,
"Can only check active prophecies"
);
return getClaimThreshold(
_claimID
);
}
/*
* @dev: getClaimThreshold
* Calculates the status of a claim. The claim is considered valid if the
* combined active signatory validator powers pass the validation threshold.
* The hardcoded threshold is (Combined signed power * 2) >= (Total power * 3).
*/
function getClaimThreshold(
bytes32 _claimID
)
internal
view
returns(bool, uint256, uint256)
{
uint256 signedPower = 0;
uint256 totalPower = valset.totalPower();
// Iterate over the signatory addresses
for (uint256 i = 0; i < oracleClaimValidators[_claimID].length; i = i.add(1)) {
address signer = oracleClaimValidators[_claimID][i];
// Only add the power of active validators
if(valset.isActiveValidator(signer)) {
signedPower = signedPower.add(
valset.getValidatorPower(
signer
)
);
}
}
// Calculate if weighted signed power has reached threshold of weighted total power
uint256 weightedSignedPower = signedPower.mul(3);
uint256 weightedTotalPower = totalPower.mul(2);
bool hasReachedThreshold = weightedSignedPower >= weightedTotalPower;
return(
hasReachedThreshold,
weightedSignedPower,
weightedTotalPower
);
}
/*
* @dev: completeClaim
* Completes a claim by completing the corresponding BridgeClaim
* on the Chain33Bridge.
*/
function completeClaim(
bytes32 _claimID
)
internal
{
chain33Bridge.completeClaim(
_claimID
);
}
}
\ No newline at end of file
package test
import (
"encoding/hex"
"fmt"
"math/big"
"strings"
"testing"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
)
func TestUnpackEvent(t *testing.T) {
const abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"receive","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"receivedAddr","type":"event"}]`
abi, err := abi.JSON(strings.NewReader(abiJSON))
if err != nil {
t.Fatal(err)
}
const hexdata = `000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158`
data, err := hex.DecodeString(hexdata)
if err != nil {
t.Fatal(err)
}
if len(data)%32 == 0 {
t.Errorf("len(data) is %d, want a non-multiple of 32", len(data))
}
type ReceivedEvent struct {
Sender common.Address
Amount *big.Int
Memo []byte
}
var ev ReceivedEvent
err = abi.Unpack(&ev, "received", data)
if err != nil {
t.Error(err)
}
fmt.Printf("\nReceivedEvent sender:%s", ev.Sender.String())
type ReceivedAddrEvent struct {
Sender common.Address
}
var receivedAddrEv ReceivedAddrEvent
err = abi.Unpack(&receivedAddrEv, "receivedAddr", data)
if err != nil {
t.Error(err)
}
fmt.Printf("\nreceivedAddrEv=%s\n\n\n", receivedAddrEv.Sender.String())
}
package setup
import (
"context"
"crypto/ecdsa"
"math/big"
"github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/ethcontract/generated"
"github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/ethinterface"
"github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/ethtxs"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto"
)
func PrepareTestEnv() (*ethinterface.SimExtend, *ethtxs.DeployPara) {
genesiskey, _ := crypto.GenerateKey()
alloc := make(core.GenesisAlloc)
genesisAddr := crypto.PubkeyToAddress(genesiskey.PublicKey)
genesisAccount := core.GenesisAccount{
Balance: big.NewInt(10000000000 * 10000),
PrivateKey: crypto.FromECDSA(genesiskey),
}
alloc[genesisAddr] = genesisAccount
var InitValidators []common.Address
var ValidatorPriKey []*ecdsa.PrivateKey
for i := 0; i < 4; i++ {
key, _ := crypto.GenerateKey()
addr := crypto.PubkeyToAddress(key.PublicKey)
InitValidators = append(InitValidators, addr)
ValidatorPriKey = append(ValidatorPriKey, key)
account := core.GenesisAccount{
Balance: big.NewInt(100000000 * 100),
PrivateKey: crypto.FromECDSA(key),
}
alloc[addr] = account
}
gasLimit := uint64(100000000)
sim := new(ethinterface.SimExtend)
sim.SimulatedBackend = backends.NewSimulatedBackend(alloc, gasLimit)
InitPowers := []*big.Int{big.NewInt(80), big.NewInt(10), big.NewInt(10), big.NewInt(10)}
para := &ethtxs.DeployPara{
DeployPrivateKey: genesiskey,
Deployer: genesisAddr,
Operator: genesisAddr,
InitValidators: InitValidators,
ValidatorPriKey: ValidatorPriKey,
InitPowers: InitPowers,
}
return sim, para
}
func PrepareTestEnvironment(deployerPrivateKey string, ethValidatorAddrKeys []string) (bind.ContractBackend, *ethtxs.DeployPara) {
genesiskey, _ := crypto.HexToECDSA(deployerPrivateKey)
alloc := make(core.GenesisAlloc)
genesisAddr := crypto.PubkeyToAddress(genesiskey.PublicKey)
genesisAccount := core.GenesisAccount{
Balance: big.NewInt(10000000000 * 10000),
PrivateKey: crypto.FromECDSA(genesiskey),
}
alloc[genesisAddr] = genesisAccount
var InitValidators []common.Address
var ValidatorPriKey []*ecdsa.PrivateKey
for _, v := range ethValidatorAddrKeys {
key, _ := crypto.HexToECDSA(v)
addr := crypto.PubkeyToAddress(key.PublicKey)
InitValidators = append(InitValidators, addr)
ValidatorPriKey = append(ValidatorPriKey, key)
account := core.GenesisAccount{
Balance: big.NewInt(100000000 * 100),
PrivateKey: crypto.FromECDSA(key),
}
alloc[addr] = account
}
gasLimit := uint64(100000000)
sim := backends.NewSimulatedBackend(alloc, gasLimit)
InitPowers := []*big.Int{big.NewInt(80), big.NewInt(10), big.NewInt(10), big.NewInt(10)}
para := &ethtxs.DeployPara{
DeployPrivateKey: genesiskey,
Deployer: genesisAddr,
Operator: genesisAddr,
InitValidators: InitValidators,
ValidatorPriKey: ValidatorPriKey,
InitPowers: InitPowers,
}
return sim, para
}
func DeployContracts() (*ethtxs.DeployPara, *ethinterface.SimExtend, *ethtxs.X2EthContracts, *ethtxs.X2EthDeployInfo, error) {
ctx := context.Background()
sim, para := PrepareTestEnv()
callMsg := ethereum.CallMsg{
From: para.Deployer,
Data: common.FromHex(generated.BridgeBankBin),
}
_, err := sim.EstimateGas(ctx, callMsg)
if nil != err {
panic("failed to estimate gas due to:" + err.Error())
}
x2EthContracts, x2EthDeployInfo, err := ethtxs.DeployAndInit(sim, para)
if nil != err {
return nil, nil, nil, nil, err
}
sim.Commit()
return para, sim, x2EthContracts, x2EthDeployInfo, nil
}
package ethinterface
import (
"context"
"math/big"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)
type EthClientSpec interface {
bind.ContractBackend
TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error)
BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error)
HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error)
}
type SimExtend struct {
*backends.SimulatedBackend
}
func (sim *SimExtend) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
return sim.Blockchain().CurrentBlock().Header(), nil
}
//func (sim *SimExtend) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
// receipt, err := sim.SimulatedBackend.TransactionReceipt(ctx, txHash)
// if receipt == nil {
// err = errors.New("not found")
// }
//
// return receipt, err
//}
package ethtxs
import (
"strings"
"github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/ethcontract/generated"
"github.com/ethereum/go-ethereum/accounts/abi"
)
const (
BridgeBankABI = "BridgeBankABI"
Chain33BankABI = "Chain33BankABI"
Chain33BridgeABI = "Chain33BridgeABI"
EthereumBankABI = "EthereumBankABI"
)
func LoadABI(contractName string) abi.ABI {
var abiJSON string
switch contractName {
case BridgeBankABI:
abiJSON = generated.BridgeBankABI
case Chain33BankABI:
abiJSON = generated.Chain33BankABI
case Chain33BridgeABI:
abiJSON = generated.Chain33BridgeABI
case EthereumBankABI:
abiJSON = generated.EthereumBankABI
default:
panic("No abi matched")
}
// Convert the raw abi into a usable format
contractABI, err := abi.JSON(strings.NewReader(abiJSON))
if err != nil {
panic(err)
}
return contractABI
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
package ethtxs
import (
"context"
"crypto/ecdsa"
"math/big"
"testing"
chain33Common "github.com/33cn/chain33/common"
"github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/ethcontract/generated"
"github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/ethinterface"
"github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/events"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_LoadABI(t *testing.T) {
abi1 := LoadABI(Chain33BankABI)
abi2 := LoadABI(Chain33BridgeABI)
abi3 := LoadABI(EthereumBankABI)
assert.NotEmpty(t, abi1, abi2, abi3)
}
func Test_isWebsocketURL(t *testing.T) {
bret := isWebsocketURL("ws://127.0.0.1:7545/")
assert.Equal(t, bret, true)
bret = isWebsocketURL("https://127.0.0.1:7545/")
assert.Equal(t, bret, false)
}
func TestContractRegistry_String(t *testing.T) {
assert.Equal(t, Valset.String(), "valset")
assert.Equal(t, Oracle.String(), "oracle")
assert.Equal(t, BridgeBank.String(), "bridgebank")
assert.Equal(t, Chain33Bridge.String(), "chain33bridge")
}
func Test_GetAddressFromBridgeRegistry(t *testing.T) {
genesiskey, _ := crypto.GenerateKey()
alloc := make(core.GenesisAlloc)
genesisAddr := crypto.PubkeyToAddress(genesiskey.PublicKey)
genesisAccount := core.GenesisAccount{
Balance: big.NewInt(10000000000 * 10000),
PrivateKey: crypto.FromECDSA(genesiskey),
}
alloc[genesisAddr] = genesisAccount
gasLimit := uint64(100000000)
sim := new(ethinterface.SimExtend)
sim.SimulatedBackend = backends.NewSimulatedBackend(alloc, gasLimit)
bridgebankTest := ContractRegistry(5)
_, err := GetAddressFromBridgeRegistry(sim, genesisAddr, genesisAddr, bridgebankTest)
require.NotNil(t, err)
}
func Test_RelayOracleClaimToEthereum(t *testing.T) {
para, sim, x2EthContracts, _, err := deployContracts()
require.NoError(t, err)
claimType := events.MsgBurn
privateKeySlice, err := chain33Common.FromHex("0x3fa21584ae2e4fd74db9b58e2386f5481607dfa4d7ba0617aaa7858e5025dc1e")
require.Nil(t, err)
privateKey, err := crypto.ToECDSA(privateKeySlice)
require.Nil(t, err)
prophecyClaim := ProphecyClaim{
ClaimType: events.MsgBurn,
Chain33Sender: []byte("12qyocayNF7Lv6C9qW4avxs2E7U41fKSfv"),
EthereumReceiver: common.HexToAddress("0x0C05bA5c230fDaA503b53702aF1962e08D0C60BF"),
TokenContractAddress: common.HexToAddress("0x0000000000000000000000000000000000000000"),
Symbol: "eth",
Amount: big.NewInt(100000000000000000),
}
chain33TxHash := common.Hex2Bytes("fd5747c43d1460bb6f8a7a26c66b4ccab5500d05668278efe5c0fd5951dfd909")
txhash, err := RelayOracleClaimToEthereum(x2EthContracts.Oracle, sim, para.InitValidators[0], claimType, prophecyClaim, privateKey, chain33TxHash)
require.Nil(t, err)
assert.Equal(t, txhash, "0x6fa087c7a2a8a4421f6e269fbc6c0838e99fa59d5760155a71cd7eb1c01aafad")
//hash := "0xc0c22aa6198fdde0dbe47ddadbe449f736b82ed4a498871de5d5f4ad9ae122a0"
//status := GetEthTxStatus(sim, common.HexToHash(hash))
//assert.Equal(t, status, EthTxPending.String())
_, err = revokeNonce(para.Operator)
require.Nil(t, err)
}
func Test_revokeNonce(t *testing.T) {
}
func deployContracts() (*DeployPara, *ethinterface.SimExtend, *X2EthContracts, *X2EthDeployInfo, error) {
// 0x8AFDADFC88a1087c9A1D6c0F5Dd04634b87F303a
deployerPrivateKey := "8656d2bc732a8a816a461ba5e2d8aac7c7f85c26a813df30d5327210465eb230"
// 0x92C8b16aFD6d423652559C6E266cBE1c29Bfd84f
ethValidatorAddrKeyA := "3fa21584ae2e4fd74db9b58e2386f5481607dfa4d7ba0617aaa7858e5025dc1e"
ethValidatorAddrKeyB := "a5f3063552f4483cfc20ac4f40f45b798791379862219de9e915c64722c1d400"
ethValidatorAddrKeyC := "bbf5e65539e9af0eb0cfac30bad475111054b09c11d668fc0731d54ea777471e"
ethValidatorAddrKeyD := "c9fa31d7984edf81b8ef3b40c761f1847f6fcd5711ab2462da97dc458f1f896b"
ethValidatorAddrKeys := make([]string, 0)
ethValidatorAddrKeys = append(ethValidatorAddrKeys, ethValidatorAddrKeyA)
ethValidatorAddrKeys = append(ethValidatorAddrKeys, ethValidatorAddrKeyB)
ethValidatorAddrKeys = append(ethValidatorAddrKeys, ethValidatorAddrKeyC)
ethValidatorAddrKeys = append(ethValidatorAddrKeys, ethValidatorAddrKeyD)
ctx := context.Background()
//var backend bind.ContractBackend
backend, para := PrepareTestEnvironment(deployerPrivateKey, ethValidatorAddrKeys)
sim := new(ethinterface.SimExtend)
sim.SimulatedBackend = backend.(*backends.SimulatedBackend)
callMsg := ethereum.CallMsg{
From: para.Deployer,
Data: common.FromHex(generated.BridgeBankBin),
}
_, err := sim.EstimateGas(ctx, callMsg)
if nil != err {
panic("failed to estimate gas due to:" + err.Error())
}
x2EthContracts, x2EthDeployInfo, err := DeployAndInit(sim, para)
if nil != err {
return nil, nil, nil, nil, err
}
sim.Commit()
return para, sim, x2EthContracts, x2EthDeployInfo, nil
}
func PrepareTestEnvironment(deployerPrivateKey string, ethValidatorAddrKeys []string) (bind.ContractBackend, *DeployPara) {
genesiskey, _ := crypto.HexToECDSA(deployerPrivateKey)
alloc := make(core.GenesisAlloc)
genesisAddr := crypto.PubkeyToAddress(genesiskey.PublicKey)
genesisAccount := core.GenesisAccount{
Balance: big.NewInt(10000000000 * 10000),
PrivateKey: crypto.FromECDSA(genesiskey),
}
alloc[genesisAddr] = genesisAccount
var InitValidators []common.Address
var ValidatorPriKey []*ecdsa.PrivateKey
for _, v := range ethValidatorAddrKeys {
key, _ := crypto.HexToECDSA(v)
addr := crypto.PubkeyToAddress(key.PublicKey)
InitValidators = append(InitValidators, addr)
ValidatorPriKey = append(ValidatorPriKey, key)
account := core.GenesisAccount{
Balance: big.NewInt(100000000 * 100),
PrivateKey: crypto.FromECDSA(key),
}
alloc[addr] = account
}
gasLimit := uint64(100000000)
sim := backends.NewSimulatedBackend(alloc, gasLimit)
InitPowers := []*big.Int{big.NewInt(80), big.NewInt(10), big.NewInt(10), big.NewInt(10)}
para := &DeployPara{
DeployPrivateKey: genesiskey,
Deployer: genesisAddr,
Operator: genesisAddr,
InitValidators: InitValidators,
ValidatorPriKey: ValidatorPriKey,
InitPowers: InitPowers,
}
return sim, para
}
package ethtxs
// ------------------------------------------------------------
// Network: Validates input and initializes a websocket Ethereum client.
// ------------------------------------------------------------
import (
"fmt"
"net/url"
"strings"
"github.com/ethereum/go-ethereum/ethclient"
log "github.com/golang/glog"
)
// IsWebsocketURL : returns true if the given URL is a websocket URL
func isWebsocketURL(rawurl string) bool {
u, err := url.Parse(rawurl)
if err != nil {
log.Infof("Error while parsing URL: %v", err)
return false
}
return u.Scheme == "ws" || u.Scheme == "wss"
}
// SetupWebsocketEthClient : returns boolean indicating if a URL is valid websocket ethclient
func SetupWebsocketEthClient(ethURL string) (*ethclient.Client, error) {
if strings.TrimSpace(ethURL) == "" {
return nil, nil
}
if !isWebsocketURL(ethURL) {
return nil, fmt.Errorf("invalid websocket eth client URL: %v", ethURL)
}
client, err := ethclient.Dial(ethURL)
if err != nil {
return nil, fmt.Errorf("error dialing websocket client %w", err)
}
return client, nil
}
package ethtxs
// --------------------------------------------------------
// Parser
//
// Parses structs containing event information into
// unsigned transactions for validators to sign, then
// relays the data packets as transactions on the
// chain33 Bridge.
// --------------------------------------------------------
import (
"math/big"
"strings"
chain33Types "github.com/33cn/chain33/types"
"github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/events"
ebrelayerTypes "github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/types"
"github.com/33cn/plugin/plugin/dapp/x2ethereum/types"
"github.com/ethereum/go-ethereum/common"
)
// LogLockToEthBridgeClaim : parses and packages a LockEvent struct with a validator address in an EthBridgeClaim msg
func LogLockToEthBridgeClaim(event *events.LockEvent, ethereumChainID int64, bridgeBrankAddr string, decimal int64) (*ebrelayerTypes.EthBridgeClaim, error) {
recipient := event.To
if 0 == len(recipient) {
return nil, ebrelayerTypes.ErrEmptyAddress
}
// Symbol formatted to lowercase
symbol := strings.ToLower(event.Symbol)
if symbol == "eth" && event.Token != common.HexToAddress("0x0000000000000000000000000000000000000000") {
return nil, ebrelayerTypes.ErrAddress4Eth
}
witnessClaim := &ebrelayerTypes.EthBridgeClaim{}
witnessClaim.EthereumChainID = ethereumChainID
witnessClaim.BridgeBrankAddr = bridgeBrankAddr
witnessClaim.Nonce = event.Nonce.Int64()
witnessClaim.TokenAddr = event.Token.String()
witnessClaim.Symbol = event.Symbol
witnessClaim.EthereumSender = event.From.String()
witnessClaim.Chain33Receiver = string(recipient)
if decimal > 8 {
event.Value = event.Value.Quo(event.Value, big.NewInt(int64(types.MultiplySpecifyTimes(1, decimal-8))))
} else {
event.Value = event.Value.Mul(event.Value, big.NewInt(int64(types.MultiplySpecifyTimes(1, 8-decimal))))
}
witnessClaim.Amount = event.Value.String()
witnessClaim.ClaimType = types.LockClaimType
witnessClaim.ChainName = types.LockClaim
witnessClaim.Decimal = decimal
return witnessClaim, nil
}
func LogBurnToEthBridgeClaim(event *events.BurnEvent, ethereumChainID int64, bridgeBrankAddr string, decimal int64) (*ebrelayerTypes.EthBridgeClaim, error) {
recipient := event.Chain33Receiver
if 0 == len(recipient) {
return nil, ebrelayerTypes.ErrEmptyAddress
}
witnessClaim := &ebrelayerTypes.EthBridgeClaim{}
witnessClaim.EthereumChainID = ethereumChainID
witnessClaim.BridgeBrankAddr = bridgeBrankAddr
witnessClaim.Nonce = event.Nonce.Int64()
witnessClaim.TokenAddr = event.Token.String()
witnessClaim.Symbol = event.Symbol
witnessClaim.EthereumSender = event.OwnerFrom.String()
witnessClaim.Chain33Receiver = string(recipient)
witnessClaim.Amount = event.Amount.String()
witnessClaim.ClaimType = types.BurnClaimType
witnessClaim.ChainName = types.BurnClaim
witnessClaim.Decimal = decimal
return witnessClaim, nil
}
// ParseBurnLockTxReceipt : parses data from a Burn/Lock event witnessed on chain33 into a Chain33Msg struct
func ParseBurnLockTxReceipt(claimType events.Event, receipt *chain33Types.ReceiptData) *events.Chain33Msg {
// Set up variables
var chain33Sender []byte
var ethereumReceiver, tokenContractAddress common.Address
var symbol string
var amount *big.Int
// Iterate over attributes
for _, log := range receipt.Logs {
if log.Ty == types.TyChain33ToEthLog || log.Ty == types.TyWithdrawChain33Log {
txslog.Debug("ParseBurnLockTxReceipt", "value", string(log.Log))
var chain33ToEth types.ReceiptChain33ToEth
err := chain33Types.Decode(log.Log, &chain33ToEth)
if err != nil {
return nil
}
chain33Sender = []byte(chain33ToEth.Chain33Sender)
ethereumReceiver = common.HexToAddress(chain33ToEth.EthereumReceiver)
tokenContractAddress = common.HexToAddress(chain33ToEth.TokenContract)
symbol = chain33ToEth.IssuerDotSymbol
chain33ToEth.Amount = types.TrimZeroAndDot(chain33ToEth.Amount)
amount = big.NewInt(1)
amount, _ = amount.SetString(chain33ToEth.Amount, 10)
if chain33ToEth.Decimals > 8 {
amount = amount.Mul(amount, big.NewInt(int64(types.MultiplySpecifyTimes(1, chain33ToEth.Decimals-8))))
} else {
amount = amount.Quo(amount, big.NewInt(int64(types.MultiplySpecifyTimes(1, 8-chain33ToEth.Decimals))))
}
txslog.Info("ParseBurnLockTxReceipt", "chain33Sender", chain33Sender, "ethereumReceiver", ethereumReceiver.String(), "tokenContractAddress", tokenContractAddress.String(), "symbol", symbol, "amount", amount.String())
// Package the event data into a Chain33Msg
chain33Msg := events.NewChain33Msg(claimType, chain33Sender, ethereumReceiver, symbol, amount, tokenContractAddress)
return &chain33Msg
}
}
return nil
}
// Chain33MsgToProphecyClaim : parses event data from a Chain33Msg, packaging it as a ProphecyClaim
func Chain33MsgToProphecyClaim(event events.Chain33Msg) ProphecyClaim {
claimType := event.ClaimType
chain33Sender := event.Chain33Sender
ethereumReceiver := event.EthereumReceiver
tokenContractAddress := event.TokenContractAddress
symbol := strings.ToLower(event.Symbol)
amount := event.Amount
prophecyClaim := ProphecyClaim{
ClaimType: claimType,
Chain33Sender: chain33Sender,
EthereumReceiver: ethereumReceiver,
TokenContractAddress: tokenContractAddress,
Symbol: symbol,
Amount: amount,
}
return prophecyClaim
}
package ethtxs
import (
"context"
"errors"
"github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/ethcontract/generated"
"github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/ethinterface"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
)
func GetOperator(client ethinterface.EthClientSpec, sender, bridgeBank common.Address) (common.Address, error) {
header, err := client.HeaderByNumber(context.Background(), nil)
if err != nil {
txslog.Error("GetOperator", "Failed to get HeaderByNumber due to:", err.Error())
return common.Address{}, err
}
// Set up CallOpts auth
auth := bind.CallOpts{
Pending: true,
From: sender,
BlockNumber: header.Number,
Context: context.Background(),
}
// Initialize BridgeRegistry instance
bridgeBankInstance, err := generated.NewBridgeBank(bridgeBank, client)
if err != nil {
txslog.Error("GetOperator", "Failed to NewBridgeBank to:", err.Error())
return common.Address{}, err
}
return bridgeBankInstance.Operator(&auth)
}
func IsActiveValidator(validator common.Address, valset *generated.Valset) (bool, error) {
opts := &bind.CallOpts{
Pending: true,
From: validator,
Context: context.Background(),
}
// Initialize BridgeRegistry instance
isActiveValidator, err := valset.IsActiveValidator(opts, validator)
if err != nil {
txslog.Error("IsActiveValidator", "Failed to query IsActiveValidator due to:", err.Error())
return false, err
}
return isActiveValidator, nil
}
func IsProphecyPending(claimID [32]byte, validator common.Address, chain33Bridge *generated.Chain33Bridge) (bool, error) {
opts := &bind.CallOpts{
Pending: true,
From: validator,
Context: context.Background(),
}
// Initialize BridgeRegistry instance
active, err := chain33Bridge.IsProphecyClaimActive(opts, claimID)
if err != nil {
txslog.Error("IsActiveValidatorFromChain33Bridge", "Failed to query IsActiveValidator due to:", err.Error())
return false, err
}
return active, nil
}
func GetBalance(client ethinterface.EthClientSpec, tokenAddr, owner string) (string, error) {
//查询ERC20余额
if tokenAddr != "" {
bridgeToken, err := generated.NewBridgeToken(common.HexToAddress(tokenAddr), client)
if nil != err {
return "", err
}
ownerAddr := common.HexToAddress(owner)
opts := &bind.CallOpts{
Pending: true,
From: ownerAddr,
Context: context.Background(),
}
balance, err := bridgeToken.BalanceOf(opts, ownerAddr)
if nil != err {
return "", err
}
return balance.String(), nil
}
//查询ETH余额
balance, err := client.BalanceAt(context.Background(), common.HexToAddress(owner), nil)
if nil != err {
return "", err
}
return balance.String(), nil
}
func GetLockedFunds(bridgeBank *generated.BridgeBank, tokenAddrStr string) (string, error) {
var tokenAddr common.Address
if tokenAddrStr != "" {
tokenAddr = common.HexToAddress(tokenAddrStr)
}
opts := &bind.CallOpts{
Pending: true,
From: tokenAddr,
Context: context.Background(),
}
balance, err := bridgeBank.LockedFunds(opts, tokenAddr)
if nil != err {
return "", err
}
return balance.String(), nil
}
func GetDepositFunds(client bind.ContractBackend, tokenAddrStr string) (string, error) {
if tokenAddrStr == "" {
return "", errors.New("nil token address")
}
tokenAddr := common.HexToAddress(tokenAddrStr)
bridgeToken, err := generated.NewBridgeToken(tokenAddr, client)
if nil != err {
return "", err
}
opts := &bind.CallOpts{
Pending: true,
From: tokenAddr,
Context: context.Background(),
}
supply, err := bridgeToken.TotalSupply(opts)
if nil != err {
return "", err
}
return supply.String(), nil
}
func GetToken2address(bridgeBank *generated.BridgeBank, tokenSymbol string) (string, error) {
opts := &bind.CallOpts{
Pending: true,
Context: context.Background(),
}
tokenAddr, err := bridgeBank.GetToken2address(opts, tokenSymbol)
if nil != err {
return "", err
}
txslog.Info("GetToken2address", "Name", tokenSymbol, "Address", tokenAddr.String())
return tokenAddr.String(), nil
}
package ethtxs
import (
"errors"
"github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/ethcontract/generated"
"github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/ethinterface"
"github.com/ethereum/go-ethereum/common"
)
func RecoverContractHandler(client ethinterface.EthClientSpec, sender, registry common.Address) (*X2EthContracts, *X2EthDeployInfo, error) {
bridgeBankAddr, err := GetAddressFromBridgeRegistry(client, sender, registry, BridgeBank)
if nil != err {
return nil, nil, errors.New("failed to get addr for bridgeBank from registry")
}
bridgeBank, err := generated.NewBridgeBank(*bridgeBankAddr, client)
if nil != err {
return nil, nil, errors.New("failed to NewBridgeBank")
}
chain33BridgeAddr, err := GetAddressFromBridgeRegistry(client, sender, registry, Chain33Bridge)
if nil != err {
return nil, nil, errors.New("failed to get addr for chain33BridgeAddr from registry")
}
chain33Bridge, err := generated.NewChain33Bridge(*chain33BridgeAddr, client)
if nil != err {
return nil, nil, errors.New("failed to NewChain33Bridge")
}
oracleAddr, err := GetAddressFromBridgeRegistry(client, sender, registry, Oracle)
if nil != err {
return nil, nil, errors.New("failed to get addr for oracleBridgeAddr from registry")
}
oracle, err := generated.NewOracle(*oracleAddr, client)
if nil != err {
return nil, nil, errors.New("failed to NewOracle")
}
valsetAddr, err := GetAddressFromBridgeRegistry(client, sender, registry, Valset)
if nil != err {
return nil, nil, errors.New("failed to get addr for valset from registry")
}
valset, err := generated.NewValset(*valsetAddr, client)
if nil != err {
return nil, nil, errors.New("failed to NewValset")
}
registryInstance, _ := generated.NewBridgeRegistry(registry, client)
x2EthContracts := &X2EthContracts{
BridgeRegistry: registryInstance,
BridgeBank: bridgeBank,
Chain33Bridge: chain33Bridge,
Oracle: oracle,
Valset: valset,
}
x2EthDeployInfo := &X2EthDeployInfo{
BridgeRegistry: &DeployResult{Address: registry},
BridgeBank: &DeployResult{Address: *bridgeBankAddr},
Chain33Bridge: &DeployResult{Address: *chain33BridgeAddr},
Oracle: &DeployResult{Address: *oracleAddr},
Valset: &DeployResult{Address: *valsetAddr},
}
return x2EthContracts, x2EthDeployInfo, nil
}
func RecoverOracleInstance(client ethinterface.EthClientSpec, sender, registry common.Address) (*generated.Oracle, error) {
oracleAddr, err := GetAddressFromBridgeRegistry(client, sender, registry, Oracle)
if nil != err {
return nil, errors.New("failed to get addr for oracleBridgeAddr from registry")
}
oracle, err := generated.NewOracle(*oracleAddr, client)
if nil != err {
return nil, errors.New("failed to NewOracle")
}
return oracle, nil
}
package ethtxs
import (
"context"
"log"
bridgeRegistry "github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/ethcontract/generated"
"github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/ethinterface"
ebrelayerTypes "github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/types"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
)
// ContractRegistry :
type ContractRegistry byte
const (
// Valset : valset contract
Valset ContractRegistry = iota + 1
// Oracle : oracle contract
Oracle
// BridgeBank : bridgeBank contract
BridgeBank
// Chain33Bridge : chain33Bridge contract
Chain33Bridge
)
// String : returns the event type as a string
func (d ContractRegistry) String() string {
return [...]string{"valset", "oracle", "bridgebank", "chain33bridge", "notsupport"}[d-1]
}
// GetAddressFromBridgeRegistry : utility method which queries the requested contract address from the BridgeRegistry
func GetAddressFromBridgeRegistry(client ethinterface.EthClientSpec, sender, registry common.Address, target ContractRegistry) (address *common.Address, err error) {
header, err := client.HeaderByNumber(context.Background(), nil)
if err != nil {
txslog.Error("GetAddressFromBridgeRegistry", "Failed to get HeaderByNumber due to:", err.Error())
return nil, err
}
// Set up CallOpts auth
auth := bind.CallOpts{
Pending: true,
From: sender,
BlockNumber: header.Number,
Context: context.Background(),
}
// Initialize BridgeRegistry instance
registryInstance, err := bridgeRegistry.NewBridgeRegistry(registry, client)
if err != nil {
txslog.Error("GetAddressFromBridgeRegistry", "Failed to NewBridgeRegistry to:", err.Error())
return nil, err
}
switch target {
case Valset:
valsetAddress, err := registryInstance.Valset(&auth)
if err != nil {
log.Fatal(err)
}
return &valsetAddress, nil
case Oracle:
oracleAddress, err := registryInstance.Oracle(&auth)
if err != nil {
log.Fatal(err)
}
return &oracleAddress, nil
case BridgeBank:
bridgeBankAddress, err := registryInstance.BridgeBank(&auth)
if err != nil {
log.Fatal(err)
}
return &bridgeBankAddress, nil
case Chain33Bridge:
chain33BridgeAddress, err := registryInstance.Chain33Bridge(&auth)
if err != nil {
log.Fatal(err)
}
return &chain33BridgeAddress, nil
default:
txslog.Error("GetAddressFromBridgeRegistry", "invalid target contract type:", target)
return nil, ebrelayerTypes.ErrInvalidContractAddress
}
}
// GetDeployHeight : 获取合约部署高度
func GetDeployHeight(client ethinterface.EthClientSpec, sender, registry common.Address) (height int64, err error) {
header, err := client.HeaderByNumber(context.Background(), nil)
if err != nil {
txslog.Error("GetAddressFromBridgeRegistry", "Failed to get HeaderByNumber due to:", err.Error())
return 0, err
}
// Set up CallOpts auth
callOpts := &bind.CallOpts{
Pending: true,
From: sender,
BlockNumber: header.Number,
Context: context.Background(),
}
// Initialize BridgeRegistry instance
registryInstance, err := bridgeRegistry.NewBridgeRegistry(registry, client)
if err != nil {
txslog.Error("GetAddressFromBridgeRegistry", "Failed to NewBridgeRegistry to:", err.Error())
return 0, err
}
bgInt, err := registryInstance.DeployHeight(callOpts)
if nil != err {
return 0, err
}
height = bgInt.Int64()
txslog.Info("GetDeployHeight", "deploy height:", height)
return
}
package ethtxs
// ------------------------------------------------------------
// Relay : Builds and encodes EthBridgeClaim Msgs with the
// specified variables, before presenting the unsigned
// transaction to validators for optional signing.
// Once signed, the data packets are sent as transactions
// on the chain33 Bridge.
// ------------------------------------------------------------
import (
"github.com/33cn/chain33/common"
chain33Crypto "github.com/33cn/chain33/common/crypto"
"github.com/33cn/chain33/rpc/jsonclient"
rpctypes "github.com/33cn/chain33/rpc/types"
chain33Types "github.com/33cn/chain33/types"
ebrelayerTypes "github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/types"
"github.com/33cn/plugin/plugin/dapp/x2ethereum/types"
)
// RelayLockToChain33 : RelayLockToChain33 applies validator's signature to an EthBridgeClaim message
// containing information about an event on the Ethereum blockchain before relaying to the Bridge
func RelayLockToChain33(privateKey chain33Crypto.PrivKey, claim *ebrelayerTypes.EthBridgeClaim, rpcURL string) (string, error) {
var res string
params := &types.Eth2Chain33{
EthereumChainID: claim.EthereumChainID,
BridgeContractAddress: claim.BridgeBrankAddr,
Nonce: claim.Nonce,
IssuerDotSymbol: claim.Symbol,
TokenContractAddress: claim.TokenAddr,
EthereumSender: claim.EthereumSender,
Chain33Receiver: claim.Chain33Receiver,
Amount: claim.Amount,
ClaimType: int64(claim.ClaimType),
Decimals: claim.Decimal,
}
pm := rpctypes.CreateTxIn{
Execer: X2Eth,
ActionName: types.NameEth2Chain33Action,
Payload: chain33Types.MustPBToJSON(params),
}
ctx := jsonclient.NewRPCCtx(rpcURL, "Chain33.CreateTransaction", pm, &res)
_, _ = ctx.RunResult()
data, err := common.FromHex(res)
if err != nil {
return "", err
}
var tx chain33Types.Transaction
err = chain33Types.Decode(data, &tx)
if err != nil {
return "", err
}
if tx.Fee == 0 {
tx.Fee, err = tx.GetRealFee(1e5)
if err != nil {
return "", err
}
}
//构建交易,验证人validator用来向chain33合约证明自己验证了该笔从以太坊向chain33跨链转账的交易
tx.Sign(chain33Types.SECP256K1, privateKey)
txData := chain33Types.Encode(&tx)
dataStr := common.ToHex(txData)
pms := rpctypes.RawParm{
Token: "BTY",
Data: dataStr,
}
var txhash string
ctx = jsonclient.NewRPCCtx(rpcURL, "Chain33.SendTransaction", pms, &txhash)
_, err = ctx.RunResult()
return txhash, err
}
func RelayBurnToChain33(privateKey chain33Crypto.PrivKey, claim *ebrelayerTypes.EthBridgeClaim, rpcURL string) (string, error) {
var res string
params := &types.Eth2Chain33{
EthereumChainID: claim.EthereumChainID,
BridgeContractAddress: claim.BridgeBrankAddr,
Nonce: claim.Nonce,
IssuerDotSymbol: claim.Symbol,
TokenContractAddress: claim.TokenAddr,
EthereumSender: claim.EthereumSender,
Chain33Receiver: claim.Chain33Receiver,
Amount: claim.Amount,
ClaimType: int64(claim.ClaimType),
Decimals: claim.Decimal,
}
pm := rpctypes.CreateTxIn{
Execer: X2Eth,
ActionName: types.NameWithdrawEthAction,
Payload: chain33Types.MustPBToJSON(params),
}
ctx := jsonclient.NewRPCCtx(rpcURL, "Chain33.CreateTransaction", pm, &res)
_, _ = ctx.RunResult()
data, err := common.FromHex(res)
if err != nil {
return "", err
}
var tx chain33Types.Transaction
err = chain33Types.Decode(data, &tx)
if err != nil {
return "", err
}
if tx.Fee == 0 {
tx.Fee, err = tx.GetRealFee(1e5)
if err != nil {
return "", err
}
}
//构建交易,验证人validator用来向chain33合约证明自己验证了该笔从以太坊向chain33跨链转账的交易
tx.Sign(chain33Types.SECP256K1, privateKey)
txData := chain33Types.Encode(&tx)
dataStr := common.ToHex(txData)
pms := rpctypes.RawParm{
Token: "BTY",
Data: dataStr,
}
var txhash string
ctx = jsonclient.NewRPCCtx(rpcURL, "Chain33.SendTransaction", pms, &txhash)
_, err = ctx.RunResult()
return txhash, err
}
package ethtxs
import (
"fmt"
"testing"
"github.com/33cn/chain33/client/mocks"
chain33Common "github.com/33cn/chain33/common"
_ "github.com/33cn/chain33/system"
"github.com/33cn/chain33/system/crypto/secp256k1"
chain33Types "github.com/33cn/chain33/types"
"github.com/33cn/chain33/util/testnode"
ebrelayerTypes "github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/types"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)
var (
chainTestCfg = chain33Types.NewChain33Config(chain33Types.GetDefaultCfgstring())
)
func Test_RelayToChain33(t *testing.T) {
var tx chain33Types.Transaction
var ret chain33Types.Reply
ret.IsOk = true
mockapi := &mocks.QueueProtocolAPI{}
// 这里对需要mock的方法打桩,Close是必须的,其它方法根据需要
mockapi.On("Close").Return()
mockapi.On("AddPushSubscribe", mock.Anything).Return(&ret, nil)
mockapi.On("CreateTransaction", mock.Anything).Return(&tx, nil)
mockapi.On("SendTx", mock.Anything).Return(&ret, nil)
mockapi.On("SendTransaction", mock.Anything).Return(&ret, nil)
mockapi.On("GetConfig", mock.Anything).Return(chainTestCfg, nil)
mock33 := testnode.New("", mockapi)
defer mock33.Close()
rpcCfg := mock33.GetCfg().RPC
// 这里必须设置监听端口,默认的是无效值
rpcCfg.JrpcBindAddr = "127.0.0.1:8801"
mock33.GetRPC().Listen()
chain33PrivateKeyStr := "0xd627968e445f2a41c92173225791bae1ba42126ae96c32f28f97ff8f226e5c68"
var driver secp256k1.Driver
privateKeySli, err := chain33Common.FromHex(chain33PrivateKeyStr)
require.Nil(t, err)
priKey, err := driver.PrivKeyFromBytes(privateKeySli)
require.Nil(t, err)
claim := &ebrelayerTypes.EthBridgeClaim{}
fmt.Println("======================= testRelayLockToChain33 =======================")
_, err = RelayLockToChain33(priKey, claim, "http://127.0.0.1:8801")
require.Nil(t, err)
fmt.Println("======================= testRelayBurnToChain33 =======================")
_, err = RelayBurnToChain33(priKey, claim, "http://127.0.0.1:8801")
require.Nil(t, err)
}
package ethtxs
import (
"crypto/ecdsa"
"github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/ethinterface"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/33cn/chain33/common/log/log15"
"github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/ethcontract/generated"
"github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/events"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)
var (
txslog = log15.New("ethereum relayer", "ethtxs")
)
const (
// GasLimit : the gas limit in Gwei used for transactions sent with TransactOpts
GasLimit = uint64(100 * 10000)
GasLimit4Deploy = uint64(0) //此处需要设置为0,让交易自行估计,否则将会导致部署失败,TODO:其他解决途径后续调研解决
)
// RelayOracleClaimToEthereum : relays the provided burn or lock to Chain33Bridge contract on the Ethereum network
func RelayOracleClaimToEthereum(oracleInstance *generated.Oracle, client ethinterface.EthClientSpec, sender common.Address, event events.Event, claim ProphecyClaim, privateKey *ecdsa.PrivateKey, chain33TxHash []byte) (txhash string, err error) {
txslog.Info("RelayProphecyClaimToEthereum", "sender", sender.String(), "event", event, "chain33Sender", hexutil.Encode(claim.Chain33Sender), "ethereumReceiver", claim.EthereumReceiver.String(), "TokenAddress", claim.TokenContractAddress.String(), "symbol", claim.Symbol, "Amount", claim.Amount.String(), "claimType", claim.ClaimType.String())
auth, err := PrepareAuth(client, privateKey, sender)
if nil != err {
txslog.Error("RelayProphecyClaimToEthereum", "PrepareAuth err", err.Error())
return "", err
}
auth.GasLimit = GasLimit
claimID := crypto.Keccak256Hash(chain33TxHash, claim.Chain33Sender, claim.EthereumReceiver.Bytes(), []byte(claim.Symbol), claim.Amount.Bytes())
// Sign the hash using the active validator's private key
signature, err := SignClaim4Eth(claimID, privateKey)
if nil != err {
return "", err
}
tx, err := oracleInstance.NewOracleClaim(auth, uint8(claim.ClaimType), claim.Chain33Sender, claim.EthereumReceiver, claim.TokenContractAddress, claim.Symbol, claim.Amount, claimID, signature)
if nil != err {
txslog.Error("RelayProphecyClaimToEthereum", "NewOracleClaim failed due to:", err.Error())
return "", err
}
txhash = tx.Hash().Hex()
txslog.Info("RelayProphecyClaimToEthereum", "NewOracleClaim tx hash:", txhash)
return txhash, nil
}
package ethtxs
import (
"math/big"
"github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/events"
"github.com/ethereum/go-ethereum/common"
)
const (
X2Eth = "x2ethereum"
BurnAction = "Chain33ToEthBurn"
LockAction = "Chain33ToEthLock"
)
// OracleClaim : contains data required to make an OracleClaim
type OracleClaim struct {
ProphecyID *big.Int
Message [32]byte
Signature []byte
}
// ProphecyClaim : contains data required to make an ProphecyClaim
type ProphecyClaim struct {
ClaimType events.Event
Chain33Sender []byte
EthereumReceiver common.Address
TokenContractAddress common.Address
Symbol string
Amount *big.Int
}
package ethtxs
import (
"context"
"crypto/ecdsa"
"errors"
"math/big"
"sync"
"time"
"github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/ethinterface"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/crypto/secp256k1"
solsha3 "github.com/miguelmota/go-solidity-sha3"
)
type EthTxStatus int32
type nonceMutex struct {
nonce int64
rw *sync.RWMutex
}
var addr2Nonce = make(map[common.Address]nonceMutex)
func (ethTxStatus EthTxStatus) String() string {
return [...]string{"Fail", "Success", "Pending"}[ethTxStatus]
}
const (
PendingDuration4TxExeuction = 300
EthTxPending = EthTxStatus(2)
)
func SignClaim4Eth(hash common.Hash, privateKey *ecdsa.PrivateKey) ([]byte, error) {
rawSignature, _ := prefixMessage(hash, privateKey)
signature := hexutil.Bytes(rawSignature)
return signature, nil
}
func prefixMessage(message common.Hash, key *ecdsa.PrivateKey) ([]byte, []byte) {
prefixed := solsha3.SoliditySHA3WithPrefix(message[:])
sig, err := secp256k1.Sign(prefixed, math.PaddedBigBytes(key.D, 32))
if err != nil {
panic(err)
}
return sig, prefixed
}
func getNonce(sender common.Address, client ethinterface.EthClientSpec) (*big.Int, error) {
if nonceMutex, exist := addr2Nonce[sender]; exist {
nonceMutex.rw.Lock()
defer nonceMutex.rw.Unlock()
nonceMutex.nonce++
addr2Nonce[sender] = nonceMutex
txslog.Debug("getNonce from cache", "address", sender.String(), "nonce", nonceMutex.nonce)
return big.NewInt(nonceMutex.nonce), nil
}
nonce, err := client.PendingNonceAt(context.Background(), sender)
if nil != err {
return nil, err
}
txslog.Debug("getNonce", "address", sender.String(), "nonce", nonce)
n := new(nonceMutex)
n.nonce = int64(nonce)
n.rw = new(sync.RWMutex)
addr2Nonce[sender] = *n
return big.NewInt(int64(nonce)), nil
}
func revokeNonce(sender common.Address) (*big.Int, error) {
if nonceMutex, exist := addr2Nonce[sender]; exist {
nonceMutex.rw.Lock()
defer nonceMutex.rw.Unlock()
nonceMutex.nonce--
addr2Nonce[sender] = nonceMutex
txslog.Debug("revokeNonce", "address", sender.String(), "nonce", nonceMutex.nonce)
return big.NewInt(nonceMutex.nonce), nil
}
return nil, errors.New("address doesn't exist tx")
}
func PrepareAuth(client ethinterface.EthClientSpec, privateKey *ecdsa.PrivateKey, transactor common.Address) (*bind.TransactOpts, error) {
if nil == privateKey || nil == client {
txslog.Error("PrepareAuth", "nil input parameter", "client", client, "privateKey", privateKey)
return nil, errors.New("nil input parameter")
}
ctx := context.Background()
gasPrice, err := client.SuggestGasPrice(ctx)
if err != nil {
txslog.Error("PrepareAuth", "Failed to SuggestGasPrice due to:", err.Error())
return nil, errors.New("failed to get suggest gas price")
}
auth := bind.NewKeyedTransactor(privateKey)
auth.Value = big.NewInt(0) // in wei
auth.GasLimit = GasLimit4Deploy
auth.GasPrice = gasPrice
if auth.Nonce, err = getNonce(transactor, client); err != nil {
return nil, err
}
return auth, nil
}
func waitEthTxFinished(client ethinterface.EthClientSpec, txhash common.Hash, txName string) error {
txslog.Info(txName, "Wait for tx to be finished executing with hash", txhash.String())
timeout := time.NewTimer(PendingDuration4TxExeuction * time.Second)
oneSecondtimeout := time.NewTicker(5 * time.Second)
for {
select {
case <-timeout.C:
return errors.New("eth tx timeout")
case <-oneSecondtimeout.C:
_, err := client.TransactionReceipt(context.Background(), txhash)
if err == ethereum.NotFound {
continue
} else if err != nil {
return err
}
txslog.Info(txName, "Finished executing for tx", txhash.String())
return nil
}
}
}
func GetEthTxStatus(client ethinterface.EthClientSpec, txhash common.Hash) string {
receipt, err := client.TransactionReceipt(context.Background(), txhash)
if nil != err {
return EthTxPending.String()
}
status := EthTxStatus(receipt.Status).String()
if status != EthTxPending.String() {
txslog.Info("GetEthTxStatus", "Eth tx hash", txhash.String(), "status", status, "BlockNum", receipt.BlockNumber.Int64())
}
return status
}
package events
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
)
// Chain33Msg : contains data from MsgBurn and MsgLock events
type Chain33Msg struct {
ClaimType Event
Chain33Sender []byte
EthereumReceiver common.Address
TokenContractAddress common.Address
Symbol string
Amount *big.Int
}
// NewChain33Msg : creates a new Chain33Msg
func NewChain33Msg(
claimType Event,
chain33Sender []byte,
ethereumReceiver common.Address,
symbol string,
amount *big.Int,
tokenContractAddress common.Address,
) Chain33Msg {
// Package data into a Chain33Msg
chain33Msg := Chain33Msg{
ClaimType: claimType,
Chain33Sender: chain33Sender,
EthereumReceiver: ethereumReceiver,
Symbol: symbol,
Amount: amount,
TokenContractAddress: tokenContractAddress,
}
return chain33Msg
}
package events
// -----------------------------------------------------
// ethereumEvent : Creates LockEvents from new events on the
// Ethereum blockchain.
// -----------------------------------------------------
import (
"math/big"
ebrelayerTypes "github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/types"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
)
// LockEvent : struct which represents a LogLock event
type LockEvent struct {
From common.Address
To []byte
Token common.Address
Symbol string
Value *big.Int
Nonce *big.Int
}
// BurnEvent : struct which represents a BurnEvent event
type BurnEvent struct {
Token common.Address
Symbol string
Amount *big.Int
OwnerFrom common.Address
Chain33Receiver []byte
Nonce *big.Int
}
// NewProphecyClaimEvent : struct which represents a LogNewProphecyClaim event
type NewProphecyClaimEvent struct {
ProphecyID *big.Int
ClaimType uint8
Chain33Sender []byte
EthereumReceiver common.Address
ValidatorAddress common.Address
TokenAddress common.Address
Symbol string
Amount *big.Int
}
type LogNewBridgeToken struct {
Token common.Address
Symbol string
}
// UnpackLogLock : Handles new LogLock events
func UnpackLogLock(contractAbi abi.ABI, eventName string, eventData []byte) (lockEvent *LockEvent, err error) {
event := &LockEvent{}
// Parse the event's attributes as Ethereum network variables
err = contractAbi.Unpack(event, eventName, eventData)
if err != nil {
eventsLog.Error("UnpackLogLock", "Failed to unpack abi due to:", err.Error())
return nil, ebrelayerTypes.ErrUnpack
}
eventsLog.Info("UnpackLogLock", "value", event.Value.String(), "symbol", event.Symbol,
"token addr", event.Token.Hex(), "sender", event.From.Hex(),
"recipient", string(event.To), "nonce", event.Nonce.String())
return event, nil
}
func UnpackLogBurn(contractAbi abi.ABI, eventName string, eventData []byte) (burnEvent *BurnEvent, err error) {
event := &BurnEvent{}
// Parse the event's attributes as Ethereum network variables
err = contractAbi.Unpack(event, eventName, eventData)
if err != nil {
eventsLog.Error("UnpackLogBurn", "Failed to unpack abi due to:", err.Error())
return nil, ebrelayerTypes.ErrUnpack
}
eventsLog.Info("UnpackLogBurn", "token addr", event.Token.Hex(), "symbol", event.Symbol,
"Amount", event.Amount.String(), "OwnerFrom", event.OwnerFrom.String(),
"Chain33Receiver", string(event.Chain33Receiver), "nonce", event.Nonce.String())
return event, nil
}
package events
// -----------------------------------------------------
// Events: Events maintains a mapping of events to an array
// of claims made by validators.
// -----------------------------------------------------
// EventRecords : map of transaction hashes to LockEvent structs
var EventRecords = make(map[string]LockEvent)
// NewEventWrite : add a validator's address to the official claims list
func NewEventWrite(txHash string, event LockEvent) {
EventRecords[txHash] = event
}
package events
import (
log "github.com/33cn/chain33/common/log/log15"
)
// Event : enum containing supported contract events
type Event int
var eventsLog = log.New("module", "ethereum_relayer")
const (
// Unsupported : unsupported Chain33 or Ethereum event
Unsupported Event = iota
// MsgBurn : Chain33 event 'Chain33Msg' type MsgBurn
MsgBurn
// MsgLock : Chain33 event 'Chain33Msg' type MsgLock
MsgLock
// LogLock : Ethereum event 'LockEvent'
LogLock
// LogChain33TokenBurn : Ethereum event 'LogChain33TokenBurn' in contract chain33Bank
LogChain33TokenBurn
// LogNewProphecyClaim : Ethereum event 'NewProphecyClaimEvent'
LogNewProphecyClaim
)
const (
ClaimTypeBurn = uint8(1)
ClaimTypeLock = uint8(2)
)
// String : returns the event type as a string
func (d Event) String() string {
return [...]string{"unknown-x2ethereum", "Chain33ToEthBurn", "Chain33ToEthLock", "LogLock", "LogChain33TokenBurn", "LogNewProphecyClaim"}[d]
}
// Chain33MsgAttributeKey : enum containing supported attribute keys
type Chain33MsgAttributeKey int
const (
// UnsupportedAttributeKey : unsupported attribute key
UnsupportedAttributeKey Chain33MsgAttributeKey = iota
// Chain33Sender : sender's address on Chain33 network
Chain33Sender
// EthereumReceiver : receiver's address on Ethereum network
EthereumReceiver
// Coin : coin type
Coin
// TokenContractAddress : coin's corresponding contract address deployed on the Ethereum network
TokenContractAddress
)
// String : returns the event type as a string
func (d Chain33MsgAttributeKey) String() string {
return [...]string{"unsupported", "chain33_sender", "ethereum_receiver", "amount", "token_contract_address"}[d]
}
package events
import (
"math/big"
"strings"
"testing"
"github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/ethcontract/generated"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_UnpackLogLock(t *testing.T) {
abiJSON := generated.BridgeBankABI
contractABI, err := abi.JSON(strings.NewReader(abiJSON))
require.Nil(t, err)
eventName := LogLock.String()
eventData := []byte("nil")
_, err = UnpackLogLock(contractABI, eventName, eventData)
require.NotNil(t, err)
_, err = UnpackLogBurn(contractABI, eventName, eventData)
require.NotNil(t, err)
}
func Test_NewEventWrite(t *testing.T) {
event := LockEvent{
Symbol: "bty",
}
NewEventWrite("1", event)
assert.Equal(t, EventRecords["1"].Symbol, "bty")
}
func Test_NewChain33Msg(t *testing.T) {
_ = NewChain33Msg(MsgBurn, []byte("12qyocayNF7Lv6C9qW4avxs2E7U41fKSfv"), common.HexToAddress("0x0C05bA5c230fDaA503b53702aF1962e08D0C60BF"),
"eth", big.NewInt(100000000000000000), common.HexToAddress("0x0000000000000000000000000000000000000000"))
}
func Test_Chain33MsgAttributeKey(t *testing.T) {
assert.Equal(t, UnsupportedAttributeKey.String(), "unsupported")
assert.Equal(t, Chain33Sender.String(), "chain33_sender")
assert.Equal(t, EthereumReceiver.String(), "ethereum_receiver")
assert.Equal(t, Coin.String(), "amount")
assert.Equal(t, TokenContractAddress.String(), "token_contract_address")
}
package main
import (
"context"
"flag"
"fmt"
"io"
"net"
"net/http"
"net/rpc"
"net/rpc/jsonrpc"
"os"
"os/signal"
"path/filepath"
"sync"
"syscall"
dbm "github.com/33cn/chain33/common/db"
logf "github.com/33cn/chain33/common/log"
"github.com/33cn/chain33/common/log/log15"
chain33Types "github.com/33cn/chain33/types"
"github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/relayer"
chain33Relayer "github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/relayer/chain33"
ethRelayer "github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/relayer/ethereum"
relayerTypes "github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/types"
tml "github.com/BurntSushi/toml"
"github.com/btcsuite/btcd/limits"
"github.com/prometheus/common/log"
)
var (
configPath = flag.String("f", "", "configfile")
versionCmd = flag.Bool("s", false, "version")
IPWhiteListMap = make(map[string]bool)
mainlog = log15.New("relayer manager", "main")
)
func main() {
flag.Parse()
if *versionCmd {
fmt.Println(relayerTypes.Version4Relayer)
return
}
if *configPath == "" {
*configPath = "relayer.toml"
}
err := os.Chdir(pwd())
if err != nil {
panic(err)
}
d, err := os.Getwd()
if err != nil {
panic(err)
}
log.Info("current dir:", "dir", d)
err = limits.SetLimits()
if err != nil {
panic(err)
}
cfg := initCfg(*configPath)
log.Info("Starting FUZAMEI Chain33-X-Ethereum relayer software:", "\n Name: ", cfg.Title)
logf.SetFileLog(convertLogCfg(cfg.Log))
ctx, cancel := context.WithCancel(context.Background())
var wg sync.WaitGroup
log.Info("db info:", " Dbdriver = ", cfg.SyncTxConfig.Dbdriver, ", DbPath = ", cfg.SyncTxConfig.DbPath, ", DbCache = ", cfg.SyncTxConfig.DbCache)
log.Info("deploy info:", "BridgeRegistry", cfg.BridgeRegistry)
mainlog.Info("db info:", " Dbdriver = ", cfg.SyncTxConfig.Dbdriver, ", DbPath = ", cfg.SyncTxConfig.DbPath, ", DbCache = ", cfg.SyncTxConfig.DbCache)
db := dbm.NewDB("relayer_db_service", cfg.SyncTxConfig.Dbdriver, cfg.SyncTxConfig.DbPath, cfg.SyncTxConfig.DbCache)
chain33RelayerService := chain33Relayer.StartChain33Relayer(ctx, cfg.SyncTxConfig, cfg.BridgeRegistry, cfg.EthProvider, db)
ethRelayerService := ethRelayer.StartEthereumRelayer(cfg.SyncTxConfig.Chain33Host, db, cfg.EthProvider, cfg.BridgeRegistry, cfg.Deploy, cfg.EthMaturityDegree, cfg.EthBlockFetchPeriod)
relayerManager := relayer.NewRelayerManager(chain33RelayerService, ethRelayerService, db)
log.Info("cfg.JrpcBindAddr = ", cfg.JrpcBindAddr)
startRPCServer(cfg.JrpcBindAddr, relayerManager)
ch := make(chan os.Signal, 1)
signal.Notify(ch, syscall.SIGTERM)
go func() {
<-ch
cancel()
wg.Wait()
os.Exit(0)
}()
}
func convertLogCfg(log *relayerTypes.Log) *chain33Types.Log {
return &chain33Types.Log{
Loglevel: log.Loglevel,
LogConsoleLevel: log.LogConsoleLevel,
LogFile: log.LogFile,
MaxFileSize: log.MaxFileSize,
MaxBackups: log.MaxBackups,
MaxAge: log.MaxAge,
LocalTime: log.LocalTime,
Compress: log.Compress,
CallerFile: log.CallerFile,
CallerFunction: log.CallerFunction,
}
}
func pwd() string {
dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
if err != nil {
panic(err)
}
return dir
}
func initCfg(path string) *relayerTypes.RelayerConfig {
var cfg relayerTypes.RelayerConfig
if _, err := tml.DecodeFile(path, &cfg); err != nil {
fmt.Println(err)
os.Exit(-1)
}
//fmt.Println(cfg)
return &cfg
}
func IsIPWhiteListEmpty() bool {
return len(IPWhiteListMap) == 0
}
//判断ipAddr是否在ip地址白名单中
func IsInIPWhitelist(ipAddrPort string) bool {
ipAddr, _, err := net.SplitHostPort(ipAddrPort)
if err != nil {
return false
}
ip := net.ParseIP(ipAddr)
if ip.IsLoopback() {
return true
}
if _, ok := IPWhiteListMap[ipAddr]; ok {
return true
}
return false
}
type RPCServer struct {
*rpc.Server
}
func (r *RPCServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
log.Info("ServeHTTP", "request address", req.RemoteAddr)
if !IsIPWhiteListEmpty() {
if !IsInIPWhitelist(req.RemoteAddr) {
log.Info("ServeHTTP", "refuse connect address", req.RemoteAddr)
w.WriteHeader(401)
return
}
}
r.Server.ServeHTTP(w, req)
}
func (r *RPCServer) HandleHTTP(rpcPath, debugPath string) {
http.Handle(rpcPath, r)
}
type HTTPConn struct {
in io.Reader
out io.Writer
}
func (c *HTTPConn) Read(p []byte) (n int, err error) { return c.in.Read(p) }
func (c *HTTPConn) Write(d []byte) (n int, err error) { return c.out.Write(d) }
func (c *HTTPConn) Close() error { return nil }
func startRPCServer(address string, api interface{}) {
listener, err := net.Listen("tcp", address)
if err != nil {
fmt.Println("监听失败,端口可能已经被占用")
panic(err)
}
srv := &RPCServer{rpc.NewServer()}
_ = srv.Server.Register(api)
srv.HandleHTTP(rpc.DefaultRPCPath, rpc.DefaultDebugPath)
var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
serverCodec := jsonrpc.NewServerCodec(&HTTPConn{in: r.Body, out: w})
w.Header().Set("Content-type", "application/json")
w.WriteHeader(200)
err := srv.ServeRequest(serverCodec)
if err != nil {
log.Debug("http", "Error while serving JSON request: %v", err)
return
}
}
})
_ = http.Serve(listener, handler)
}
# 本配置文件专门用于33复杂美闪电网络使用,即lns33
title="x2Ethereum_relayer"
#用于cli向该relayer进行配置
JrpcBindAddr="localhost:9901"
EthProvider="ws://127.0.0.1:7545/"
#EthProvider="wss://rinkeby.infura.io/ws/v3/404eb4acc421426ebeb6e92c7ce9a270"
#EthProvider="wss://ropsten.infura.io/ws/v3/404eb4acc421426ebeb6e92c7ce9a270"
EthMaturityDegree=10
EthBlockFetchPeriod=5000
#BridgeRegistry="0x5331F912027057fBE8139D91B225246e8159232f"
[SyncTxConfig]
chain33Host="http://172.18.0.7:8801"
pushHost="http://192.168.0.125:20000"
pushName="x2ethereum"
pushBind="0.0.0.0:20000"
maturityDegree=10
dbdriver="leveldb"
dbPath="datadir"
dbCache=64
fetchHeightPeriodMs=5000
#如果需要配置起始的块的信息,则为了保持一致性,三项缺一不可,或者都不配置
#startSyncHeight=24531
#startSyncSequence=24531
#startSyncHash="0xb96168940ceebc12dcf4a4911ec8f509344f5c8591af37a49f4fb5f433093d2e"
startSyncHeight=0
startSyncSequence=0
startSyncHash=""
[deploy]
#合约部署人员私钥,用于部署合约时签名使用
operatorAddr="0x8afdadfc88a1087c9a1d6c0f5dd04634b87f303a"
#合约部署人员私钥,用于部署合约时签名使用
deployerPrivateKey="8656d2bc732a8a816a461ba5e2d8aac7c7f85c26a813df30d5327210465eb230"
#验证人地址,至少配置3个以上,即大于等于3个
validatorsAddr=["0x92c8b16afd6d423652559c6e266cbe1c29bfd84f", "0x0df9a824699bc5878232c9e612fe1a5346a5a368", "0xcb074cb21cdddf3ce9c3c0a7ac4497d633c9d9f1", "0xd9dab021e74ecf475788ed7b61356056b2095830"]
#验证人权重
initPowers=[25, 25, 25, 25]
[log]
# 日志级别,支持debug(dbug)/info/warn/error(eror)/crit
loglevel = "debug"
logConsoleLevel = "debug"
# 日志文件名,可带目录,所有生成的日志文件都放到此目录下
logFile = "logs/x2Ethereum_relayer.log"
# 单个日志文件的最大值(单位:兆)
maxFileSize = 300
# 最多保存的历史日志文件个数
maxBackups = 100
# 最多保存的历史日志消息(单位:天)
maxAge = 28
# 日志文件名是否使用本地事件(否则使用UTC时间)
localTime = true
# 历史日志文件是否压缩(压缩格式为gz)
compress = true
# 是否打印调用源文件和行号
callerFile = false
# 是否打印调用方法
callerFunction = false
\ No newline at end of file
package chain33
import (
chain33Common "github.com/33cn/chain33/common"
"github.com/ethereum/go-ethereum/crypto"
//dbm "github.com/33cn/chain33/common/db"
chain33Types "github.com/33cn/chain33/types"
wcom "github.com/33cn/chain33/wallet/common"
x2ethTypes "github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/types"
)
var (
chain33AccountKey = []byte("Chain33Account4Relayer")
start = int(1)
)
func (chain33Relayer *Relayer4Chain33) GetAccount(passphrase string) (privateKey, addr string, err error) {
accountInfo, err := chain33Relayer.db.Get(chain33AccountKey)
if nil != err {
return "", "", err
}
ethAccount := &x2ethTypes.Account4Relayer{}
if err := chain33Types.Decode(accountInfo, ethAccount); nil != err {
return "", "", err
}
decryptered := wcom.CBCDecrypterPrivkey([]byte(passphrase), ethAccount.Privkey)
privateKey = chain33Common.ToHex(decryptered)
addr = ethAccount.Addr
return
}
func (chain33Relayer *Relayer4Chain33) GetAccountAddr() (addr string, err error) {
accountInfo, err := chain33Relayer.db.Get(chain33AccountKey)
if nil != err {
relayerLog.Info("GetValidatorAddr", "Failed to get account from db due to:", err.Error())
return "", err
}
ethAccount := &x2ethTypes.Account4Relayer{}
if err := chain33Types.Decode(accountInfo, ethAccount); nil != err {
relayerLog.Info("GetValidatorAddr", "Failed to decode due to:", err.Error())
return "", err
}
addr = ethAccount.Addr
return
}
func (chain33Relayer *Relayer4Chain33) ImportPrivateKey(passphrase, privateKeyStr string) (addr string, err error) {
privateKeySlice, err := chain33Common.FromHex(privateKeyStr)
if nil != err {
return "", err
}
privateKey, err := crypto.ToECDSA(privateKeySlice)
if nil != err {
return "", err
}
ethSender := crypto.PubkeyToAddress(privateKey.PublicKey)
chain33Relayer.privateKey4Ethereum = privateKey
chain33Relayer.ethSender = ethSender
chain33Relayer.unlock <- start
addr = chain33Common.ToHex(ethSender.Bytes())
encryptered := wcom.CBCEncrypterPrivkey([]byte(passphrase), privateKeySlice)
ethAccount := &x2ethTypes.Account4Relayer{
Privkey: encryptered,
Addr: addr,
}
encodedInfo := chain33Types.Encode(ethAccount)
err = chain33Relayer.db.SetSync(chain33AccountKey, encodedInfo)
return
}
func (chain33Relayer *Relayer4Chain33) StoreAccountWithNewPassphase(newPassphrase, oldPassphrase string) error {
accountInfo, err := chain33Relayer.db.Get(chain33AccountKey)
if nil != err {
relayerLog.Info("StoreAccountWithNewPassphase", "pls check account is created already, err", err)
return err
}
ethAccount := &x2ethTypes.Account4Relayer{}
if err := chain33Types.Decode(accountInfo, ethAccount); nil != err {
return err
}
decryptered := wcom.CBCDecrypterPrivkey([]byte(oldPassphrase), ethAccount.Privkey)
encryptered := wcom.CBCEncrypterPrivkey([]byte(newPassphrase), decryptered)
ethAccount.Privkey = encryptered
encodedInfo := chain33Types.Encode(ethAccount)
return chain33Relayer.db.SetSync(chain33AccountKey, encodedInfo)
}
func (chain33Relayer *Relayer4Chain33) RestorePrivateKeys(passphrase string) error {
accountInfo, err := chain33Relayer.db.Get(chain33AccountKey)
if nil != err {
relayerLog.Info("No private key saved for Relayer4Chain33")
return nil
}
ethAccount := &x2ethTypes.Account4Relayer{}
if err := chain33Types.Decode(accountInfo, ethAccount); nil != err {
relayerLog.Info("RestorePrivateKeys", "Failed to decode due to:", err.Error())
return err
}
decryptered := wcom.CBCDecrypterPrivkey([]byte(passphrase), ethAccount.Privkey)
privateKey, err := crypto.ToECDSA(decryptered)
if nil != err {
relayerLog.Info("RestorePrivateKeys", "Failed to ToECDSA:", err.Error())
return err
}
chain33Relayer.rwLock.Lock()
chain33Relayer.privateKey4Ethereum = privateKey
chain33Relayer.ethSender = crypto.PubkeyToAddress(privateKey.PublicKey)
chain33Relayer.rwLock.Unlock()
chain33Relayer.unlock <- start
return nil
}
//func (chain33Relayer *Relayer4Chain33) UpdatePrivateKey(Passphrase, privateKey string) error {
// return nil
//}
This diff is collapsed.
package chain33
import (
"fmt"
"sync/atomic"
"github.com/33cn/chain33/types"
ebTypes "github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/types"
"github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/utils"
"github.com/ethereum/go-ethereum/common"
)
var (
lastSyncHeightPrefix = []byte("lastSyncHeight:")
chain33ToEthBurnLockTxHashPrefix = "chain33ToEthBurnLockTxHash"
chain33ToEthBurnLockTxTotalAmount = []byte("chain33ToEthBurnLockTxTotalAmount")
EthTxStatusCheckedIndex = []byte("EthTxStatusCheckedIndex")
)
func calcRelay2EthTxhash(txindex int64) []byte {
return []byte(fmt.Sprintf("%s-%012d", chain33ToEthBurnLockTxHashPrefix, txindex))
}
func (chain33Relayer *Relayer4Chain33) updateTotalTxAmount2Eth(total int64) error {
totalTx := &types.Int64{
Data: atomic.LoadInt64(&chain33Relayer.totalTx4Chain33ToEth),
}
//更新成功见证的交易数
return chain33Relayer.db.Set(chain33ToEthBurnLockTxTotalAmount, types.Encode(totalTx))
}
func (chain33Relayer *Relayer4Chain33) getTotalTxAmount2Eth() int64 {
totalTx, _ := utils.LoadInt64FromDB(chain33ToEthBurnLockTxTotalAmount, chain33Relayer.db)
return totalTx
}
func (chain33Relayer *Relayer4Chain33) setLastestRelay2EthTxhash(status, txhash string, txIndex int64) error {
key := calcRelay2EthTxhash(txIndex)
ethTxStatus := &ebTypes.EthTxStatus{
Status: status,
Txhash: txhash,
}
data := types.Encode(ethTxStatus)
return chain33Relayer.db.Set(key, data)
}
func (chain33Relayer *Relayer4Chain33) getEthTxhash(txIndex int64) (common.Hash, error) {
key := calcRelay2EthTxhash(txIndex)
ethTxStatus := &ebTypes.EthTxStatus{}
data, err := chain33Relayer.db.Get(key)
if nil != err {
return common.Hash{}, err
}
err = types.Decode(data, ethTxStatus)
if nil != err {
return common.Hash{}, err
}
return common.HexToHash(ethTxStatus.Txhash), nil
}
func (chain33Relayer *Relayer4Chain33) setStatusCheckedIndex(txIndex int64) error {
index := &types.Int64{
Data: txIndex,
}
data := types.Encode(index)
return chain33Relayer.db.Set(EthTxStatusCheckedIndex, data)
}
func (chain33Relayer *Relayer4Chain33) getStatusCheckedIndex() int64 {
index, _ := utils.LoadInt64FromDB(EthTxStatusCheckedIndex, chain33Relayer.db)
return index
}
//获取上次同步到app的高度
func (chain33Relayer *Relayer4Chain33) loadLastSyncHeight() int64 {
height, err := utils.LoadInt64FromDB(lastSyncHeightPrefix, chain33Relayer.db)
if nil != err && err != types.ErrHeightNotExist {
relayerLog.Error("loadLastSyncHeight", "err:", err.Error())
return 0
}
return height
}
func (chain33Relayer *Relayer4Chain33) setLastSyncHeight(syncHeight int64) {
bytes := types.Encode(&types.Int64{Data: syncHeight})
_ = chain33Relayer.db.Set(lastSyncHeightPrefix, bytes)
}
package sync
import (
"fmt"
"math"
"sync/atomic"
dbm "github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/types"
"github.com/33cn/plugin/plugin/dapp/x2ethereum/ebrelayer/utils"
"github.com/pkg/errors"
)
// SeqType
const (
SeqTypeAdd = int32(1)
SeqTypeDel = int32(2)
)
var (
syncLastHeight = []byte("syncLastHeight:")
txReceiptPrefix = []byte("txReceiptPrefix:")
lastSequences = []byte("lastSequences:")
seqOperationType = []string{"SeqTypeAdd", "SeqTypeDel"}
)
var txReceiptCh chan *types.TxReceipts4Subscribe
var resultCh chan error
func init() {
txReceiptCh = make(chan *types.TxReceipts4Subscribe)
resultCh = make(chan error)
}
func txReceiptsKey4Height(height int64) []byte {
return append(txReceiptPrefix, []byte(fmt.Sprintf("%012d", height))...)
}
// pushTxReceipts push block to backend
func pushTxReceipts(txReceipts *types.TxReceipts4Subscribe) error {
txReceiptCh <- txReceipts
err := <-resultCh
return err
}
type TxReceipts struct {
db dbm.DB
seqNum int64 //当前同步的序列号
height int64 //当前区块高度
quit chan struct{}
}
func NewSyncTxReceipts(db dbm.DB) *TxReceipts {
sync := &TxReceipts{
db: db,
}
sync.seqNum, _ = sync.loadBlockLastSequence()
sync.height, _ = sync.LoadLastBlockHeight()
sync.quit = make(chan struct{})
sync.initSyncReceiptDataBase()
return sync
}
//此处添加一个高度为0的空块,只是为了查找下一个比较方便,并不需要使用其信息
func (syncTx *TxReceipts) initSyncReceiptDataBase() {
txblock0, _ := syncTx.GetTxReceipts(0)
if nil != txblock0 {
return
}
txsPerBlock := &types.TxReceipts4SubscribePerBlk{
Height: 0,
}
syncTx.setTxReceiptsPerBlock(txsPerBlock)
}
func (syncTx *TxReceipts) Stop() {
close(syncTx.quit)
}
// SaveAndSyncTxs2Relayer save block to db
func (syncTx *TxReceipts) SaveAndSyncTxs2Relayer() {
for {
select {
case txReceipts := <-txReceiptCh:
log.Info("to deal request", "seq", txReceipts.TxReceipts[0].SeqNum, "count", len(txReceipts.TxReceipts))
syncTx.dealTxReceipts(txReceipts)
case <-syncTx.quit:
return
}
}
}
// 保存区块步骤
// 1. 记录 seqNumber -> seq
// 2. 记录 lastseq
// 3. 更新高度
//
// 重启恢复
// 1. 看高度, 对应高度是已经完成的
// 2. 继续重新下一个高度即可。 重复写, 幂等
// 所以不需要恢复过程, 读出高度即可
// 处理输入流程
func (syncTx *TxReceipts) dealTxReceipts(txReceipts *types.TxReceipts4Subscribe) {
count, start, txReceiptsParsed, err := parseTxReceipts(txReceipts)
if err != nil {
resultCh <- err
}
//正常情况下,本次开始的的seq不能小于上次结束的seq
if start < syncTx.seqNum {
log.Error("dealTxReceipts err: the tx and receipt pushed is old", "start", start, "current_seq", syncTx.seqNum)
resultCh <- errors.New("The tx and receipt pushed is old")
return
}
var height int64
for i := 0; i < count; i++ {
txsPerBlock := txReceiptsParsed[i]
if txsPerBlock.AddDelType == SeqTypeAdd {
syncTx.setTxReceiptsPerBlock(txsPerBlock)
syncTx.setBlockLastSequence(txsPerBlock.SeqNum)
syncTx.setBlockHeight(txsPerBlock.Height)
height = txsPerBlock.Height
} else {
//删除分叉区块处理
syncTx.delTxReceipts(txsPerBlock.Height)
syncTx.setBlockLastSequence(txsPerBlock.SeqNum)
height = txsPerBlock.Height - 1
//删除区块不需要通知新的高度,因为这只会降低未处理区块的成熟度
syncTx.setBlockHeight(height)
}
}
//syncTx.syncReceiptChan <- height
//发送回复,确认接收成功
resultCh <- nil
log.Debug("dealTxReceipts", "seqStart", start, "count", count, "maxBlockHeight", height)
}
func (syncTx *TxReceipts) loadBlockLastSequence() (int64, error) {
return utils.LoadInt64FromDB(lastSequences, syncTx.db)
}
func (syncTx *TxReceipts) LoadLastBlockHeight() (int64, error) {
return utils.LoadInt64FromDB(syncLastHeight, syncTx.db)
}
func (syncTx *TxReceipts) setBlockLastSequence(newSequence int64) {
Sequencebytes := types.Encode(&types.Int64{Data: newSequence})
syncTx.db.Set(lastSequences, Sequencebytes)
//同时更新内存中的seq
syncTx.updateSequence(newSequence)
}
func (syncTx *TxReceipts) setBlockHeight(height int64) {
bytes := types.Encode(&types.Int64{Data: height})
syncTx.db.Set(syncLastHeight, bytes)
atomic.StoreInt64(&syncTx.height, height)
}
func (syncTx *TxReceipts) updateSequence(newSequence int64) {
atomic.StoreInt64(&syncTx.seqNum, newSequence)
}
func (syncTx *TxReceipts) setTxReceiptsPerBlock(txReceipts *types.TxReceipts4SubscribePerBlk) {
key := txReceiptsKey4Height(txReceipts.Height)
value := types.Encode(txReceipts)
if err := syncTx.db.Set(key, value); nil != err {
panic("setTxReceiptsPerBlock failed due to:" + err.Error())
}
}
func (syncTx *TxReceipts) GetTxReceipts(height int64) (*types.TxReceipts4SubscribePerBlk, error) {
key := txReceiptsKey4Height(height)
value, err := syncTx.db.Get(key)
if err != nil {
return nil, err
}
detail := &types.TxReceipts4SubscribePerBlk{}
err = types.Decode(value, detail)
if err != nil {
return nil, err
}
return detail, nil
}
func (syncTx *TxReceipts) GetNextValidTxReceipts(height int64) (*types.TxReceipts4SubscribePerBlk, error) {
key := txReceiptsKey4Height(height)
helper := dbm.NewListHelper(syncTx.db)
TxReceipts := helper.List(txReceiptPrefix, key, 1, dbm.ListASC)
if nil == TxReceipts {
return nil, nil
}
detail := &types.TxReceipts4SubscribePerBlk{}
err := types.Decode(TxReceipts[0], detail)
if err != nil {
return nil, err
}
return detail, nil
}
func (syncTx *TxReceipts) delTxReceipts(height int64) {
key := txReceiptsKey4Height(height)
_ = syncTx.db.Set(key, nil)
}
// 检查输入是否有问题, 并解析输入
func parseTxReceipts(txReceipts *types.TxReceipts4Subscribe) (count int, start int64, txsWithReceipt []*types.TxReceipts4SubscribePerBlk, err error) {
count = len(txReceipts.TxReceipts)
txsWithReceipt = make([]*types.TxReceipts4SubscribePerBlk, 0)
start = math.MaxInt64
for i := 0; i < count; i++ {
if txReceipts.TxReceipts[i].AddDelType != SeqTypeAdd && txReceipts.TxReceipts[i].AddDelType != SeqTypeDel {
log.Error("parseTxReceipts seq op not support", "seq", txReceipts.TxReceipts[i].SeqNum,
"height", txReceipts.TxReceipts[i].Height, "seqOp", txReceipts.TxReceipts[i].AddDelType)
continue
}
txsWithReceipt = append(txsWithReceipt, txReceipts.TxReceipts[i])
if txReceipts.TxReceipts[i].SeqNum < start {
start = txReceipts.TxReceipts[i].SeqNum
}
log.Debug("parseTxReceipts get one block's tx with receipts", "seq", txReceipts.TxReceipts[i].SeqNum,
"height", txReceipts.TxReceipts[i].Height, "seqOpType", seqOperationType[txReceipts.TxReceipts[i].AddDelType-1])
}
if len(txsWithReceipt) != count {
err = errors.New("duplicate block's tx receipt")
return
}
return
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment