Commit 29a2b9e0 authored by luxq's avatar luxq

add code

parents
Pipeline #877 failed with stages
.idea
data
build
FROM golang:1.24-alpine AS build
# Set up dependencies
ENV PACKAGES build-base
# Install dependencies
RUN apk add --update $PACKAGES
# Add source files
WORKDIR /build
COPY ./ /build/token-bridge
RUN cd /build/token-bridge && go mod tidy && go build -v -o /tmp/validator ./cmd/validator
FROM alpine
WORKDIR /app
COPY ./config.toml /app/config.toml
COPY --from=build /tmp/validator /usr/bin/validator
EXPOSE 8080
\ No newline at end of file
.PHONY: default all clean dev
GOBIN = $(shell pwd)/build/bin
default: all
all: validator
validator:
go build $(BUILD_FLAGS) -v -o=${GOBIN}/$@ ./cmd/validator
dev:
go build $(BUILD_FLAGS) -v -o=${GOBIN}/$@ -gcflags "all=-N -l" ./cmd
docker:
docker build -t token-bridge:latest -f ./Dockerfile .
docker tag token-bridge:latest token-bridge:$${TAG:-latest}
.PHONY: docker
package chain
import (
"code.wuban.net.cn/movabridge/token-bridge/config"
"code.wuban.net.cn/movabridge/token-bridge/constant"
"code.wuban.net.cn/movabridge/token-bridge/contract/bridge"
"code.wuban.net.cn/movabridge/token-bridge/dao"
dbModel "code.wuban.net.cn/movabridge/token-bridge/model/db"
"fmt"
"golang.org/x/crypto/sha3"
"math/big"
"strings"
"sync"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
log "github.com/sirupsen/logrus"
)
type ChainSync struct {
chain *config.ChainConfig
d *dao.Dao
name string
heightKey string
bridgeCa *bridge.BridgeContract
quit chan struct{}
stopOnce sync.Once
}
func NewChainSync(_chain *config.ChainConfig, _d *dao.Dao) (sync *ChainSync) {
bridgeCa, err := bridge.NewBridgeContract(common.HexToAddress(_chain.BridgeContract), nil)
if err != nil {
panic(err)
}
sync = &ChainSync{
chain: _chain,
d: _d,
name: _chain.Name,
heightKey: fmt.Sprintf("%d_%s", _chain.ChainId, "height"),
bridgeCa: bridgeCa,
quit: make(chan struct{}),
}
return sync
}
func (s *ChainSync) Start() {
lastHeight, err := s.d.GetStorageHeight(s.heightKey)
if err != nil {
if err == dao.ErrRecordNotFound {
lastHeight = s.chain.InitialHeight
} else {
log.WithField("chain", s.name).WithField("chain", s.name).WithError(err).Error("get last block height")
return
}
}
if lastHeight != 1 {
// 数据库里保存的是已完成的区块, 再次同步时+1
lastHeight++
}
log.WithField("chain", s.name).WithField("height", lastHeight).Info("last validator block height")
var latestHeight int64
var beginHeight = lastHeight
var endHeight = beginHeight + int64(s.chain.BatchBlock)
tm := time.NewTicker(time.Second)
defer tm.Stop()
for {
select {
case <-s.quit:
log.WithField("chain", s.name).Info("chain sync stopped")
return
case <-tm.C:
latestHeight, err = s.d.GetBlockHeight(s.chain, s.chain.BehindBlock)
if err != nil {
log.WithField("chain", s.name).WithError(err).Error("get latest block height")
time.Sleep(time.Second)
continue
}
if (latestHeight-int64(s.chain.BatchBlock)-1)-beginHeight < int64(s.chain.BatchBlock) {
time.Sleep(2 * time.Second)
continue
}
if err := s.SyncLogs(beginHeight, endHeight); err != nil {
log.WithField("chain", s.name).WithFields(log.Fields{
"begin height": beginHeight,
"end height": endHeight,
}).WithError(err).Error("sync logs failed")
time.Sleep(time.Second)
continue
}
if err = s.d.SetStorageHeight(s.heightKey, endHeight); err != nil {
log.WithField("chain", s.name).WithError(err).Error("set last block height")
}
beginHeight = endHeight + 1
endHeight = beginHeight + int64(s.chain.BatchBlock)
log.WithField("chain", s.name).WithFields(log.Fields{
"begin height": beginHeight,
"end height": endHeight,
"latest height": latestHeight,
"diff height": latestHeight - endHeight,
}).Info("validator block")
}
}
}
func (s *ChainSync) Stop() {
s.stopOnce.Do(func() {
log.WithField("chain", s.name).Info("stopping chain sync")
close(s.quit)
})
}
func (s *ChainSync) SyncLogs(beginHeight, endHeight int64) error {
if endHeight < 0 {
return nil
}
if beginHeight < 0 {
beginHeight = 0
}
abi, _ := bridge.BridgeContractMetaData.GetAbi()
topics := []string{
abi.Events["TransferOut"].ID.Hex(), // TransferOut
abi.Events["TransferIn"].ID.Hex(), // TransferIn
abi.Events["TransferInConfirmation"].ID.Hex(), // TransferInConfirmation
abi.Events["TransferInRejection"].ID.Hex(), // TransferInRejection
abi.Events["TransferInExecution"].ID.Hex(), // TransferInExecution
}
logs, err := s.d.GetLogs(s.chain, beginHeight, endHeight, topics, []string{
s.chain.BridgeContract,
})
if err != nil {
log.WithField("chain", s.name).WithFields(log.Fields{"begin": beginHeight, "end": endHeight}).WithError(err).Error("rpc: get logs")
return err
}
if len(logs) > 0 {
log.WithField("chain", s.name).WithFields(log.Fields{"begin": beginHeight, "end": endHeight}).Infof("get %d logs", len(logs))
}
// begin orm transaction
ormTx, err := s.d.BeginTx()
if err != nil {
log.WithField("chain", s.name).WithError(err).Error("begin db transaction")
return err
}
var ormTxErr error
for _, txLog := range logs {
if err := s.FilterTransferOut(txLog, ormTx); err != nil {
ormTxErr = err
break
}
if err := s.FilterTransferIn(txLog, ormTx); err != nil {
ormTxErr = err
break
}
if err := s.FilterValidatorEvents(txLog, ormTx); err != nil {
ormTxErr = err
break
}
}
// Commit or rollback transaction based on error
if ormTxErr != nil {
if rbErr := ormTx.Rollback(); rbErr != nil {
log.WithField("chain", s.name).WithError(rbErr).Error("failed to rollback transaction")
}
log.WithField("chain", s.name).WithError(ormTxErr).Error("error processing logs, transaction rolled back")
} else {
if cmtErr := ormTx.Commit(); cmtErr != nil {
log.WithField("chain", s.name).WithError(cmtErr).Error("failed to commit transaction")
}
}
return nil
}
// FilterTransferOut 用户从当前链跨出事件.
func (s *ChainSync) FilterTransferOut(txLog types.Log, tx *dao.Transaction) error {
if len(txLog.Topics) == 0 {
return nil
}
abi, _ := bridge.BridgeContractMetaData.GetAbi()
if txLog.Topics[0].Hex() == abi.Events["TransferOut"].ID.Hex() {
event, err := s.bridgeCa.ParseTransferOut(txLog)
if err != nil {
log.WithField("chain", s.name).WithError(err).Error("parse TransferOut log")
return err
}
// 防止重复入库.
eventHash := transferOutEventHash(event.FromChainID.Int64(), event.OutId.Int64(), strings.ToLower(txLog.Address.String()))
dbEvent := &dbModel.BridgeEvent{
FromChain: event.FromChainID.Int64(),
OutTimestamp: int64(txLog.BlockTimestamp),
FromContract: strings.ToLower(txLog.Address.String()),
FromAddress: strings.ToLower(event.Sender.String()),
FromToken: strings.ToLower(event.Token.String()),
FromChainTxHash: strings.ToLower(txLog.TxHash.String()),
SendAmount: event.Amount.Text(10),
FeeAmount: event.Fee.Text(10),
ToToken: strings.ToLower(event.ReceiveToken.String()),
ReceiveAmount: new(big.Int).Sub(event.Amount, event.Fee).Text(10),
OutId: event.OutId.Int64(),
Receiver: strings.ToLower(event.Receiver.String()),
ToChain: event.ToChainID.Int64(),
ToChainStatus: constant.TransferChainNoProcess,
Hash: eventHash,
}
err = s.d.CreateBridgeEventTx(tx, dbEvent)
if err != nil {
log.WithField("chain", s.name).WithFields(log.Fields{
"error": err.Error(),
}).Error("db create bridge in event")
return err
}
log.WithField("chain", s.name).WithField("txHash", txLog.TxHash.Hex()).Info("db create, TransferOut event")
}
return nil
}
// FilterTransferIn 用户从目标链跨入事件及执行结束事件.
func (s *ChainSync) FilterTransferIn(txLog types.Log, tx *dao.Transaction) error {
if len(txLog.Topics) == 0 {
return nil
}
abi, _ := bridge.BridgeContractMetaData.GetAbi()
switch txLog.Topics[0].Hex() {
case abi.Events["TransferIn"].ID.Hex():
event, err := s.bridgeCa.ParseTransferIn(txLog)
if err != nil {
log.WithField("chain", s.name).WithError(err).Error("parse TransferIn log")
return err
}
// find out if the event already exists in the database.
dbEvent, err := s.d.GetBridgeEventWithOutInfoTx(tx, event.FromChainID.Int64(), event.OutId.Int64())
if err == dao.ErrRecordNotFound {
log.WithField("chain", s.name).WithField("outId", event.OutId.Int64()).Error("transfer out event not found")
return nil
}
// update the event with the transfer in information.
dbEvent.ToContract = strings.ToLower(txLog.Address.String())
dbEvent.InTimestamp = int64(txLog.BlockTimestamp)
dbEvent.InId = event.InId.Int64()
dbEvent.ToChainTxHash = strings.ToLower(txLog.TxHash.String())
dbEvent.ToChainStatus = constant.TransferChainWaitConfirm
if err := s.d.UpdateBridgeWithTransferInTx(tx, dbEvent); err != nil {
log.WithField("chain", s.name).WithFields(log.Fields{
"error": err.Error(),
}).Error("db update transfer in event")
return err
}
return nil
case abi.Events["TransferInExecution"].ID.Hex():
event, err := s.bridgeCa.ParseTransferInExecution(txLog)
if err != nil {
log.WithField("chain", s.name).WithError(err).Error("parse TransferInExecution log")
return err
}
dbEvent, err := s.d.GetBridgeEventWithInInfoTx(tx, s.chain.ChainId, event.InId.Int64())
if err == dao.ErrRecordNotFound {
log.WithField("chain", s.name).WithField("inId", event.InId.Int64()).Error("transfer in event not found")
return nil
}
dbEvent.ToChainStatus = constant.TransferChainExecuted
dbEvent.FinishTxHash = strings.ToLower(txLog.TxHash.String())
if err := s.d.UpdateBridgeResultTx(tx, dbEvent, dbEvent.FinishTxHash, dbEvent.ToChainStatus); err != nil {
log.WithField("chain", s.name).WithFields(log.Fields{
"error": err.Error(),
}).Error("db update transfer in execution event")
return err
}
case abi.Events["TransferInRejection"].ID.Hex():
event, err := s.bridgeCa.ParseTransferInRejection(txLog)
if err != nil {
log.WithField("chain", s.name).WithError(err).Error("parse TransferInExecution log")
return err
}
dbEvent, err := s.d.GetBridgeEventWithInInfoTx(tx, s.chain.ChainId, event.InId.Int64())
if err == dao.ErrRecordNotFound {
log.WithField("chain", s.name).WithField("inId", event.InId.Int64()).Error("transfer in event not found")
return nil
}
dbEvent.ToChainStatus = constant.TransferChainRejected
dbEvent.FinishTxHash = strings.ToLower(txLog.TxHash.String())
if err := s.d.UpdateBridgeResultTx(tx, dbEvent, dbEvent.FinishTxHash, dbEvent.ToChainStatus); err != nil {
log.WithField("chain", s.name).WithFields(log.Fields{
"error": err.Error(),
}).Error("db update transfer in execution event")
return err
}
}
return nil
}
// FilterValidatorEvents 当前链验证者事件.
func (s *ChainSync) FilterValidatorEvents(txLog types.Log, tx *dao.Transaction) error {
if len(txLog.Topics) == 0 {
return nil
}
var (
chainId = s.chain.ChainId
validator = ""
eventType = ""
inId = int64(0)
eventHash = ""
txHash = txLog.TxHash.Hex()
)
abi, _ := bridge.BridgeContractMetaData.GetAbi()
switch txLog.Topics[0].Hex() {
case abi.Events["TransferInConfirmation"].ID.Hex():
event, err := s.bridgeCa.ParseTransferInConfirmation(txLog)
if err != nil {
log.WithField("chain", s.name).WithError(err).Error("parse TransferInConfirmation log")
return err
}
eventHash = validatorEventHash(s.chain.ChainId, event.Validator.String(), txLog.TxHash.Hex(), event.InId.Int64(), "TransferInConfirmation")
validator = strings.ToLower(event.Validator.String())
inId = event.InId.Int64()
eventType = "TransferInConfirmation"
case abi.Events["TransferInRejection"].ID.Hex():
event, err := s.bridgeCa.ParseTransferInRejection(txLog)
if err != nil {
log.WithField("chain", s.name).WithError(err).Error("parse TransferInRejection log")
return err
}
eventHash = validatorEventHash(s.chain.ChainId, event.Validator.String(), txLog.TxHash.Hex(), event.InId.Int64(), "TransferInRejection")
validator = strings.ToLower(event.Validator.String())
inId = event.InId.Int64()
eventType = "TransferInRejection"
}
err := s.d.CreateValidatorEventTx(tx, eventHash, chainId, validator, txHash, eventType, inId)
if err != nil {
log.WithField("chain", s.name).WithFields(log.Fields{
"error": err.Error(),
}).Error("db create validator event")
return err
}
return nil
}
func validatorEventHash(chainId int64, validator string, txHash string, inId int64, event string) string {
hash := sha3.NewLegacyKeccak256()
hash.Write([]byte(fmt.Sprintf("%d%s%s%d%s", chainId, validator, txHash, inId, event)))
return common.BytesToHash(hash.Sum(nil)).String()
}
func transferOutEventHash(fromChain int64, outId int64, fromContract string) string {
hash := sha3.NewLegacyKeccak256()
hash.Write([]byte(fmt.Sprintf("%d%d%s", fromChain, outId, fromContract)))
return common.BytesToHash(hash.Sum(nil)).String()
}
package main
import (
"code.wuban.net.cn/movabridge/token-bridge/chain"
"code.wuban.net.cn/movabridge/token-bridge/config"
"code.wuban.net.cn/movabridge/token-bridge/dao"
"flag"
"os"
"os/signal"
"syscall"
log "github.com/sirupsen/logrus"
)
func init() {
log.SetFormatter(&log.TextFormatter{
FullTimestamp: true,
})
}
func main() {
flag.Parse()
conf, err := config.New()
if err != nil {
panic(err)
}
d, err := dao.New(conf)
if err != nil {
panic(err)
}
if conf.Debug {
log.SetLevel(log.DebugLevel)
}
syncers := make([]*chain.ChainSync, 0)
for _, chainConfig := range conf.Chains {
syncer := chain.NewChainSync(chainConfig, d)
go syncer.Start()
syncers = append(syncers, syncer)
}
// Set up signal handling
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
// Wait for termination signal
sig := <-sigCh
log.WithField("signal", sig.String()).Info("received termination signal, shutting down")
// Stop all chain sync instances
for _, syncer := range syncers {
syncer.Stop()
}
log.Info("graceful shutdown completed")
os.Exit(0)
}
debug = true
[[chains]]
name = "cad"
rpc = "https://1rpc.io/sepolia"
initial_height = 1
batch_block = 100
confirm_block_count = 2
bridge_contract = "0x19Bd3121fEC07F047ac991e7b35C265a2B1F51eE"
validator_private_key = "af426ee077b1eb602fd011714cccf4398d0fc879fd3001a4c648487b1c3e7d2b"
[[chains]]
name = "mova"
rpc = "https://pegasus.rpc.caduceus.foundation"
initial_height = 1
batch_block = 100
confirm_block_count = 2
bridge_contract = "0xC160b598505c034A820f19e1C8b83ee5d2805A41"
validator_private_key = "af426ee077b1eb602fd011714cccf4398d0fc879fd3001a4c648487b1c3e7d2b"
[mysql]
host = "bridgedb"
port = 3306
user = "root"
password = "XN2UARuys3zy4Oux"
database = "bridge"
max_conn = 20
max_idle_conn = 10
[server]
listen = ":8080"
invalid_headers = [
"Mozilla/4.0 (compatible; MSIE 9.0; Windows NT 6.1)",
]
\ No newline at end of file
package config
import (
"flag"
"github.com/BurntSushi/toml"
)
type Config struct {
Debug bool
Chains map[string]*ChainConfig `toml:"chains"`
MySQL MySQLConfig
Server ServerConfig
}
type ChainConfig struct {
Name string `toml:"name"`
RPC string `toml:"rpc"`
InitialHeight int64 `toml:"initial_height"`
BatchBlock int `toml:"batch_block"`
BehindBlock int `toml:"behind_block"`
BridgeContract string `toml:"bridge_contract"`
ValidatorPrivateKey string `toml:"validator_private_key"`
ChainId int64 `toml:"chain_id"` // Will be populated by code
}
type MySQLConfig struct {
Host string
Port int
User string
Password string
Database string
MaxConn int `toml:"max_conn"`
MaxIdleConn int `toml:"max_idle_conn"`
}
type ServerConfig struct {
Listen string
InvalidHeaders []string `toml:"invalid_headers"`
}
var confPath = flag.String("c", "config.toml", "config file path")
func New() (*Config, error) {
var cfg Config
cfg.Chains = make(map[string]*ChainConfig)
// Parse the TOML configuration
_, err := toml.DecodeFile(*confPath, &cfg)
if err != nil {
return nil, err
}
// Process the chains from array to map using name as key
var chainArray []ChainConfig
_, err = toml.DecodeFile(*confPath, &struct {
Chains *[]ChainConfig `toml:"chains"`
*Config
}{
Chains: &chainArray,
Config: &cfg,
})
if err != nil {
return nil, err
}
// Convert to map for easier access
for _, chain := range chainArray {
chainCopy := chain // Create a copy to avoid pointer issues
cfg.Chains[chain.Name] = &chainCopy
}
return &cfg, nil
}
package constant
const JwtSecret = "uEj7AgDNCREwsvnTaCEtzDXt0I5eFDl8"
const (
InvalidParam = "invalid param"
UnsupportedPlatform = "unsupported platform"
InternalError = "internal error"
)
const (
TransferChainNoProcess = 0
TransferChainWaitConfirm = 1
TransferChainExecuted = 2
TransferChainRejected = 3
)
const (
ValidatorStatusNoPrecess = 0
ValidatorStatusConfirmation = 1
ValidatorStatusRejection = 2
ValidatorStatusFailure = 3
)
This source diff could not be displayed because it is too large. You can view the blob instead.
[
{
"inputs": [
{
"internalType": "address",
"name": "initialOwner",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "OwnableInvalidOwner",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "OwnableUnauthorizedAccount",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferred",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "prior",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "present",
"type": "uint256"
}
],
"name": "RequirementChange",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": true,
"internalType": "uint256",
"name": "toChainID",
"type": "uint256"
},
{
"indexed": false,
"internalType": "bool",
"name": "enabled",
"type": "bool"
}
],
"name": "TokenConfigChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "fromChainID",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "outId",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "inId",
"type": "uint256"
},
{
"indexed": false,
"internalType": "address",
"name": "receiver",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "TransferIn",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "inId",
"type": "uint256"
},
{
"indexed": false,
"internalType": "address",
"name": "validator",
"type": "address"
}
],
"name": "TransferInConfirmation",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "inId",
"type": "uint256"
},
{
"indexed": false,
"internalType": "address",
"name": "receiver",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "TransferInExecution",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "inId",
"type": "uint256"
},
{
"indexed": false,
"internalType": "address",
"name": "validator",
"type": "address"
}
],
"name": "TransferInRejection",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "outId",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "fromChainID",
"type": "uint256"
},
{
"indexed": false,
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "fee",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "toChainID",
"type": "uint256"
},
{
"indexed": false,
"internalType": "address",
"name": "receiver",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "receiveToken",
"type": "address"
}
],
"name": "TransferOut",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "oldTreasury",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newTreasury",
"type": "address"
}
],
"name": "TreasuryChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "minReserve",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "reserveRatio",
"type": "uint256"
},
{
"indexed": false,
"internalType": "bool",
"name": "enabled",
"type": "bool"
}
],
"name": "TreasuryConfigChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "contractBalance",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "treasuryBalance",
"type": "uint256"
}
],
"name": "TreasuryTransfer",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "validator",
"type": "address"
}
],
"name": "ValidatorAddition",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "validator",
"type": "address"
}
],
"name": "ValidatorRemoval",
"type": "event"
},
{
"inputs": [],
"name": "_inID",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "_outID",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "validator",
"type": "address"
}
],
"name": "addValidator",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "autoTransferToTreasury",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "calculateTreasuryTransfer",
"outputs": [
{
"internalType": "bool",
"name": "canTransfer",
"type": "bool"
},
{
"internalType": "uint256",
"name": "transferAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "willReserve",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "currentBalance",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint256",
"name": "toChainID",
"type": "uint256"
},
{
"components": [
{
"internalType": "address",
"name": "receiveToken",
"type": "address"
},
{
"internalType": "uint256",
"name": "fee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "limit",
"type": "uint256"
},
{
"internalType": "bool",
"name": "isBurn",
"type": "bool"
},
{
"internalType": "bool",
"name": "enabled",
"type": "bool"
}
],
"internalType": "struct Bridge.OutConfig",
"name": "config",
"type": "tuple"
}
],
"name": "changeOutConfig",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "required",
"type": "uint256"
}
],
"name": "changeValidRequired",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "inId",
"type": "uint256"
}
],
"name": "confirmInTransfer",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"internalType": "address",
"name": "to",
"type": "address"
}
],
"name": "emergencyWithdraw",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "getInId",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "toChainID",
"type": "uint256"
}
],
"name": "getSupportedTokensOut",
"outputs": [
{
"internalType": "address[]",
"name": "",
"type": "address[]"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "inTotal",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "inTransfers",
"outputs": [
{
"internalType": "uint256",
"name": "inId",
"type": "uint256"
},
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "receiver",
"type": "address"
},
{
"internalType": "uint256",
"name": "fee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "fromChainID",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "outId",
"type": "uint256"
},
{
"internalType": "bool",
"name": "executed",
"type": "bool"
},
{
"internalType": "uint256",
"name": "confirmCounter",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "rejectCounter",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "inId",
"type": "uint256"
}
],
"name": "isChecked",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "inId",
"type": "uint256"
},
{
"internalType": "address",
"name": "validator",
"type": "address"
}
],
"name": "isConfirmed",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "inId",
"type": "uint256"
},
{
"internalType": "address",
"name": "validator",
"type": "address"
}
],
"name": "isRejected",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
},
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "isTokenSupportedOut",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint256",
"name": "toChainID",
"type": "uint256"
}
],
"name": "isTokenTransferEnabled",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "isValidator",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "outConfiguration",
"outputs": [
{
"internalType": "address",
"name": "receiveToken",
"type": "address"
},
{
"internalType": "uint256",
"name": "fee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "limit",
"type": "uint256"
},
{
"internalType": "bool",
"name": "isBurn",
"type": "bool"
},
{
"internalType": "bool",
"name": "enabled",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "outTotal",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "toChainID",
"type": "uint256"
},
{
"internalType": "address",
"name": "receiver",
"type": "address"
}
],
"name": "outTransfer",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "outTransfers",
"outputs": [
{
"internalType": "uint256",
"name": "outId",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "fromChainID",
"type": "uint256"
},
{
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "fee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "toChainID",
"type": "uint256"
},
{
"internalType": "address",
"name": "receiver",
"type": "address"
},
{
"internalType": "address",
"name": "receiveToken",
"type": "address"
},
{
"internalType": "uint256",
"name": "receiveAmount",
"type": "uint256"
},
{
"internalType": "bytes32",
"name": "signature",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "inId",
"type": "uint256"
}
],
"name": "rejectInTransfer",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "validator",
"type": "address"
}
],
"name": "removeValidator",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "validator",
"type": "address"
},
{
"internalType": "address",
"name": "newValidator",
"type": "address"
}
],
"name": "replaceValidator",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "inId",
"type": "uint256"
},
{
"internalType": "address",
"name": "receiver",
"type": "address"
},
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "retryTransfer",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newTreasury",
"type": "address"
}
],
"name": "setTreasury",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint256",
"name": "minReserve",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "reserveRatio",
"type": "uint256"
},
{
"internalType": "bool",
"name": "enabled",
"type": "bool"
}
],
"name": "setTreasuryConfig",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "uint256",
"name": "toChainID",
"type": "uint256"
},
{
"internalType": "address",
"name": "receiver",
"type": "address"
},
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "outId",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "fromChainID",
"type": "uint256"
},
{
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"internalType": "address",
"name": "sendToken",
"type": "address"
},
{
"internalType": "uint256",
"name": "sendAmount",
"type": "uint256"
},
{
"internalType": "bytes32",
"name": "signature",
"type": "bytes32"
}
],
"internalType": "struct Bridge.submitParams",
"name": "params",
"type": "tuple"
}
],
"name": "submitInTransfer",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "supportedTokensOut",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "treasury",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "treasuryConfigs",
"outputs": [
{
"internalType": "uint256",
"name": "minReserve",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "reserveRatio",
"type": "uint256"
},
{
"internalType": "bool",
"name": "enabled",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "treasuryTotal",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "validRequired",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
}
]
\ No newline at end of file
package dao
import (
"code.wuban.net.cn/movabridge/token-bridge/config"
dbModel "code.wuban.net.cn/movabridge/token-bridge/model/db"
"context"
"fmt"
"time"
"github.com/ethereum/go-ethereum/ethclient"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/schema"
)
type Dao struct {
c *config.Config
db *gorm.DB
ethClient map[int64]*ethclient.Client
}
func New(_c *config.Config) (dao *Dao, err error) {
dao = &Dao{
c: _c,
ethClient: make(map[int64]*ethclient.Client),
}
// Connect to all configured chains
for name, chainConfig := range _c.Chains {
var client *ethclient.Client
client, err = ethclient.Dial(chainConfig.RPC)
if err != nil {
return nil, fmt.Errorf("failed to connect to %s chain: %w", name, err)
}
// Get and store chain ID
chainId, err := client.ChainID(context.Background())
if err != nil {
return nil, fmt.Errorf("failed to get %s chain ID: %w", name, err)
}
// Update the chain ID in the config
chainConfig.ChainId = chainId.Int64()
dao.ethClient[chainId.Int64()] = client
fmt.Printf("Connected to %s chain with ID %d\n", name, chainConfig.ChainId)
}
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True",
_c.MySQL.User, _c.MySQL.Password, _c.MySQL.Host, _c.MySQL.Port, _c.MySQL.Database)
// dbLogger := logger.Default.LogMode(logger.Silent)
// if _c.Debug {
// dbLogger = logger.Default.LogMode(logger.Info)
// }
dao.db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
NamingStrategy: schema.NamingStrategy{
SingularTable: true,
},
// Logger: dbLogger,
})
if err != nil {
return
}
sqlDB, err := dao.db.DB()
if err != nil {
return
}
sqlDB.SetMaxOpenConns(_c.MySQL.MaxConn)
sqlDB.SetMaxIdleConns(_c.MySQL.MaxIdleConn)
sqlDB.SetConnMaxIdleTime(time.Hour)
err = dao.db.AutoMigrate(&dbModel.Height{}, &dbModel.BridgeEvent{})
if err != nil {
panic(err)
}
return dao, nil
}
package dao
import (
dbModel "code.wuban.net.cn/movabridge/token-bridge/model/db"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/logger"
)
// GetStorageHeight 获取上次缓存的高度
func (d *Dao) GetStorageHeight(key string) (value int64, err error) {
storage := new(dbModel.Height)
err = d.db.Model(storage).Where("`key` = ?", key).First(storage).Error
if err == gorm.ErrRecordNotFound {
return 0, ErrRecordNotFound
}
return storage.IntValue, err
}
// SetStorageHeight 设置上次缓存的高度
func (d *Dao) SetStorageHeight(key string, intValue int64) (err error) {
tx := d.db.Session(&gorm.Session{Logger: d.db.Logger.LogMode(logger.Error)})
return tx.Model(&dbModel.Height{}).Clauses(clause.OnConflict{UpdateAll: true}).Create(&dbModel.Height{
Key: key,
IntValue: intValue,
}).Error
}
// func (d *Dao) CreateBridgeEvent(event *dbModel.BridgeEvent) (err error) {
// return d.db.Clauses(clause.OnConflict{DoNothing: true}).Create(event).Error
// }
//
// func (d *Dao) GetBridgeEventWithOutInfo(fromChain int64, outId int64) (event *dbModel.BridgeEvent, err error) {
// event = new(dbModel.BridgeEvent)
// err = d.db.Model(event).Where("`from_chain` = ? AND `out_id` = ?", fromChain, outId).First(event).Error
// if err == gorm.ErrRecordNotFound {
// return nil, ErrRecordNotFound
// }
// return event, err
// }
//
// func (d *Dao) GetBridgeEventWithInInfo(chain int64, inId int64) (event *dbModel.BridgeEvent, err error) {
// event = new(dbModel.BridgeEvent)
// err = d.db.Model(event).Where("`to_chain` = ? AND `in_id` = ?", chain, inId).First(event).Error
// if err == gorm.ErrRecordNotFound {
// return nil, ErrRecordNotFound
// }
// return event, err
// }
//
// func (d *Dao) UpdateBridgeWithTransferIn(event *dbModel.BridgeEvent) (err error) {
// return d.db.Model(event).Where("`id` = ?", event.ID).Updates(map[string]interface{}{
// "to_contract": event.ToContract,
// "in_timestamp": event.InTimestamp,
// "in_id": event.InId,
// "to_chain_tx_hash": event.ToChainTxHash,
// "to_chain_status": event.ToChainStatus,
// }).Error
// }
//
// func (d *Dao) UpdateBridgeResult(event *dbModel.BridgeEvent, toChainHash string, status int) (err error) {
// return d.db.Model(&dbModel.BridgeEvent{}).Where("`id` = ?", event.ID).Updates(map[string]interface{}{
// "to_chain_status": status,
// "finish_tx_hash": toChainHash,
// }).Error
// }
func (d *Dao) UpdateBridgeValidatorOperation(event *dbModel.BridgeEvent, op int) (err error) {
return d.db.Model(&dbModel.BridgeEvent{}).Where("`id` = ?", event.ID).Updates(map[string]interface{}{
"validator_status": op,
}).Error
}
//
//func (d *Dao) CreateValidatorEvent(hash string, chain int64, validator string, txHash string, eventType string, transferInId int64) (err error) {
// event := &dbModel.ValidatorEvent{
// ChainId: chain,
// Validator: validator,
// TxHash: txHash,
// Event: eventType,
// TransferInId: transferInId,
// Hash: hash,
// }
// return d.db.Clauses(clause.OnConflict{DoNothing: true}).Create(event).Error
//}
package dao
import (
dbModel "code.wuban.net.cn/movabridge/token-bridge/model/db"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
var (
ErrRecordNotFound = gorm.ErrRecordNotFound
)
// Transaction represents a database transaction
type Transaction struct {
tx *gorm.DB
}
// BeginTx starts a new transaction
func (d *Dao) BeginTx() (*Transaction, error) {
tx := d.db.Begin()
if tx.Error != nil {
return nil, tx.Error
}
return &Transaction{tx: tx}, nil
}
// Commit commits the transaction
func (tx *Transaction) Commit() error {
return tx.tx.Commit().Error
}
// Rollback aborts the transaction
func (tx *Transaction) Rollback() error {
return tx.tx.Rollback().Error
}
// Transaction-aware versions of the database methods
func (d *Dao) CreateBridgeEventTx(tx *Transaction, event *dbModel.BridgeEvent) error {
return tx.tx.Clauses(clause.OnConflict{DoNothing: true}).Create(event).Error
}
func (d *Dao) GetBridgeEventWithOutInfoTx(tx *Transaction, fromChain int64, outId int64) (event *dbModel.BridgeEvent, err error) {
event = new(dbModel.BridgeEvent)
err = tx.tx.Model(event).Where("`from_chain` = ? AND `out_id` = ?", fromChain, outId).First(event).Error
if err == gorm.ErrRecordNotFound {
return nil, ErrRecordNotFound
}
return event, err
}
func (d *Dao) GetBridgeEventWithInInfoTx(tx *Transaction, chain int64, inId int64) (event *dbModel.BridgeEvent, err error) {
event = new(dbModel.BridgeEvent)
err = tx.tx.Model(event).Where("`to_chain` = ? AND `in_id` = ?", chain, inId).First(event).Error
if err == gorm.ErrRecordNotFound {
return nil, ErrRecordNotFound
}
return event, err
}
func (d *Dao) UpdateBridgeWithTransferInTx(tx *Transaction, event *dbModel.BridgeEvent) error {
return tx.tx.Model(event).Where("`id` = ?", event.ID).Updates(map[string]interface{}{
"to_contract": event.ToContract,
"in_timestamp": event.InTimestamp,
"in_id": event.InId,
"to_chain_tx_hash": event.ToChainTxHash,
"to_chain_status": event.ToChainStatus,
}).Error
}
func (d *Dao) UpdateBridgeResultTx(tx *Transaction, event *dbModel.BridgeEvent, toChainHash string, status int) error {
return tx.tx.Model(&dbModel.BridgeEvent{}).Where("`id` = ?", event.ID).Updates(map[string]interface{}{
"to_chain_status": status,
"finish_tx_hash": toChainHash,
}).Error
}
func (d *Dao) CreateValidatorEventTx(tx *Transaction, hash string, chain int64, validator string, txHash string, eventType string, transferInId int64) error {
event := &dbModel.ValidatorEvent{
ChainId: chain,
Validator: validator,
TxHash: txHash,
Event: eventType,
TransferInId: transferInId,
Hash: hash,
}
return tx.tx.Clauses(clause.OnConflict{DoNothing: true}).Create(event).Error
}
package dao
import (
"code.wuban.net.cn/movabridge/token-bridge/config"
"code.wuban.net.cn/movabridge/token-bridge/constant"
"code.wuban.net.cn/movabridge/token-bridge/contract/bridge"
dbModel "code.wuban.net.cn/movabridge/token-bridge/model/db"
"context"
"errors"
"github.com/ethereum/go-ethereum/accounts/abi"
"golang.org/x/crypto/sha3"
"math/big"
"time"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
log "github.com/sirupsen/logrus"
)
func (d *Dao) GetBlockHeight(chain *config.ChainConfig, behindBlock ...int) (height int64, err error) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if _, ok := d.ethClient[chain.ChainId]; !ok {
return 0, errors.New("chain client not support")
}
n, err := d.ethClient[chain.ChainId].BlockNumber(ctx)
if len(behindBlock) > 0 {
n -= uint64(behindBlock[0])
if n < 0 {
n = 0
}
}
return int64(n), err
}
func (d *Dao) GetLatestBockHash(chain *config.ChainConfig) (hash string, err error) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if _, ok := d.ethClient[chain.ChainId]; !ok {
return "", errors.New("chain client not support")
}
block, err := d.ethClient[chain.ChainId].BlockByNumber(ctx, nil)
if err != nil {
return
}
return block.Hash().Hex(), nil
}
func (d *Dao) GetBlockTime(chain *config.ChainConfig, height int) (timestamp int, err error) {
if _, ok := d.ethClient[chain.ChainId]; !ok {
return 0, errors.New("chain client not support")
}
for i := 0; i < 2; i++ {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
block, err := d.ethClient[chain.ChainId].BlockByNumber(ctx, big.NewInt(int64(height)))
if err == nil {
return int(block.Time()), nil
}
}
return
}
func (d *Dao) GetLogs(chain *config.ChainConfig, beginHeight, endHeight int64, topics, addresses []string) (logs []types.Log, err error) {
if _, ok := d.ethClient[chain.ChainId]; !ok {
return nil, errors.New("chain client not support")
}
for i := 0; i < 2; i++ {
// 重试2次
logs, err = d.getLogs(chain, beginHeight, endHeight, topics, addresses)
if err == nil {
return logs, nil
}
}
return
}
func (d *Dao) getLogs(chain *config.ChainConfig, beginHeight, endHeight int64, topics []string, addresses []string) (logs []types.Log, err error) {
addrs := make([]common.Address, 0)
for _, addr := range addresses {
addrs = append(addrs, common.HexToAddress(addr))
}
q := ethereum.FilterQuery{
FromBlock: big.NewInt(int64(beginHeight)),
ToBlock: big.NewInt(int64(endHeight)),
Topics: [][]common.Hash{{}},
Addresses: addrs,
}
for _, topic := range topics {
q.Topics[0] = append(q.Topics[0], common.HexToHash(topic))
}
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
return d.ethClient[chain.ChainId].FilterLogs(ctx, q)
}
func (d *Dao) buildParam(event *dbModel.BridgeEvent) (bridge.BridgesubmitParams, error) {
sendAmount, _ := new(big.Int).SetString(event.SendAmount, 10)
receiveAmount, _ := new(big.Int).SetString(event.ReceiveAmount, 10)
param := bridge.BridgesubmitParams{
ToChainID: big.NewInt(event.ToChain),
Receiver: common.HexToAddress(event.Receiver),
Token: common.HexToAddress(event.ToToken),
Amount: receiveAmount,
OutId: big.NewInt(int64(event.OutId)),
FromChainID: big.NewInt(event.FromChain),
Sender: common.HexToAddress(event.FromAddress),
SendToken: common.HexToAddress(event.FromToken),
SendAmount: sendAmount,
}
u256Type, _ := abi.NewType("uint256", "", nil)
addrType, _ := abi.NewType("address", "", nil)
arguments := abi.Arguments{
{Type: u256Type},
{Type: u256Type},
{Type: addrType},
{Type: addrType},
{Type: u256Type},
{Type: u256Type},
{Type: addrType},
{Type: addrType},
}
data, err := arguments.Pack(param.OutId, param.FromChainID, param.Sender,
param.SendToken, param.SendAmount, param.ToChainID, param.Receiver, param.Token)
if err != nil {
return bridge.BridgesubmitParams{}, err
}
sh := sha3.NewLegacyKeccak256()
sh.Write(data)
signature := sh.Sum(nil)
copy(param.Signature[:], signature)
return param, nil
}
func (d *Dao) SubmitInTransfer(event *dbModel.BridgeEvent) error {
if _, ok := d.ethClient[event.ToChain]; !ok {
return errors.New("chain client not support")
}
var chain *config.ChainConfig
for _, c := range d.c.Chains {
if c.ChainId == event.ToChain {
chain = c
break
}
}
if chain == nil {
return errors.New("chain not found in config")
}
// verify the event is valid.
valid := d.CheckEventValid()
if !valid {
log.WithField("chainId", chain.ChainId).Error("event is not valid")
return errors.New("event is not valid")
}
ca, err := bridge.NewBridgeContract(common.HexToAddress(chain.BridgeContract), d.ethClient[event.ToChain])
if err != nil {
return err
}
k := chain.ValidatorPrivateKey
signPrivateKey, err := crypto.HexToECDSA(common.Bytes2Hex(common.FromHex(k)))
if err != nil {
log.WithField("chainId", chain.ChainId).WithError(err).Error("failed to parse private key")
return err
}
opts, err := bind.NewKeyedTransactorWithChainID(signPrivateKey, big.NewInt(int64(chain.ChainId)))
if err != nil {
log.WithField("chainId", chain.ChainId).WithError(err).Error("new keyed transfer failed")
return err
}
param, err := d.buildParam(event)
if err != nil {
log.WithField("chainId", chain.ChainId).WithError(err).Error("build param failed")
return err
}
if tx, err := ca.SubmitInTransfer(opts, param); err != nil {
log.WithField("chainId", chain.ChainId).WithError(err).Error("failed to submit in transfer")
return err
} else {
// update validator status.
log.WithField("chainId", chain.ChainId).Infof("submit in transfer tx hash: %s", tx.Hash().Hex())
return d.UpdateBridgeValidatorOperation(event, constant.ValidatorStatusConfirmation)
}
}
func (d *Dao) CheckEventValid() bool {
// Implement the logic to check if the event is valid.
// This is a placeholder implementation.
return true
}
package dao
import (
"bytes"
"code.wuban.net.cn/movabridge/token-bridge/contract/bridge"
"encoding/hex"
"fmt"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"golang.org/x/crypto/sha3"
"math/big"
"testing"
)
func TestDao_AbiEncode(t *testing.T) {
ether := new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil)
param := bridge.BridgesubmitParams{
ToChainID: big.NewInt(2),
Receiver: common.HexToAddress("0x000000000000000000000000000000000000dead"),
Token: common.HexToAddress("0x000000000000000000000000000000000000beef"),
Amount: new(big.Int).Mul(big.NewInt(10), ether),
OutId: big.NewInt(1),
FromChainID: big.NewInt(1),
Sender: common.HexToAddress("0x000000000000000000000000000000000000cafe"),
SendToken: common.HexToAddress("0x000000000000000000000000000000000000babe"),
SendAmount: new(big.Int).Mul(big.NewInt(10), ether),
}
expectHash := common.HexToHash("0x7be55178ff6b46f92c87979ffdffc36e242f0e559556c89f1f8403a71e72e09c")
u256Type, _ := abi.NewType("uint256", "", nil)
addrType, _ := abi.NewType("address", "", nil)
arguments := abi.Arguments{
{Type: u256Type},
{Type: u256Type},
{Type: addrType},
{Type: addrType},
{Type: u256Type},
{Type: u256Type},
{Type: addrType},
{Type: addrType},
}
data, err := arguments.Pack(param.OutId, param.FromChainID, param.Sender, param.SendToken, param.SendAmount, param.ToChainID, param.Receiver, param.Token)
if err != nil {
t.Fatalf("failed to encode abi: %v", err)
}
fmt.Println("data=0x", hex.EncodeToString(data))
sh := sha3.NewLegacyKeccak256()
sh.Write(data)
signature := sh.Sum(nil)
if bytes.Compare(signature, expectHash[:]) != 0 {
t.Errorf("expect hash: %x, got: %x", expectHash, signature)
}
}
networks:
default:
name: bridge-network
services:
bridgedb:
image: mysql:8
container_name: bridgedb
volumes:
- ./data/db:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: "XN2UARuys3zy4Oux"
MYSQL_DATABASE: "bridge"
healthcheck:
test: [ "CMD", "mysqladmin", "ping", "-h", "localhost", "-uroot", "--password=$$(cat $$MYSQL_ROOT_PASSWORD)" ]
interval: 10s
timeout: 5s
retries: 5
start_period: 5s
validator:
image: token-bridge:latest
container_name: bridge-validator
depends_on:
bridgedb:
condition: service_healthy
volumes:
- ./config.toml:/app/config.toml
command:
- "/bin/sh"
- "-c"
- "/usr/bin/validator -c /app/config.toml"
restart:
unless-stopped
\ No newline at end of file
module code.wuban.net.cn/movabridge/token-bridge
go 1.24.0
require (
github.com/BurntSushi/toml v1.5.0
github.com/btcsuite/btcutil v1.0.2
github.com/ethereum/go-ethereum v1.16.2
github.com/gin-contrib/cors v1.7.6
github.com/gin-gonic/gin v1.10.1
github.com/google/uuid v1.6.0
github.com/shopspring/decimal v1.4.0
github.com/sirupsen/logrus v1.9.3
golang.org/x/crypto v0.41.0
gorm.io/driver/mysql v1.6.0
gorm.io/gorm v1.30.1
)
require (
filippo.io/edwards25519 v1.1.0 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/StackExchange/wmi v1.2.1 // indirect
github.com/bits-and-blooms/bitset v1.20.0 // indirect
github.com/btcsuite/btcd v0.20.1-beta // indirect
github.com/bytedance/sonic v1.13.3 // indirect
github.com/bytedance/sonic/loader v0.2.4 // indirect
github.com/cloudwego/base64x v0.1.5 // indirect
github.com/consensys/gnark-crypto v0.18.0 // indirect
github.com/crate-crypto/go-eth-kzg v1.3.0 // indirect
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect
github.com/deckarep/golang-set/v2 v2.6.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/ethereum/c-kzg-4844/v2 v2.1.0 // indirect
github.com/ethereum/go-verkle v0.2.2 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
github.com/gin-contrib/sse v1.1.0 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.26.0 // indirect
github.com/go-sql-driver/mysql v1.8.1 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/holiman/uint256 v1.3.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect
github.com/supranational/blst v0.3.14 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.3.0 // indirect
golang.org/x/arch v0.18.0 // indirect
golang.org/x/net v0.42.0 // indirect
golang.org/x/sync v0.16.0 // indirect
golang.org/x/sys v0.35.0 // indirect
golang.org/x/text v0.28.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI=
github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI=
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3MdfoPyRVU=
github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw=
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2uts=
github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts=
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
github.com/bytedance/sonic v1.13.3 h1:MS8gmaH16Gtirygw7jV91pDCN33NyMrPbN7qiYhEsF0=
github.com/bytedance/sonic v1.13.3/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk=
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I=
github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8=
github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4=
github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M=
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE=
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
github.com/cockroachdb/pebble v1.1.5 h1:5AAWCBWbat0uE0blr8qzufZP5tBjkRyy/jWe1QWLnvw=
github.com/cockroachdb/pebble v1.1.5/go.mod h1:17wO9el1YEigxkP/YtV8NtCivQDgoCyBg5c4VR/eOWo=
github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30=
github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo=
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ=
github.com/consensys/gnark-crypto v0.18.0 h1:vIye/FqI50VeAr0B3dx+YjeIvmc3LWz4yEfbWBpTUf0=
github.com/consensys/gnark-crypto v0.18.0/go.mod h1:L3mXGFTe1ZN+RSJ+CLjUt9x7PNdx8ubaYfDROyp2Z8c=
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/crate-crypto/go-eth-kzg v1.3.0 h1:05GrhASN9kDAidaFJOda6A4BEvgvuXbazXg/0E3OOdI=
github.com/crate-crypto/go-eth-kzg v1.3.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI=
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a h1:W8mUrRp6NOVl3J+MYp5kPMoUZPp7aOYHtaua31lwRHg=
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a/go.mod h1:sTwzHBvIzm2RfVCGNEBZgRyjwK40bVoun3ZnGOCafNM=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM=
github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
github.com/deepmap/oapi-codegen v1.6.0 h1:w/d1ntwh91XI0b/8ja7+u5SvA4IFfM0UNNLmiDR1gg0=
github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M=
github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A=
github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s=
github.com/ethereum/c-kzg-4844/v2 v2.1.0 h1:gQropX9YFBhl3g4HYhwE70zq3IHFRgbbNPw0Shwzf5w=
github.com/ethereum/c-kzg-4844/v2 v2.1.0/go.mod h1:TC48kOKjJKPbN7C++qIgt0TJzZ70QznYR7Ob+WXl57E=
github.com/ethereum/go-ethereum v1.16.2 h1:VDHqj86DaQiMpnMgc7l0rwZTg0FRmlz74yupSG5SnzI=
github.com/ethereum/go-ethereum v1.16.2/go.mod h1:X5CIOyo8SuK1Q5GnaEizQVLHT/DfsiGWuNeVdQcEMNA=
github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8=
github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk=
github.com/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY=
github.com/ferranbt/fastssz v0.1.4/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI=
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps=
github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
github.com/gin-contrib/cors v1.7.6 h1:3gQ8GMzs1Ylpf70y8bMw4fVpycXIeX1ZemuSQIsnQQY=
github.com/gin-contrib/cors v1.7.6/go.mod h1:Ulcl+xN4jel9t1Ry8vqph23a60FwH9xVLd+3ykmTjOk=
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk=
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/graph-gophers/graphql-go v1.3.0 h1:Eb9x/q6MFpCLz7jBCiP/WTxjSDrYLR1QY41SORZyNJ0=
github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc=
github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE=
github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0=
github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 h1:X4egAf/gcS1zATw6wn4Ej8vjuVGxeHdan+bRb2ebyv4=
github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc=
github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao=
github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA=
github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA=
github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
github.com/influxdata/influxdb-client-go/v2 v2.4.0 h1:HGBfZYStlx3Kqvsv1h2pJixbCl/jhnFtxpKFAv9Tu5k=
github.com/influxdata/influxdb-client-go/v2 v2.4.0/go.mod h1:vLNHdxTJkIf2mSLvGrpj8TCcISApPoXkaxP8g9uRlW8=
github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c h1:qSHzRbhzK8RdXOsAdfDgO49TtqC1oZ+acxPrkfTxcCs=
github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 h1:W9WBk7wlPfJLvMCdtV4zPulc4uCPrlywQOmbFOhgQNU=
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo=
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4=
github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4=
github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A=
github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 h1:oYW+YCJ1pachXTQmzR3rNLYGGz4g/UgFcjb28p/viDM=
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8=
github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
github.com/pion/stun/v2 v2.0.0 h1:A5+wXKLAypxQri59+tmQKVs7+l6mMM+3d+eER9ifRU0=
github.com/pion/stun/v2 v2.0.0/go.mod h1:22qRSh08fSEttYUmJZGlriq9+03jtVmXNODgLccj8GQ=
github.com/pion/transport/v2 v2.2.1 h1:7qYnCBlpgSJNYMbLCKuSY9KbQdBFoETvPNETv0y4N7c=
github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g=
github.com/pion/transport/v3 v3.0.1 h1:gDTlPJwROfSfz6QfSi0ZmeCSkFcnWWiiR9ES0ouANiM=
github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM=
github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU=
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/supranational/blst v0.3.14 h1:xNMoHRJOTwMn63ip6qoWJ2Ymgvj7E2b9jY2FAwY+qRo=
github.com/supranational/blst v0.3.14/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
golang.org/x/arch v0.18.0 h1:WN9poc33zL4AzGxqf8VtpKUnGvMi8O9lhNyBMF/85qc=
golang.org/x/arch v0.18.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg=
gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo=
gorm.io/gorm v1.30.1 h1:lSHg33jJTBxs2mgJRfRZeLDG+WZaHYCk3Wtfl6Ngzo4=
gorm.io/gorm v1.30.1/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
package middleware
import (
"math/rand"
"strings"
"time"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
)
func CheckHeaderMiddleware(invalidHeaders []string) gin.HandlerFunc {
for _, invalidHeader := range invalidHeaders {
log.WithField("invalid-header", invalidHeader).Debug("init invalid header")
}
return func(c *gin.Context) {
// 获取user-agent
userAgent := c.Request.Header.Get("User-Agent")
for _, invalidHeader := range invalidHeaders {
if strings.Contains(userAgent, invalidHeader) {
time.Sleep(time.Millisecond * time.Duration(rand.Intn(100)+10))
c.JSON(200, gin.H{
"code": 0,
"msg": "ok",
"data": "",
})
log.WithFields(log.Fields{"user-agent": userAgent}).Debug("invalid header, return fake data")
c.Abort()
return
}
}
c.Next()
}
}
package middleware
import (
"bytes"
"io"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
)
func PrintRequestResponseBodyMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// 读取请求 body
var requestBody []byte
if c.Request.Body != nil {
requestBody, _ = io.ReadAll(c.Request.Body)
c.Request.Body = io.NopCloser(bytes.NewBuffer(requestBody))
}
log.WithFields(log.Fields{"method": c.Request.Method, "uri": c.Request.RequestURI, "body": string(requestBody)}).Debug("request body")
bodyWriter := &responseBodyWriter{body: bytes.NewBufferString(""), ResponseWriter: c.Writer}
c.Writer = bodyWriter
c.Next()
responseBody := bodyWriter.body.String()
log.WithFields(log.Fields{"status": c.Writer.Status(), "body": responseBody}).Debug("response body")
}
}
type responseBodyWriter struct {
gin.ResponseWriter
body *bytes.Buffer
}
func (r *responseBodyWriter) Write(b []byte) (int, error) {
r.body.Write(b)
return r.ResponseWriter.Write(b)
}
package middleware
package dbModel
import (
"gorm.io/gorm"
)
type Height struct {
Key string `gorm:"primaryKey"`
IntValue int64 `gorm:"type:int;not null"` // 配置value
}
type BridgeEvent struct {
FromChain int64 `gorm:"type:int;comment:源链"`
OutTimestamp int64 `gorm:"type:int;comment:Out时间戳"`
FromContract string `gorm:"type:varchar(255);comment:源合约"`
FromToken string `gorm:"type:varchar(255);comment:源token"`
FromAddress string `gorm:"type:varchar(255);index;comment:源地址"` // 用户地址
FromChainTxHash string `gorm:"type:varchar(255);index;comment:源链交易hash"` // 源链交易hash
SendAmount string `gorm:"type:varchar(255);comment:发送金额"` // 发送金额
FeeAmount string `gorm:"type:varchar(255);comment:手续费金额"` // 手续费金额
Receiver string `gorm:"type:varchar(255);comment:接收者地址"` // 目标链接收者地址
ToChain int64 `gorm:"type:int;comment:目标链"`
ToToken string `gorm:"type:varchar(255);comment:目标token"` // 目标链token
ReceiveAmount string `gorm:"type:varchar(255);comment:接收金额"` // 接收金额 = 发送金额 - 手续费金额
Hash string `gorm:"type:varchar(255);uniqueIndex;comment:bridge hash"`
OutId int64 `gorm:"type:int;index;comment:源链转出ID"`
InTimestamp int64 `gorm:"type:int;comment:Out时间戳"`
InId int64 `gorm:"type:int;index;comment:源链转入ID"`
ToContract string `gorm:"type:varchar(255);comment:目标合约(toAddr)"`
FinishTxHash string `gorm:"type:varchar(255);comment:完成交易hash"` // 完成交易hash
ToChainTxHash string `gorm:"type:varchar(255);comment:目标链交易hash"`
ToChainStatus int `gorm:"type:int;comment:目标链状态"` // 0未执行, 1等待确认, 2已执行, 3已拒绝
ValidatorStatus int `gorm:"type:int;comment:验证者状态"` // 0未验证, 1已确认, 2已拒绝, 3操作失败
gorm.Model
}
type ValidatorEvent struct {
ChainId int64 `gorm:"type:int;comment:链ID"`
Validator string `gorm:"type:varchar(255);index;comment:验证者地址"` // 验证者地址
TxHash string `gorm:"type:varchar(255);index;comment:交易hash"` // 交易hash
Event string `gorm:"type:varchar(255);index;comment:事件类型"` // 事件类型
TransferInId int64 `gorm:"type:int;index;comment:转入ID"` // 转入ID
Hash string `gorm:"type:varchar(255);uniqueIndex;comment:event hash"`
gorm.Model
}
# How to run
1. make docker
2. modify config.yml for chain and mysql password.
3. modify docker-compose.yml for mysql password.
4. docker compose up -d
5. docker compose down
\ No newline at end of file
package util
import (
"encoding/binary"
"fmt"
"strings"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)
func TestGenHDKey(t *testing.T) {
// xpub := "xpub69CSVbN4XnU6ituHBAnS4urNA2uiwcCciXWjJ7BKgsM5YAsQV1AfE1gEi7Aeip6dE6kFs3suNghVQukcmoGcdFYPohyAyX7KnxJNnqr2XSU"
xpub := "xpub6C2ojpneBn4KHz1zaHpsyVHQMuJQbeDPbdkXywAUR43hXpjyQNcRv1ZQdvxnGmGKvXLoGPoN1G7cwfmW5CGZjPagpLnggXmqN52HhJk9F4B"
// k, _ := GetPubKeyByPub(xpub, "0/1")
// addr := crypto.PubkeyToAddress(*k)
// t.Log(addr.String())
userA := common.HexToAddress("0xAaf459E071637dE222D0a9e3b439704f478ed767")
path := convertAddrToPath(userA.String())
st := time.Now()
k, _ := GetPubKeyByPub(xpub, path)
addr2 := crypto.PubkeyToAddress(*k)
t.Log(path, addr2.String())
t.Log(time.Since(st))
a, _ := GetDepositAddress(userA, xpub)
t.Log(a.String())
t.Log(addr2.String(), time.Since(st))
}
func convertAddrToPath(address string) string {
addrBytes := common.HexToAddress(address).Bytes()
// split to 8
addrByteArray := make([]string, 7)
for i := 0; i < 7; i++ {
uint32Temp := uint32(0)
if i == 6 {
// fmt.Println("tt", tt)
// binary.BigEndian.PutUint32(tt, addrByteArray[i])
// fmt.Println("after tt", addrByteArray[i])
uint32Temp = binary.BigEndian.Uint32(common.LeftPadBytes(addrBytes[i*3:i*3+2], 4))
} else {
// addrByteArray[i] = addrBytes[i*3 : i*3+3]
// binary.BigEndian.PutUint32(temp4Byte(addrBytes[i*3:i*3+3]), addrByteArray[i])
uint32Temp = binary.BigEndian.Uint32(common.LeftPadBytes(addrBytes[i*3:i*3+3], 4))
}
addrByteArray[i] = fmt.Sprintf("%d", uint32Temp)
}
fmt.Println(strings.Join(addrByteArray, "/"))
return strings.Join(addrByteArray, "/")
}
package util
import (
"crypto/ecdsa"
"encoding/binary"
"fmt"
"strconv"
"strings"
"github.com/btcsuite/btcutil/hdkeychain"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)
func GetDepositAddress(input common.Address, xpub string) (output common.Address, err error) {
paths := make([]string, 7)
for i := 0; i < 7; i++ {
uint32Temp := uint32(0)
if i == 6 {
uint32Temp = binary.BigEndian.Uint32(common.LeftPadBytes(input.Bytes()[i*3:i*3+2], 4))
} else {
uint32Temp = binary.BigEndian.Uint32(common.LeftPadBytes(input.Bytes()[i*3:i*3+3], 4))
}
paths[i] = fmt.Sprintf("%d", uint32Temp)
}
pubkey, err := GetPubKeyByPub(xpub, strings.Join(paths, "/"))
if err != nil {
return common.Address{}, err
}
return crypto.PubkeyToAddress(*pubkey), nil
}
func GetPubKeyByPub(xpub string, path string) (pubkey *ecdsa.PublicKey, err error) {
pathList := parsePath(path)
var next *hdkeychain.ExtendedKey
for _, floor := range pathList {
idx := floor[0]
isHardened, _ := strconv.ParseBool(strconv.Itoa(floor[1]))
next, err = nextFloor(xpub, isHardened, uint32(idx))
if err != nil {
return
}
xpub = next.String()
}
pk, err := next.ECPubKey()
if err != nil {
return
}
return pk.ToECDSA(), nil
}
// 返回一个二维数组 参数1 对应每一层偏移 参数2 1代表hardened 0普通
func parsePath(path string) [][]int {
l := strings.Split(path, "/")
var resList [][]int
// m开头或者/开头 去掉第一个
if l[0] == "m" || l[0] == "" {
l = l[1:]
}
// /结尾 去掉最后一个
if l[len(l)-1] == "" {
l = l[:len(l)-1]
}
for _, s := range l {
if strings.HasSuffix(s, "'") {
idx, _ := strconv.Atoi(s[:len(s)-1])
resList = append(resList, []int{idx, 1})
} else {
idx, _ := strconv.Atoi(s)
resList = append(resList, []int{idx, 0})
}
}
return resList
}
func nextFloor(key string, hardened bool, idx uint32) (*hdkeychain.ExtendedKey, error) {
key1, err := hdkeychain.NewKeyFromString(key)
if err != nil {
return nil, err
}
if hardened {
return key1.Child(hdkeychain.HardenedKeyStart + idx)
} else {
return key1.Child(idx)
}
}
package util
import (
"crypto/md5"
"fmt"
"math/big"
"time"
"golang.org/x/crypto/sha3"
)
func TextToHash(data []byte) []byte {
msg := fmt.Sprintf("\x19Ethereum Signed Mesage:\n%d%s", len(data), string(data))
hasher := sha3.NewLegacyKeccak256()
hasher.Write([]byte(msg))
return hasher.Sum(nil)
}
func GetUnixDay() *big.Int {
return big.NewInt((time.Now().Unix() + 8*3600) / 86400)
}
func GetNonce(user string) (nonce *big.Int) {
payload := append([]byte(user), GetUnixDay().Bytes()...)
payload = append(payload, []byte{0x01}...)
hash := md5.Sum(payload)
return new(big.Int).SetBytes(hash[:6])
}
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