Commit 0e4a7fa6 authored by vicotor's avatar vicotor

update code

parent 6363dce5
......@@ -11,7 +11,7 @@ WORKDIR /build
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
......
.PHONY: default all clean dev
GOBIN = $(shell pwd)/build/bin
BUILD_FLAGS = -ldflags "-s -w"
default: all
......
......@@ -23,6 +23,7 @@ type ChainSync struct {
quit chan struct{}
stopOnce sync.Once
wg sync.WaitGroup
syncmode bool
}
func (s *ChainSync) ParseTransferOut(log types.Log) (*bridge.BridgeContractTransferOut, error) {
......@@ -57,6 +58,7 @@ func NewChainSync(_chain *config.ChainConfig, _d *dao.Dao) (sync *ChainSync) {
heightKey: fmt.Sprintf("%d_%s", _chain.ChainId, "height"),
bridgeCa: bridgeCa,
quit: make(chan struct{}),
syncmode: true,
}
return sync
......@@ -97,10 +99,21 @@ func (s *ChainSync) loop() {
log.WithField("chain", s.name).WithError(err).Error("get latest block height")
continue
}
if latestHeight <= beginHeight {
s.syncmode = false
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 {
endHeight = latestHeight
}
......@@ -177,3 +190,7 @@ func (s *ChainSync) SyncLogs(beginHeight, endHeight int64) error {
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 (
"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"
"github.com/spf13/cobra"
)
var (
confPath string
)
func init() {
......@@ -18,12 +22,19 @@ func init() {
})
}
func main() {
flag.Parse()
conf, err := config.New()
var rootCmd = &cobra.Command{
Use: "validator",
Short: "Token bridge validator service",
Long: "A validator service for the token bridge system that syncs with multiple blockchain networks",
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)
if err != nil {
......@@ -34,12 +45,14 @@ func main() {
if conf.Debug {
log.SetLevel(log.DebugLevel)
}
syncers := make([]*chain.ChainSync, 0)
for _, chainConfig := range conf.Chains {
syncer := chain.NewChainSync(chainConfig, d)
syncer.Start()
syncers = append(syncers, syncer)
d.AddSyncer(chainConfig.ChainId, syncer)
}
// Set up signal handling
......@@ -58,4 +71,15 @@ func main() {
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
import (
"flag"
"github.com/BurntSushi/toml"
)
type Config struct {
Debug bool
AesKeyPath string `toml:"aeskey"`
OTPKeyPath string `toml:"otpkey"`
Chains map[string]*ChainConfig `toml:"chains"`
MySQL MySQLConfig
Server ServerConfig
......@@ -19,7 +20,7 @@ type ChainConfig struct {
BatchBlock int `toml:"batch_block"`
BehindBlock int `toml:"behind_block"`
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
}
......@@ -38,21 +39,19 @@ type ServerConfig struct {
InvalidHeaders []string `toml:"invalid_headers"`
}
var confPath = flag.String("c", "config.toml", "config file path")
func New() (*Config, error) {
func New(confPath string) (*Config, error) {
var cfg Config
cfg.Chains = make(map[string]*ChainConfig)
// Parse the TOML configuration
_, err := toml.DecodeFile(*confPath, &cfg)
_, 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 {
_, err = toml.DecodeFile(confPath, &struct {
Chains *[]ChainConfig `toml:"chains"`
*Config
}{
......
......@@ -17,6 +17,7 @@ import (
type ChainInterface interface {
Name() string
GetChain() *config.ChainConfig
IsSyncing() bool
ParseTransferOut(log types.Log) (*bridge.BridgeContractTransferOut, error)
ParseTransferIn(log types.Log) (*bridge.BridgeContractTransferIn, error)
ParseTransferInExecution(log types.Log) (*bridge.BridgeContractTransferInExecution, error)
......@@ -265,7 +266,7 @@ func (s *Dao) filterValidatorEvents(chain ChainInterface, txLog types.Log, tx *T
valOp ValidatorOp = TransferChainNoProcess
)
isMyselfOp := false
valAddr := strings.ToLower(s.GetChainValidatorAddr(chain.GetChain()).Hex())
valAddr := strings.ToLower(s.GetChainValidatorAddr().Hex())
switch txLog.Topics[0].Hex() {
case TransferInConfirmationEvent.ID.Hex():
......
......@@ -2,9 +2,13 @@ package dao
import (
"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"
"context"
"crypto/ecdsa"
"fmt"
"github.com/ethereum/go-ethereum/crypto"
log "github.com/sirupsen/logrus"
"sync"
"time"
......@@ -23,18 +27,34 @@ type Dao struct {
c *config.Config
db *gorm.DB
chainGroup map[int64]ChainInfo
syncer map[int64]ChainInterface
quit chan struct{}
wg sync.WaitGroup
handleMux sync.Mutex
validatorPk *ecdsa.PrivateKey
}
func New(_c *config.Config) (dao *Dao, err error) {
dao = &Dao{
c: _c,
chainGroup: make(map[int64]ChainInfo),
syncer: make(map[int64]ChainInterface),
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
for name, chainConfig := range _c.Chains {
var client *ethclient.Client
......@@ -112,3 +132,19 @@ func (d *Dao) Stop() {
d.db = 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) {
}).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{}).
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").
Limit(limit).
Offset(offset).
Find(&events).Error
if err == gorm.ErrRecordNotFound {
return nil, ErrRecordNotFound
......
......@@ -55,7 +55,7 @@ func (d *Dao) GetLatestBockHash(chain *config.ChainConfig) (hash string, err err
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]
if !ok {
return 0, errors.New("chain client not support")
......@@ -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)))
if err == nil {
return int(block.Time()), nil
return int64(block.Time()), nil
}
}
return
......@@ -167,22 +167,14 @@ func (d *Dao) SubmitInTransfer(event *dbModel.BridgeEvent) error {
if err != nil {
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)))
if err != nil {
log.WithField("chainId", chain.conf.ChainId).WithError(err).Error("failed to parse private key")
return err
}
signPrivateKey := d.validatorPk
opts, err := bind.NewKeyedTransactorWithChainID(signPrivateKey, big.NewInt(int64(chain.conf.ChainId)))
if err != nil {
log.WithField("chainId", chain.conf.ChainId).WithError(err).Error("new keyed transfer failed")
return err
}
opts.GasLimit = uint64(1000000)
param, err := d.buildParam(event)
if err != nil {
log.WithField("chainId", chain.conf.ChainId).WithError(err).Error("build param failed")
......@@ -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")
return err
} 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.
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)
}
}
......@@ -210,6 +216,7 @@ func (d *Dao) HandleTasks() {
ticker := time.NewTicker(5 * time.Second)
maxCount := 5
offset := 0
defer ticker.Stop()
for {
......@@ -218,22 +225,34 @@ func (d *Dao) HandleTasks() {
log.Info("stopping Dao task.")
return
case <-ticker.C:
if d.validatorPk == nil {
continue
}
// select unprocessed bridge event with maxCount.
events, err := d.GetUnprocessedBridgeEvents(maxCount)
events, err := d.GetUnprocessedBridgeEvents(maxCount, offset)
if err != nil {
log.WithError(err).Error("failed to get unprocessed bridge events")
continue
}
if len(events) == 0 {
offset = 0
log.Info("no unprocessed bridge events found")
continue
}
log.Debugf("found %d unprocessed bridge events", len(events))
offset += len(events)
for _, event := range events {
//log.WithFields(log.Fields{
// "fromChain": event.FromChain,
// "txhash": event.FromChainTxHash,
//}).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 {
log.WithError(err).WithFields(log.Fields{
......@@ -251,11 +270,7 @@ func (d *Dao) HandleTasks() {
}
}
func (d *Dao) GetChainValidatorAddr(chain *config.ChainConfig) common.Address {
k := chain.ValidatorPrivateKey
private, err := crypto.ToECDSA(common.FromHex(k))
if err != nil {
return common.Address{}
}
return crypto.PubkeyToAddress(private.PublicKey)
func (d *Dao) GetChainValidatorAddr() common.Address {
k := d.validatorPk
return crypto.PubkeyToAddress(k.PublicKey)
}
......@@ -27,6 +27,8 @@ services:
bridgedb:
condition: service_healthy
volumes:
- ./aes.key:/app/aes.key
- ./val.otp:/app/val.otp
- ./config.toml:/app/config.toml
command:
- "/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 (
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
github.com/spf13/cobra v1.8.1
golang.org/x/crypto v0.41.0
gorm.io/driver/mysql v1.6.0
gorm.io/gorm v1.30.1
......@@ -41,8 +39,10 @@ require (
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/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.4.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/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect
......@@ -53,6 +53,7 @@ require (
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/spf13/pflag v1.0.6 // 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
......
......@@ -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/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.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/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
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
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=
......@@ -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/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
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/go.mod h1:vLNHdxTJkIf2mSLvGrpj8TCcISApPoXkaxP8g9uRlW8=
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
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/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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
......
......@@ -4,3 +4,26 @@
3. modify docker-compose.yml for mysql password.
4. docker compose up -d
5. docker compose down
# 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