package eth import ( "context" "fmt" "log" "m2pool-payment/internal/db" "m2pool-payment/internal/listen" message "m2pool-payment/internal/msg" "math/big" "sync" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethclient" ) type ETHNode struct { decodeKey string // 消息解密密钥,用于解密RMQ消息的Sign NetID *big.Int Config message.ETHConfig ConfirmHeight uint64 WsClient *ethclient.Client RpcClient *ethclient.Client MysqlDB *db.MySQLPool SqliteDB *db.SQLite USDT *USDT NetInfo *NetInfo // 网络通用状态(当前高度、gas费用等) // Messages map[string]any // {"queue_id": message.Topup{}, "queue_id": message.Withdraw{}, ...} MessageServer *listen.ListenServer UnConfirmedTxs *UnConfirmedTxs Wallets map[string]*Wallets // {"address" : &Wallets{}, ...} LogsChan chan *types.Header Ctx context.Context Cancel context.CancelFunc mu sync.Mutex } type USDT struct { Address common.Address // USDT合约地址 ListeningAddresses map[string]any // 监听的USDT转账消息 ABI abi.ABI // USDT ABI TransferSig common.Hash // USDT函数签名 LogsChan chan types.Log } type UnConfirmedTxs struct { mu sync.Mutex Transactions map[string]message.Transaction // {"queue_id": message.Transactions{}, ...} } type NetInfo struct { mu sync.Mutex Height uint64 GasLimit uint64 GasTipCap *big.Int GasFeeCap *big.Int GasPrice *big.Int // 老版本转账使用的gas } type Wallets struct { address string queueId string pk string eth_balance *eth_balance usdt_balance *usdt_balance timestamp uint64 sign string status int mu sync.Mutex } type eth_balance struct { symbol string // 默认ETH used_gas *big.Int // 1 ETH = 1e18 WEI balance *big.Int // 实际拥有的ETH余额 successed_tx_hash []string failed_tx_hash []string } type usdt_balance struct { symbol string freeze_num *big.Int // 1 USDT = 1e6 WEI balance *big.Int // 实际拥有的USDT余额 successed_tx_hash []string failed_tx_hash []string } func NewETHNode(cfg message.Config, decodeKey string, l *listen.ListenServer) (*ETHNode, error) { // 连入ETH节点的ws ws_client, err := ethclient.Dial(cfg.ETHConfig.WsUrl) if err != nil { return nil, fmt.Errorf("failed to connect to Ethereum node: %w", err) } // 连入ETH节点的rpc rpc_client, err := ethclient.Dial(cfg.ETHConfig.RpcUrl) if err != nil { return nil, fmt.Errorf("failed to connect to Ethereum node rpc: %w", err) } // 创建可取消的 context ctx, cancel := context.WithCancel(context.Background()) // 获得net_id netId, err := rpc_client.NetworkID(ctx) if err != nil { cancel() return nil, fmt.Errorf("failed to connect to get node net_id: %w", err) } // 初始化MySQL数据库 dbConn, err := db.NewMySQLPool(cfg.MysqlConfig["wallet"]) if err != nil { cancel() return nil, fmt.Errorf("mysql connect error: %w", err) } // 初始化SQLite3 sqlite, err := db.NewSQLite(cfg.ETHConfig.SqlitePath) if err != nil { cancel() return nil, fmt.Errorf("sqlite3 connect error: %w", err) } logchan := make(chan *types.Header) // 初始化USDT usdt := init_USDT() ethnode := ÐNode{ decodeKey: decodeKey, NetID: netId, Config: cfg.ETHConfig, ConfirmHeight: cfg.ETHConfig.ConfirmHeight, WsClient: ws_client, RpcClient: rpc_client, USDT: usdt, MysqlDB: dbConn, SqliteDB: sqlite, NetInfo: &NetInfo{}, UnConfirmedTxs: &UnConfirmedTxs{ Transactions: make(map[string]message.Transaction), }, Wallets: make(map[string]*Wallets), MessageServer: l, LogsChan: logchan, Ctx: ctx, Cancel: cancel, } // 初始化表 // err = ethnode.MysqlDB.ExecuteSQLFile("../public/eth_mysql.sql") // if err != nil { // log.Fatalf("ETH-Chain初始化数据库表失败:%v", err) // } // 更新网络公共数据和加载钱包 height, err := ethnode.getHeight() if err != nil { return nil, fmt.Errorf("failed to get blockHeight: %v", err) } // 更新网络公共数据 ethnode.updateNetInfo(height) // 加载钱包 if err := ethnode.loadWallets(); err != nil { return nil, fmt.Errorf("[inital eth wallets]: %v", err) } // 加载未确认交易 if err := ethnode.loadUnConfirmedTxs(); err != nil { return nil, fmt.Errorf("load unconfirmtxs error: %v", err) } log.Println("✅ ETH节点已启动") // ethnode.handleHistoryETHEvent(23795552) return ethnode, nil } // 转账 func (e *ETHNode) Transfer(from, to, symbol string, amount, fee float64) error { // 执行转账 err := e.handleTx(symbol, from, to, amount) if err != nil { return fmt.Errorf("%s-tranfer error: %v", symbol, err) } return nil } // 监听消息 func (e *ETHNode) ListenMsg() { log.Printf("✅ 开始监听msg...") if e.MessageServer.ChToChainServer["ETH"] == nil { log.Printf("ETH消息通道还未建立") return } for msg := range e.MessageServer.ChToChainServer["ETH"] { switch v := msg.(type) { case message.TopupMsg_req: go e.handleListen_Topup_req(v) case message.WithdrawMsg_req: go e.handleListen_Withdraw_req(v) case message.PayMsg_req: go e.handleListen_Pay_req(v) case message.RemoveListenMsg_req: go e.handleListen_Remove_req(v) default: log.Printf("ListenMsg error: %v", msg) } } } // 监听区块链数据 func (e *ETHNode) Listen() error { log.Println("✅ 开始监听 ETH 和 USDT 转账事件...") go func() { err := e.listenETHTransactions() if err != nil { log.Printf("Listen ETH Transactions Error: %v", err) } }() go func() { err := e.listenUSDTTransactions() if err != nil { log.Printf("Listen USDT Transactions Error: %v", err) } }() return nil } func (e *ETHNode) Stop() { if e.Cancel != nil { e.Cancel() } log.Println("🛑 停止监听...") }