Commit 0e4a7fa6 authored by vicotor's avatar vicotor

update code

parent 6363dce5
...@@ -11,7 +11,7 @@ WORKDIR /build ...@@ -11,7 +11,7 @@ WORKDIR /build
COPY ./ /build/token-bridge COPY ./ /build/token-bridge
RUN cd /build/token-bridge && go mod tidy && go build -v -o /tmp/validator ./cmd/validator RUN cd /build/token-bridge && go mod tidy && go build -ldflags="-s -w" -o /tmp/validator ./cmd/validator
FROM alpine FROM alpine
......
.PHONY: default all clean dev .PHONY: default all clean dev
GOBIN = $(shell pwd)/build/bin GOBIN = $(shell pwd)/build/bin
BUILD_FLAGS = -ldflags "-s -w"
default: all default: all
......
...@@ -23,6 +23,7 @@ type ChainSync struct { ...@@ -23,6 +23,7 @@ type ChainSync struct {
quit chan struct{} quit chan struct{}
stopOnce sync.Once stopOnce sync.Once
wg sync.WaitGroup wg sync.WaitGroup
syncmode bool
} }
func (s *ChainSync) ParseTransferOut(log types.Log) (*bridge.BridgeContractTransferOut, error) { func (s *ChainSync) ParseTransferOut(log types.Log) (*bridge.BridgeContractTransferOut, error) {
...@@ -57,6 +58,7 @@ func NewChainSync(_chain *config.ChainConfig, _d *dao.Dao) (sync *ChainSync) { ...@@ -57,6 +58,7 @@ func NewChainSync(_chain *config.ChainConfig, _d *dao.Dao) (sync *ChainSync) {
heightKey: fmt.Sprintf("%d_%s", _chain.ChainId, "height"), heightKey: fmt.Sprintf("%d_%s", _chain.ChainId, "height"),
bridgeCa: bridgeCa, bridgeCa: bridgeCa,
quit: make(chan struct{}), quit: make(chan struct{}),
syncmode: true,
} }
return sync return sync
...@@ -97,10 +99,21 @@ func (s *ChainSync) loop() { ...@@ -97,10 +99,21 @@ func (s *ChainSync) loop() {
log.WithField("chain", s.name).WithError(err).Error("get latest block height") log.WithField("chain", s.name).WithError(err).Error("get latest block height")
continue continue
} }
if latestHeight <= beginHeight { if latestHeight <= beginHeight {
s.syncmode = false
continue continue
} }
if latestTime, err := s.d.GetBlockTime(s.chain, latestHeight); err == nil {
blockTime := time.Unix(int64(latestTime), 0)
if time.Since(blockTime) < time.Minute {
s.syncmode = false
} else {
s.syncmode = true
}
}
if latestHeight < endHeight { if latestHeight < endHeight {
endHeight = latestHeight endHeight = latestHeight
} }
...@@ -177,3 +190,7 @@ func (s *ChainSync) SyncLogs(beginHeight, endHeight int64) error { ...@@ -177,3 +190,7 @@ func (s *ChainSync) SyncLogs(beginHeight, endHeight int64) error {
return s.d.HandleEvents(s, logs) return s.d.HandleEvents(s, logs)
} }
func (s *ChainSync) IsSyncing() bool {
return s.syncmode
}
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/hex"
"fmt"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"io"
"os"
)
func encryptText(plaintext string, keyHex string) (string, error) {
// Decode the hex key
key, err := hex.DecodeString(keyHex)
if err != nil {
return "", fmt.Errorf("invalid hex key: %v", err)
}
// Create AES cipher
block, err := aes.NewCipher(key)
if err != nil {
return "", fmt.Errorf("failed to create cipher: %v", err)
}
// Create GCM mode
gcm, err := cipher.NewGCM(block)
if err != nil {
return "", fmt.Errorf("failed to create GCM: %v", err)
}
// Generate random nonce
nonce := make([]byte, gcm.NonceSize())
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return "", fmt.Errorf("failed to generate nonce: %v", err)
}
// Encrypt the text
ciphertext := gcm.Seal(nonce, nonce, []byte(plaintext), nil)
return hex.EncodeToString(ciphertext), nil
}
var encryptCmd = &cobra.Command{
Use: "encrypt",
Short: "Encrypt text using AES-256-GCM",
Long: "Encrypt text using AES-256-GCM encryption with the provided key",
Run: func(cmd *cobra.Command, args []string) {
// Get flags
inputFile, _ := cmd.Flags().GetString("in")
outputFile, _ := cmd.Flags().GetString("out")
keyFile, _ := cmd.Flags().GetString("aes")
// Read the AES key
keyData, err := os.ReadFile(keyFile)
if err != nil {
log.Fatal("Failed to read key file:", err)
}
keyHex := string(keyData)
// Read input text
var plaintext string
if inputFile != "" {
data, err := os.ReadFile(inputFile)
if err != nil {
log.Fatal("Failed to read input file:", err)
}
plaintext = string(data)
} else {
log.Fatal("Input file is required")
}
// Encrypt the text
encrypted, err := encryptText(plaintext, keyHex)
if err != nil {
log.Fatal("Failed to encrypt text:", err)
}
// Save to output file
if outputFile != "" {
err := os.WriteFile(outputFile, []byte(encrypted), 0644)
if err != nil {
log.Fatal("Failed to write output file:", err)
}
fmt.Printf("Text encrypted and saved to: %s\n", outputFile)
} else {
fmt.Printf("Encrypted text: %s\n", encrypted)
}
},
}
func init() {
encryptCmd.Flags().StringP("in", "i", "", "Input file containing text to encrypt (required)")
encryptCmd.Flags().StringP("out", "o", "validator.fck", "Output file for encrypted text")
encryptCmd.Flags().StringP("aes", "k", "aes.key", "File containing the AES key")
encryptCmd.MarkFlagRequired("in")
rootCmd.AddCommand(encryptCmd)
}
package main
import (
"crypto/rand"
"encoding/hex"
"fmt"
"os"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
func generateAESKey() ([]byte, error) {
key := make([]byte, 32) // 32 bytes = 256 bits
_, err := rand.Read(key)
if err != nil {
return nil, err
}
return key, nil
}
var keyCmd = &cobra.Command{
Use: "genkey",
Short: "Generate a random 256-bit AES key",
Long: "Generate a cryptographically secure random 256-bit AES key and output it in hexadecimal format",
Run: func(cmd *cobra.Command, args []string) {
key, err := generateAESKey()
if err != nil {
log.Fatal("Failed to generate AES key:", err)
}
keyHex := hex.EncodeToString(key)
fmt.Printf("Generated 256-bit AES key: %s\n", keyHex)
// Check if output file flag is provided
outputFile, _ := cmd.Flags().GetString("output")
if outputFile != "" {
err := saveKeyToFile(keyHex, outputFile)
if err != nil {
log.Fatal("Failed to save key to file:", err)
}
fmt.Printf("Key saved to: %s\n", outputFile)
}
},
}
func saveKeyToFile(keyHex, filepath string) error {
return os.WriteFile(filepath, []byte(keyHex), 0600)
}
func init() {
keyCmd.Flags().StringP("output", "o", "aes.key", "File path to save the generated AES key")
rootCmd.AddCommand(keyCmd)
}
...@@ -4,12 +4,16 @@ import ( ...@@ -4,12 +4,16 @@ import (
"code.wuban.net.cn/movabridge/token-bridge/chain" "code.wuban.net.cn/movabridge/token-bridge/chain"
"code.wuban.net.cn/movabridge/token-bridge/config" "code.wuban.net.cn/movabridge/token-bridge/config"
"code.wuban.net.cn/movabridge/token-bridge/dao" "code.wuban.net.cn/movabridge/token-bridge/dao"
"flag"
"os" "os"
"os/signal" "os/signal"
"syscall" "syscall"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
var (
confPath string
) )
func init() { func init() {
...@@ -18,44 +22,64 @@ func init() { ...@@ -18,44 +22,64 @@ func init() {
}) })
} }
func main() { var rootCmd = &cobra.Command{
flag.Parse() Use: "validator",
conf, err := config.New() Short: "Token bridge validator service",
if err != nil { Long: "A validator service for the token bridge system that syncs with multiple blockchain networks",
panic(err) Run: func(cmd *cobra.Command, args []string) {
} conf, err := config.New(confPath)
if err != nil {
panic(err)
}
if len(conf.OTPKeyPath) == 0 || len(conf.AesKeyPath) == 0 {
log.Warning("no OTP key or AES key, please make sure you have set them correctly")
log.Warning("the server will runing in sync mode only")
}
d, err := dao.New(conf) d, err := dao.New(conf)
if err != nil { if err != nil {
panic(err) panic(err)
} }
d.Start() d.Start()
if conf.Debug { if conf.Debug {
log.SetLevel(log.DebugLevel) log.SetLevel(log.DebugLevel)
} }
syncers := make([]*chain.ChainSync, 0)
for _, chainConfig := range conf.Chains { syncers := make([]*chain.ChainSync, 0)
syncer := chain.NewChainSync(chainConfig, d)
syncer.Start()
syncers = append(syncers, syncer)
}
// Set up signal handling for _, chainConfig := range conf.Chains {
sigCh := make(chan os.Signal, 1) syncer := chain.NewChainSync(chainConfig, d)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) syncer.Start()
syncers = append(syncers, syncer)
d.AddSyncer(chainConfig.ChainId, syncer)
}
// Wait for termination signal // Set up signal handling
sig := <-sigCh sigCh := make(chan os.Signal, 1)
log.WithField("signal", sig.String()).Info("received termination signal, shutting down") signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
// Stop all chain sync instances // Wait for termination signal
for _, syncer := range syncers { sig := <-sigCh
syncer.Stop() log.WithField("signal", sig.String()).Info("received termination signal, shutting down")
}
d.Stop()
log.Info("graceful shutdown completed") // Stop all chain sync instances
os.Exit(0) for _, syncer := range syncers {
syncer.Stop()
}
d.Stop()
log.Info("graceful shutdown completed")
os.Exit(0)
},
}
func init() {
rootCmd.Flags().StringVarP(&confPath, "config", "c", "config.toml", "config file path")
}
func main() {
if err := rootCmd.Execute(); err != nil {
log.Fatal(err)
}
} }
package main
import (
"code.wuban.net.cn/movabridge/token-bridge/encpk"
"fmt"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"os"
)
var onetimeKeyCmd = &cobra.Command{
Use: "onetime",
Short: "Generate a one-time-key from fpk",
Long: "Generate a one-time-key from fpk",
Run: func(cmd *cobra.Command, args []string) {
// Get flags
inputFile, _ := cmd.Flags().GetString("in")
outputFile, _ := cmd.Flags().GetString("out")
// Read input text
var plaintext string
if inputFile != "" {
data, err := os.ReadFile(inputFile)
if err != nil {
log.Fatal("Failed to read input file:", err)
}
plaintext = string(data)
} else {
log.Fatal("Input file is required")
}
encrypted, err := encpk.EncryptPKWithTimeKey([]byte(plaintext))
if err != nil {
log.Fatal("Failed to encrypt text:", err)
}
// Save to output file
if outputFile != "" {
err := os.WriteFile(outputFile, []byte(encrypted), 0644)
if err != nil {
log.Fatal("Failed to write output file:", err)
}
fmt.Printf("Text encrypted and saved to: %s\n", outputFile)
} else {
fmt.Printf("Encrypted text: %s\n", encrypted)
}
},
}
func init() {
onetimeKeyCmd.Flags().StringP("in", "i", "", "Input file containing text to encrypt (required)")
onetimeKeyCmd.Flags().StringP("out", "o", "validator.otk", "Output file for one-time-key")
onetimeKeyCmd.MarkFlagRequired("in")
rootCmd.AddCommand(onetimeKeyCmd)
}
package config package config
import ( import (
"flag"
"github.com/BurntSushi/toml" "github.com/BurntSushi/toml"
) )
type Config struct { type Config struct {
Debug bool Debug bool
Chains map[string]*ChainConfig `toml:"chains"` AesKeyPath string `toml:"aeskey"`
MySQL MySQLConfig OTPKeyPath string `toml:"otpkey"`
Server ServerConfig Chains map[string]*ChainConfig `toml:"chains"`
MySQL MySQLConfig
Server ServerConfig
} }
type ChainConfig struct { type ChainConfig struct {
Name string `toml:"name"` Name string `toml:"name"`
RPC string `toml:"rpc"` RPC string `toml:"rpc"`
InitialHeight int64 `toml:"initial_height"` InitialHeight int64 `toml:"initial_height"`
BatchBlock int `toml:"batch_block"` BatchBlock int `toml:"batch_block"`
BehindBlock int `toml:"behind_block"` BehindBlock int `toml:"behind_block"`
BridgeContract string `toml:"bridge_contract"` BridgeContract string `toml:"bridge_contract"`
ValidatorPrivateKey string `toml:"validator_private_key"` //ValidatorPrivateKey string `toml:"validator_private_key"`
ChainId int64 `toml:"chain_id"` // Will be populated by code ChainId int64 `toml:"chain_id"` // Will be populated by code
} }
type MySQLConfig struct { type MySQLConfig struct {
...@@ -38,21 +39,19 @@ type ServerConfig struct { ...@@ -38,21 +39,19 @@ type ServerConfig struct {
InvalidHeaders []string `toml:"invalid_headers"` InvalidHeaders []string `toml:"invalid_headers"`
} }
var confPath = flag.String("c", "config.toml", "config file path") func New(confPath string) (*Config, error) {
func New() (*Config, error) {
var cfg Config var cfg Config
cfg.Chains = make(map[string]*ChainConfig) cfg.Chains = make(map[string]*ChainConfig)
// Parse the TOML configuration // Parse the TOML configuration
_, err := toml.DecodeFile(*confPath, &cfg) _, err := toml.DecodeFile(confPath, &cfg)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Process the chains from array to map using name as key // Process the chains from array to map using name as key
var chainArray []ChainConfig var chainArray []ChainConfig
_, err = toml.DecodeFile(*confPath, &struct { _, err = toml.DecodeFile(confPath, &struct {
Chains *[]ChainConfig `toml:"chains"` Chains *[]ChainConfig `toml:"chains"`
*Config *Config
}{ }{
......
...@@ -17,6 +17,7 @@ import ( ...@@ -17,6 +17,7 @@ import (
type ChainInterface interface { type ChainInterface interface {
Name() string Name() string
GetChain() *config.ChainConfig GetChain() *config.ChainConfig
IsSyncing() bool
ParseTransferOut(log types.Log) (*bridge.BridgeContractTransferOut, error) ParseTransferOut(log types.Log) (*bridge.BridgeContractTransferOut, error)
ParseTransferIn(log types.Log) (*bridge.BridgeContractTransferIn, error) ParseTransferIn(log types.Log) (*bridge.BridgeContractTransferIn, error)
ParseTransferInExecution(log types.Log) (*bridge.BridgeContractTransferInExecution, error) ParseTransferInExecution(log types.Log) (*bridge.BridgeContractTransferInExecution, error)
...@@ -265,7 +266,7 @@ func (s *Dao) filterValidatorEvents(chain ChainInterface, txLog types.Log, tx *T ...@@ -265,7 +266,7 @@ func (s *Dao) filterValidatorEvents(chain ChainInterface, txLog types.Log, tx *T
valOp ValidatorOp = TransferChainNoProcess valOp ValidatorOp = TransferChainNoProcess
) )
isMyselfOp := false isMyselfOp := false
valAddr := strings.ToLower(s.GetChainValidatorAddr(chain.GetChain()).Hex()) valAddr := strings.ToLower(s.GetChainValidatorAddr().Hex())
switch txLog.Topics[0].Hex() { switch txLog.Topics[0].Hex() {
case TransferInConfirmationEvent.ID.Hex(): case TransferInConfirmationEvent.ID.Hex():
......
...@@ -2,9 +2,13 @@ package dao ...@@ -2,9 +2,13 @@ package dao
import ( import (
"code.wuban.net.cn/movabridge/token-bridge/config" "code.wuban.net.cn/movabridge/token-bridge/config"
"code.wuban.net.cn/movabridge/token-bridge/encpk"
dbModel "code.wuban.net.cn/movabridge/token-bridge/model/db" dbModel "code.wuban.net.cn/movabridge/token-bridge/model/db"
"context" "context"
"crypto/ecdsa"
"fmt" "fmt"
"github.com/ethereum/go-ethereum/crypto"
log "github.com/sirupsen/logrus"
"sync" "sync"
"time" "time"
...@@ -20,21 +24,37 @@ type ChainInfo struct { ...@@ -20,21 +24,37 @@ type ChainInfo struct {
} }
type Dao struct { type Dao struct {
c *config.Config c *config.Config
db *gorm.DB db *gorm.DB
chainGroup map[int64]ChainInfo chainGroup map[int64]ChainInfo
quit chan struct{} syncer map[int64]ChainInterface
wg sync.WaitGroup quit chan struct{}
handleMux sync.Mutex wg sync.WaitGroup
handleMux sync.Mutex
validatorPk *ecdsa.PrivateKey
} }
func New(_c *config.Config) (dao *Dao, err error) { func New(_c *config.Config) (dao *Dao, err error) {
dao = &Dao{ dao = &Dao{
c: _c, c: _c,
chainGroup: make(map[int64]ChainInfo), chainGroup: make(map[int64]ChainInfo),
syncer: make(map[int64]ChainInterface),
quit: make(chan struct{}), quit: make(chan struct{}),
} }
if len(_c.OTPKeyPath) > 0 && len(_c.AesKeyPath) > 0 {
pkstr, err := encpk.DecryptOTP(_c.AesKeyPath, _c.OTPKeyPath)
if err == nil {
if pk, err := crypto.HexToECDSA(string(pkstr)); err != nil {
log.WithError(err).Error("parse validator private key failed")
} else {
dao.validatorPk = pk
}
} else {
log.WithError(err).Error("decrypt validator otp failed")
}
}
// Connect to all configured chains // Connect to all configured chains
for name, chainConfig := range _c.Chains { for name, chainConfig := range _c.Chains {
var client *ethclient.Client var client *ethclient.Client
...@@ -112,3 +132,19 @@ func (d *Dao) Stop() { ...@@ -112,3 +132,19 @@ func (d *Dao) Stop() {
d.db = nil d.db = nil
d.c = nil d.c = nil
} }
func (d *Dao) AddSyncer(chainId int64, syncer ChainInterface) {
d.handleMux.Lock()
defer d.handleMux.Unlock()
d.syncer[chainId] = syncer
}
func (d *Dao) IsSyncing(chainId int64) bool {
d.handleMux.Lock()
defer d.handleMux.Unlock()
syncer, exists := d.syncer[chainId]
if !exists {
return true
}
return syncer.IsSyncing()
}
...@@ -26,11 +26,12 @@ func (d *Dao) SetStorageHeight(key string, intValue int64) (err error) { ...@@ -26,11 +26,12 @@ func (d *Dao) SetStorageHeight(key string, intValue int64) (err error) {
}).Error }).Error
} }
func (d *Dao) GetUnprocessedBridgeEvents(limit int) (events []*dbModel.BridgeEvent, err error) { func (d *Dao) GetUnprocessedBridgeEvents(limit int, offset int) (events []*dbModel.BridgeEvent, err error) {
err = d.db.Model(&dbModel.BridgeEvent{}). err = d.db.Model(&dbModel.BridgeEvent{}).
Where("`to_chain_status` < ? AND `validator_status` = ?", 2, 0). Where("`from_chain_tx_hash` != '' AND `to_chain_status` < ? AND `validator_status` = ?", 2, 0).
Order("created_at ASC"). Order("created_at ASC").
Limit(limit). Limit(limit).
Offset(offset).
Find(&events).Error Find(&events).Error
if err == gorm.ErrRecordNotFound { if err == gorm.ErrRecordNotFound {
return nil, ErrRecordNotFound return nil, ErrRecordNotFound
......
...@@ -55,7 +55,7 @@ func (d *Dao) GetLatestBockHash(chain *config.ChainConfig) (hash string, err err ...@@ -55,7 +55,7 @@ func (d *Dao) GetLatestBockHash(chain *config.ChainConfig) (hash string, err err
return block.Hash().Hex(), nil return block.Hash().Hex(), nil
} }
func (d *Dao) GetBlockTime(chain *config.ChainConfig, height int) (timestamp int, err error) { func (d *Dao) GetBlockTime(chain *config.ChainConfig, height int64) (timestamp int64, err error) {
chainInfo, ok := d.chainGroup[chain.ChainId] chainInfo, ok := d.chainGroup[chain.ChainId]
if !ok { if !ok {
return 0, errors.New("chain client not support") return 0, errors.New("chain client not support")
...@@ -66,7 +66,7 @@ func (d *Dao) GetBlockTime(chain *config.ChainConfig, height int) (timestamp int ...@@ -66,7 +66,7 @@ func (d *Dao) GetBlockTime(chain *config.ChainConfig, height int) (timestamp int
block, err := chainInfo.cli.BlockByNumber(ctx, big.NewInt(int64(height))) block, err := chainInfo.cli.BlockByNumber(ctx, big.NewInt(int64(height)))
if err == nil { if err == nil {
return int(block.Time()), nil return int64(block.Time()), nil
} }
} }
return return
...@@ -167,22 +167,14 @@ func (d *Dao) SubmitInTransfer(event *dbModel.BridgeEvent) error { ...@@ -167,22 +167,14 @@ func (d *Dao) SubmitInTransfer(event *dbModel.BridgeEvent) error {
if err != nil { if err != nil {
return err return err
} }
k := chain.conf.ValidatorPrivateKey
if k == "" {
log.WithField("chain", chain.conf.Name).Warn("validator private key is empty, skip submit in transfer")
return nil
}
signPrivateKey, err := crypto.HexToECDSA(common.Bytes2Hex(common.FromHex(k))) signPrivateKey := d.validatorPk
if err != nil {
log.WithField("chainId", chain.conf.ChainId).WithError(err).Error("failed to parse private key")
return err
}
opts, err := bind.NewKeyedTransactorWithChainID(signPrivateKey, big.NewInt(int64(chain.conf.ChainId))) opts, err := bind.NewKeyedTransactorWithChainID(signPrivateKey, big.NewInt(int64(chain.conf.ChainId)))
if err != nil { if err != nil {
log.WithField("chainId", chain.conf.ChainId).WithError(err).Error("new keyed transfer failed") log.WithField("chainId", chain.conf.ChainId).WithError(err).Error("new keyed transfer failed")
return err return err
} }
opts.GasLimit = uint64(1000000)
param, err := d.buildParam(event) param, err := d.buildParam(event)
if err != nil { if err != nil {
log.WithField("chainId", chain.conf.ChainId).WithError(err).Error("build param failed") log.WithField("chainId", chain.conf.ChainId).WithError(err).Error("build param failed")
...@@ -193,8 +185,22 @@ func (d *Dao) SubmitInTransfer(event *dbModel.BridgeEvent) error { ...@@ -193,8 +185,22 @@ func (d *Dao) SubmitInTransfer(event *dbModel.BridgeEvent) error {
log.WithField("chainId", chain.conf.ChainId).WithError(err).Error("failed to submit in transfer") log.WithField("chainId", chain.conf.ChainId).WithError(err).Error("failed to submit in transfer")
return err return err
} else { } else {
// wait tx result and update validator status.
receipt, err := bind.WaitMined(context.Background(), chain.cli, tx)
if err != nil {
log.WithField("chainId", chain.conf.ChainId).WithError(err).Error("wait tx mined failed")
return err
}
if receipt.Status != types.ReceiptStatusSuccessful {
log.WithField("chainId", chain.conf.ChainId).WithField("tx", tx.Hash().Hex()).Error("tx failed")
return errors.New("tx failed")
}
// update validator status. // update validator status.
log.WithField("chainId", chain.conf.ChainId).Infof("submit in transfer tx hash: %s", tx.Hash().Hex()) log.WithFields(log.Fields{
"tx": tx.Hash().Hex(),
"chainId": chain.conf.ChainId,
"validator": d.GetChainValidatorAddr(),
}).Debug("submit in transfer tx succeed")
return d.UpdateBridgeValidatorOperation(event, constant.ValidatorStatusConfirmation) return d.UpdateBridgeValidatorOperation(event, constant.ValidatorStatusConfirmation)
} }
} }
...@@ -210,6 +216,7 @@ func (d *Dao) HandleTasks() { ...@@ -210,6 +216,7 @@ func (d *Dao) HandleTasks() {
ticker := time.NewTicker(5 * time.Second) ticker := time.NewTicker(5 * time.Second)
maxCount := 5 maxCount := 5
offset := 0
defer ticker.Stop() defer ticker.Stop()
for { for {
...@@ -218,22 +225,34 @@ func (d *Dao) HandleTasks() { ...@@ -218,22 +225,34 @@ func (d *Dao) HandleTasks() {
log.Info("stopping Dao task.") log.Info("stopping Dao task.")
return return
case <-ticker.C: case <-ticker.C:
if d.validatorPk == nil {
continue
}
// select unprocessed bridge event with maxCount. // select unprocessed bridge event with maxCount.
events, err := d.GetUnprocessedBridgeEvents(maxCount) events, err := d.GetUnprocessedBridgeEvents(maxCount, offset)
if err != nil { if err != nil {
log.WithError(err).Error("failed to get unprocessed bridge events") log.WithError(err).Error("failed to get unprocessed bridge events")
continue continue
} }
if len(events) == 0 { if len(events) == 0 {
offset = 0
log.Info("no unprocessed bridge events found") log.Info("no unprocessed bridge events found")
continue continue
} }
log.Debugf("found %d unprocessed bridge events", len(events)) log.Debugf("found %d unprocessed bridge events", len(events))
offset += len(events)
for _, event := range events { for _, event := range events {
//log.WithFields(log.Fields{ //log.WithFields(log.Fields{
// "fromChain": event.FromChain, // "fromChain": event.FromChain,
// "txhash": event.FromChainTxHash, // "txhash": event.FromChainTxHash,
//}).Info("processing bridge event") //}).Info("processing bridge event")
if d.IsSyncing(event.ToChain) {
log.WithFields(log.Fields{
"toChain": event.ToChain,
}).Debug("target chain is syncing, skip do task")
continue
}
if err := d.SubmitInTransfer(event); err != nil { if err := d.SubmitInTransfer(event); err != nil {
log.WithError(err).WithFields(log.Fields{ log.WithError(err).WithFields(log.Fields{
...@@ -251,11 +270,7 @@ func (d *Dao) HandleTasks() { ...@@ -251,11 +270,7 @@ func (d *Dao) HandleTasks() {
} }
} }
func (d *Dao) GetChainValidatorAddr(chain *config.ChainConfig) common.Address { func (d *Dao) GetChainValidatorAddr() common.Address {
k := chain.ValidatorPrivateKey k := d.validatorPk
private, err := crypto.ToECDSA(common.FromHex(k)) return crypto.PubkeyToAddress(k.PublicKey)
if err != nil {
return common.Address{}
}
return crypto.PubkeyToAddress(private.PublicKey)
} }
...@@ -27,6 +27,8 @@ services: ...@@ -27,6 +27,8 @@ services:
bridgedb: bridgedb:
condition: service_healthy condition: service_healthy
volumes: volumes:
- ./aes.key:/app/aes.key
- ./val.otp:/app/val.otp
- ./config.toml:/app/config.toml - ./config.toml:/app/config.toml
command: command:
- "/bin/sh" - "/bin/sh"
......
package encpk
import (
"crypto/aes"
"crypto/cipher"
"crypto/sha256"
"encoding/binary"
"encoding/hex"
"errors"
"os"
"time"
)
const (
TimeWindowSeconds = 60 // 1分钟时间窗口
InitialSeed = "uEj7AgDNCREwsvnTaCEtzDXt0I5eFDl8"
)
// GenerateTimeBasedKey 根据时间窗口和种子生成AES密钥
func GenerateTimeBasedKey(seed string, timestamp time.Time) []byte {
// 将时间戳转换为分钟级别的时间窗口
timeWindow := timestamp.Unix() / TimeWindowSeconds
// 创建哈希输入:种子 + 时间窗口
hash := sha256.New()
hash.Write([]byte(seed))
timeBytes := make([]byte, 8)
binary.BigEndian.PutUint64(timeBytes, uint64(timeWindow))
hash.Write(timeBytes)
// 返回32字节的AES-256密钥
return hash.Sum(nil)
}
// EncryptPKWithTimeKey 使用时间密钥加密私钥
func EncryptPKWithTimeKey(pk []byte) ([]byte, error) {
if len(pk) == 0 {
return nil, errors.New("private key cannot be empty")
}
var seed = InitialSeed
// 生成当前时间的密钥
key := GenerateTimeBasedKey(seed, time.Now())
// 创建AES密码块
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
// 创建GCM模式
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
// 生成随机nonce
nonce := make([]byte, gcm.NonceSize())
// 这里应该使用crypto/rand生成随机nonce,为简化示例使用固定值
// 加密数据
ciphertext := gcm.Seal(nonce, nonce, pk, nil)
return ciphertext, nil
}
// DecryptPKWithTimeKey 使用时间密钥解密私钥
func DecryptPKWithTimeKey(encryptedPK []byte) ([]byte, error) {
if len(encryptedPK) == 0 {
return nil, errors.New("encrypted private key cannot be empty")
}
var seed = InitialSeed
now := time.Now()
// 尝试当前时间窗口的密钥
if plaintext, err := tryDecryptWithTime(encryptedPK, seed, now); err == nil {
return plaintext, nil
}
// 尝试前一个时间窗口的密钥(处理边界情况)
prevTime := now.Add(-time.Duration(TimeWindowSeconds) * time.Second)
if plaintext, err := tryDecryptWithTime(encryptedPK, seed, prevTime); err == nil {
return plaintext, nil
}
return nil, errors.New("decryption failed: data may be expired or corrupted")
}
// tryDecryptWithTime 尝试使用特定时间的密钥解密
func tryDecryptWithTime(encryptedPK []byte, seed string, timestamp time.Time) ([]byte, error) {
key := GenerateTimeBasedKey(seed, timestamp)
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
nonceSize := gcm.NonceSize()
if len(encryptedPK) < nonceSize {
return nil, errors.New("encrypted data too short")
}
nonce := encryptedPK[:nonceSize]
ciphertext := encryptedPK[nonceSize:]
return gcm.Open(nil, nonce, ciphertext, nil)
}
func DecryptOTPWithAESKey(key []byte, otp []byte) ([]byte, error) {
if len(key) == 0 {
return nil, errors.New("key cannot be empty")
}
encryptedPK, err := DecryptPKWithTimeKey(otp)
if err != nil {
return nil, err
}
fpk, _ := hex.DecodeString(string(encryptedPK))
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
nonceSize := gcm.NonceSize()
if len(encryptedPK) < nonceSize {
return nil, errors.New("encrypted data too short")
}
nonce := fpk[:nonceSize]
ciphertext := fpk[nonceSize:]
return gcm.Open(nil, nonce, ciphertext, nil)
}
func DecryptOTP(aesPath string, otpPath string) ([]byte, error) {
aeskey, err := os.ReadFile(aesPath)
if err != nil {
return nil, errors.New("Failed to read AES key file:" + err.Error())
}
aeskeyData, _ := hex.DecodeString(string(aeskey))
otpkey, err := os.ReadFile(otpPath)
if err != nil {
return nil, errors.New("Failed to read OTP file:" + err.Error())
}
return DecryptOTPWithAESKey(aeskeyData, otpkey)
}
package encpk
import (
"encoding/hex"
"fmt"
"os"
"testing"
)
func TestDecryptOTP(t *testing.T) {
otpkey, err := os.ReadFile("../val.otp")
if err != nil {
t.Fatal("Failed to read key file:", err)
}
aeskey, err := os.ReadFile("../aes.key")
if err != nil {
t.Fatal("Failed to read AES key file:", err)
}
aeskeyData, _ := hex.DecodeString(string(aeskey))
deckey, err := DecryptOTPWithAESKey(aeskeyData, otpkey)
if err != nil {
t.Fatal("Failed to decrypt key:", err)
}
fmt.Println("Decrypted key:", string(deckey))
}
//
//func TestEncryptAndDecrypt(t *testing.T) {
// enckey, err := os.ReadFile("../val.otp")
// if err != nil {
// t.Fatal("Failed to read key file:", err)
// }
//
// deckey, err := DecryptPKWithTimeKey(enckey)
// if err != nil {
// t.Fatal("Failed to decrypt key:", err)
// }
// fmt.Println("Decrypted key:", string(deckey))
//}
...@@ -6,11 +6,9 @@ require ( ...@@ -6,11 +6,9 @@ require (
github.com/BurntSushi/toml v1.5.0 github.com/BurntSushi/toml v1.5.0
github.com/btcsuite/btcutil v1.0.2 github.com/btcsuite/btcutil v1.0.2
github.com/ethereum/go-ethereum v1.16.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/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 github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.8.1
golang.org/x/crypto v0.41.0 golang.org/x/crypto v0.41.0
gorm.io/driver/mysql v1.6.0 gorm.io/driver/mysql v1.6.0
gorm.io/gorm v1.30.1 gorm.io/gorm v1.30.1
...@@ -41,8 +39,10 @@ require ( ...@@ -41,8 +39,10 @@ require (
github.com/go-playground/validator/v10 v10.26.0 // indirect github.com/go-playground/validator/v10 v10.26.0 // indirect
github.com/go-sql-driver/mysql v1.8.1 // indirect github.com/go-sql-driver/mysql v1.8.1 // indirect
github.com/goccy/go-json v0.10.5 // indirect github.com/goccy/go-json v0.10.5 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.4.2 // indirect github.com/gorilla/websocket v1.4.2 // indirect
github.com/holiman/uint256 v1.3.2 // indirect github.com/holiman/uint256 v1.3.2 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
...@@ -53,6 +53,7 @@ require ( ...@@ -53,6 +53,7 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // 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/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/supranational/blst v0.3.14 // indirect github.com/supranational/blst v0.3.14 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect github.com/tklauser/numcpus v0.6.1 // indirect
......
...@@ -52,6 +52,7 @@ github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAK ...@@ -52,6 +52,7 @@ github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAK
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= 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 h1:vIye/FqI50VeAr0B3dx+YjeIvmc3LWz4yEfbWBpTUf0=
github.com/consensys/gnark-crypto v0.18.0/go.mod h1:L3mXGFTe1ZN+RSJ+CLjUt9x7PNdx8ubaYfDROyp2Z8c= github.com/consensys/gnark-crypto v0.18.0/go.mod h1:L3mXGFTe1ZN+RSJ+CLjUt9x7PNdx8ubaYfDROyp2Z8c=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= 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/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 h1:05GrhASN9kDAidaFJOda6A4BEvgvuXbazXg/0E3OOdI=
...@@ -89,8 +90,6 @@ github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqG ...@@ -89,8 +90,6 @@ github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqG
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= 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/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 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= 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 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
...@@ -143,6 +142,8 @@ github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXei ...@@ -143,6 +142,8 @@ github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXei
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 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 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/influxdata/influxdb-client-go/v2 v2.4.0 h1:HGBfZYStlx3Kqvsv1h2pJixbCl/jhnFtxpKFAv9Tu5k= 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/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 h1:qSHzRbhzK8RdXOsAdfDgO49TtqC1oZ+acxPrkfTxcCs=
...@@ -238,10 +239,13 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf ...@@ -238,10 +239,13 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 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 h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU=
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= 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 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
......
...@@ -3,4 +3,27 @@ ...@@ -3,4 +3,27 @@
2. modify config.yml for chain and mysql password. 2. modify config.yml for chain and mysql password.
3. modify docker-compose.yml for mysql password. 3. modify docker-compose.yml for mysql password.
4. docker compose up -d 4. docker compose up -d
5. docker compose down 5. docker compose down
\ No newline at end of file
# How to use
1. make docker
2. prepare a validator private key, write it to `val.pk`
3. prepare a aes key.
```
docker run -it -v "${PWD}:/app" --rm token-bridge:latest validator genkey --output aes.key
```
4. crypt the private key with aes key, named it as validator.fpk
```
docker run -it -v "${PWD}:/app" --rm token-bridge:latest validator encrypt --in val.pk --out val.fpk --aes aes.key
```
5. generate one-time-key for the validator.
```
docker run -it -v "${PWD}:/app" --rm token-bridge:latest validator onetime --in val.fpk --out val.otp
```
6. keep the `val.pk` safe and delete `val.pk`, `val.fpk` from the server. Must keep `aes.key` and `val.otp` exist.
7. start the validator service.
```
docker compose up -d
```
\ No newline at end of file
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