package eth import ( "context" "fmt" "log" "m2pool-payment/internal/db" message "m2pool-payment/internal/msg" "m2pool-payment/internal/utils" "math/big" "strings" "sync" "time" "github.com/ethereum/go-ethereum" "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/crypto" "github.com/ethereum/go-ethereum/ethclient" ) const erc20ABI = ` [ { "constant": true, "inputs": [{"name": "_owner", "type": "address"}], "name": "balanceOf", "outputs": [{"name": "balance", "type": "uint256"}], "type": "function" }, { "constant": false, "inputs": [ {"name": "_to", "type": "address"}, {"name": "_value", "type": "uint256"} ], "name": "transfer", "outputs": [{"name": "", "type": "bool"}], "type": "function" }, { "anonymous": false, "inputs": [ {"indexed": true, "name": "from", "type": "address"}, {"indexed": true, "name": "to", "type": "address"}, {"indexed": false,"name": "value","type": "uint256"} ], "name": "Transfer", "type": "event" } ] ` // USDT 合约地址(主网) const USDTAddress = "0xdAC17F958D2ee523a2206206994597C13D831ec7" // 状态码常量 const ( STATUS_FAILED = 0 // 失败 STATUS_SUCCESS = 1 // 成功 STATUS_PENDING = 2 // 待确认 STATUS_VERIFY_FAILED = 3 // 验证失败 ) type ETHNode struct { decodeKey string // 私钥解密密钥,仅针对普通钱包 NetID *big.Int // 网络ID,主网为1,其他ID可通过rpc.NetworkID方法获取 Config message.ETHConfig ConfirmHeight uint64 WsClient *ethclient.Client RpcClient *ethclient.Client Db db.MySQLPool // {"address": message.TopupMsg_req{}, ...},仅针对充值 TopupMsg map[string]*message.TopupMsg_req // {"fromAddress": message.WithdrawMsg_req{}, ...},仅针对提现 WithdrawMsg map[string]*message.WithdrawMsg_req // {"fromAddress": message.PayMsg_req{}, ...}, 仅针对支付 PayMsg map[string]*message.PayMsg_req // {"tx_hash": message.Transaction} UnConfirmTxs map[string]*message.Transaction LogsChan chan *types.Header USDT *USDT RealData *RealData 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 RealData struct { mu sync.Mutex Heihgt uint64 GasLimit uint64 GasTipCap *big.Int GasFeeCap *big.Int GasPrice *big.Int // 老版本转账使用的gas } func NewETHNode(cfg message.ETHConfig, decodeKey string) (*ETHNode, error) { // 连入ETH节点的ws ws_client, err := ethclient.Dial(cfg.WsURL) if err != nil { return nil, fmt.Errorf("failed to connect to Ethereum node: %w", err) } // 连入ETH节点的rpc rpc_client, err := ethclient.Dial(cfg.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) } // 构造USDT合约相关 usdt := &USDT{} usdt.Address = common.HexToAddress("0xdAC17F958D2ee523a2206206994597C13D831ec7") // 解析合约地址 usdt.ABI = func() abi.ABI { a, _ := abi.JSON(strings.NewReader(erc20ABI)); return a }() // 解析合约ABI usdt.TransferSig = crypto.Keccak256Hash([]byte("Transfer(address,address,uint256)")) // 解析合约transfer函数签名 usdt.LogsChan = make(chan types.Log, 1000) // 初始化合约日志通道 usdt.ListeningAddresses = make(map[string]any) // 初始化数据库 dbConn, err := db.NewMySQLPool(cfg.DbConfig) if err != nil { cancel() return nil, fmt.Errorf("mysql connect error: %w", err) } // 初始化结构 topup := make(map[string]*message.TopupMsg_req) withdraw := make(map[string]*message.WithdrawMsg_req) pay := make(map[string]*message.PayMsg_req) unConfirmTxs := make(map[string]*message.Transaction) logsChan := make(chan *types.Header, 1000) // 启动时初始化实时数据 realData := &RealData{} return ÐNode{ decodeKey: decodeKey, NetID: netId, Config: cfg, ConfirmHeight: cfg.ConfirmHeight, WsClient: ws_client, RpcClient: rpc_client, Db: *dbConn, TopupMsg: topup, WithdrawMsg: withdraw, PayMsg: pay, UnConfirmTxs: unConfirmTxs, LogsChan: logsChan, USDT: usdt, RealData: realData, Ctx: ctx, Cancel: cancel, }, nil } // ============================ 抽象接口 ============================ func (e *ETHNode) AddAddress(msg any) error { switch v := msg.(type) { case message.TopupMsg_req: e.mu.Lock() addr := strings.ToLower(v.Address) e.TopupMsg[addr] = &v e.mu.Unlock() case message.WithdrawMsg_req: e.mu.Lock() addr := strings.ToLower(v.ToAddress) e.WithdrawMsg[addr] = &v e.mu.Unlock() case message.PayMsg_req: e.mu.Lock() addr := strings.ToLower(v.FromAddress) e.PayMsg[addr] = &v e.mu.Unlock() default: return fmt.Errorf("unsupported message type: %T", msg) } return nil } func (e *ETHNode) RemoveAddress(msg any) error { switch v := msg.(type) { case message.RemoveListenMsg_req: e.mu.Lock() addr := strings.ToLower(v.Address) delete(e.TopupMsg, addr) e.mu.Unlock() case message.WithdrawMsg_req: e.mu.Lock() addr := strings.ToLower(v.ToAddress) delete(e.WithdrawMsg, addr) e.mu.Unlock() case message.PayMsg_req: e.mu.Lock() addr := strings.ToLower(v.FromAddress) delete(e.PayMsg, addr) e.mu.Unlock() default: return fmt.Errorf("unsupported message type: %T", msg) } return nil } func (e *ETHNode) Listen(ch chan any) { log.Println("✅ 开始监听 ETH 和 USDT 转账事件...") go func() { err := e.listenETHTransactions(ch) if err != nil { log.Fatalf("Listen ETH Transactions Error: %v", err) } }() go func() { err := e.listenUSDTTransactions(ch) if err != nil { log.Fatalf("Listen USDT Transactions Error: %v", err) } }() } // 转账 func (e *ETHNode) Transfer(msg any) error { switch v := msg.(type) { case message.WithdrawMsg_req: // 1. 校验余额 verifyResult, err := e.verifyBalance(v.Symbol, v.FromAddress, v.Amount) if err != nil || !verifyResult { log.Printf("address (%s) balance verification failed: %v", v.FromAddress, err) return err } // 2. 构建未签名交易 unSignTx, err := e.contractTx(v.Symbol, v.FromAddress, v.ToAddress, v.Amount) if err != nil { log.Printf("failed to create contract transaction: %v", err) return err } // 3. 签名交易 signedTx, err := e.signTx(unSignTx, v.FromAddress) if err != nil { log.Printf("failed to sign transaction: %v", err) return err } txHash := signedTx.Hash().Hex() // 4. 发送交易并存入交易池 if err := e.sendTransaction(signedTx, txHash, v.Symbol, v.FromAddress, v.ToAddress, v.Amount); err != nil { log.Printf("failed to send transaction: %v", err) } // 5. 将tx_hash添加到消息中 if wm, ok := e.WithdrawMsg[v.FromAddress]; ok { wm.TxHash = txHash e.WithdrawMsg[v.FromAddress] = wm } else { // 如果不存在原始消息,创建一条新的记录并写回 map(根据实际结构字段调整) v.TxHash = txHash e.WithdrawMsg[v.FromAddress] = &v } case message.PayMsg_req: // 1. 校验余额 verifyResult, err := e.verifyBalance(v.Symbol, v.FromAddress, v.TotalAmount) if err != nil || !verifyResult { log.Printf("address (%s) balance verification failed: %v", v.FromAddress, err) return err } // 2. 预先获取起始 nonce 避免并发冲突 startNonce, err := e.getTransactionNonce(v.FromAddress) if err != nil { log.Printf("failed to get start nonce: %v", err) return err } // 3. 按顺序发送多笔交易(注意:顺序发送可以避免 nonce 冲突,但可能较慢) i := 0 for toAddr, tx := range v.Trasnactions { // 构建未签名交易(使用递增的 nonce) unSignTx, err := e.buildContractTxWithNonce(v.Symbol, v.FromAddress, tx.ToAddress, tx.Amount, startNonce+uint64(i)) if err != nil { log.Printf("failed to create contract transaction: %v", err) i++ continue } // 签名交易 signedTx, err := e.signTx(unSignTx, v.FromAddress) if err != nil { log.Printf("failed to sign transaction: %v", err) i++ continue } txHash := signedTx.Hash().Hex() // 发送交易并存入交易池 if err := e.sendTransaction(signedTx, txHash, v.Symbol, v.FromAddress, tx.ToAddress, tx.Amount); err != nil { log.Printf("failed to send transaction: %v", err) i++ continue } // 将tx_hash添加到消息中 e.mu.Lock() tx.TxHash = txHash e.PayMsg[v.FromAddress].Trasnactions[toAddr] = tx e.mu.Unlock() i++ } } return nil } func (e *ETHNode) Stop() { if e.Cancel != nil { e.Cancel() } log.Println("🛑 停止监听...") } // ============================ rpc节点方法 ============================ func (e *ETHNode) getETHBalance(address string) (*big.Int, error) { account := common.HexToAddress(address) ctx := context.Background() balance, err := e.RpcClient.BalanceAt(ctx, account, nil) // nil表示最新高度 if err != nil { return nil, fmt.Errorf("failed to get eth balance:%w", err) } // fBalance := new(big.Float).SetInt(balance) // ethValue := new(big.Float).Quo(fBalance, big.NewFloat(1e18)) // 转 ETH // value, _ := ethValue.Float64() // 转 float64 return balance, nil } func (e *ETHNode) getUSDTBalance(address string) (*big.Int, error) { // 统一转换为小写(common.HexToAddress会自动处理,但为了一致性显式转换) address = strings.ToLower(address) contractAddress := e.USDT.Address accountAddress := common.HexToAddress(address) data, err := e.USDT.ABI.Pack("balanceOf", accountAddress) if err != nil { return nil, fmt.Errorf("failed to pack balanceOf data: %w", err) } msg := ethereum.CallMsg{ To: &contractAddress, Data: data, } // 使用 CallContract 方法查询合约余额 res, err := e.RpcClient.CallContract(e.Ctx, msg, nil) if err != nil { return nil, fmt.Errorf("failed to get contract balance: %w", err) } // 解析返回的字节为 *big.Int outputs, err := e.USDT.ABI.Unpack("balanceOf", res) if err != nil || len(outputs) == 0 { return nil, fmt.Errorf("failed to unpack balanceOf result: %w", err) } balance, ok := outputs[0].(*big.Int) if !ok { return nil, fmt.Errorf("unexpected type for balanceOf result") } // bal := utils.BigIntUSDTToFloat64(balance) return balance, nil } func (e *ETHNode) getTransactionNonce(address string) (uint64, error) { nonce, err := e.RpcClient.PendingNonceAt(e.Ctx, common.HexToAddress(address)) if err != nil { log.Fatalf("failed to get nonce: %v", err) return 0, err } return nonce, nil } func (e *ETHNode) getGasLimit() (uint64, error) { // 对于ERC20转账,使用固定的gas limit // 通常ERC20 transfer需要约65,000-100,000 gas // 这里设置为80,000,足够覆盖大部分情况 return 80000, nil } func (e *ETHNode) getSuggestGasPrice() (*big.Int, error) { ctx := context.Background() gasPrice, err := e.RpcClient.SuggestGasPrice(ctx) if err != nil { return nil, fmt.Errorf("get suggest-gasprice error:%v", err) } // 设置gas price上限,避免在网络拥堵时费用过高 // 这里设置为20 Gwei (20 * 10^9 wei) maxGasPrice := new(big.Int).SetUint64(20000000000) // 20 Gwei if gasPrice.Cmp(maxGasPrice) > 0 { log.Printf("⚠️ 建议gas price过高 (%v wei),使用上限 20 Gwei", new(big.Int).Div(gasPrice, big.NewInt(1e18))) return maxGasPrice, nil } // log.Printf("✅ 使用建议gas price: %v wei", new(big.Int).Div(gasPrice, big.NewInt(1e18))) return gasPrice, nil } func (e *ETHNode) checkTransaction(tx_hash string) (bool, error) { receipt, err := e.RpcClient.TransactionReceipt(e.Ctx, common.HexToHash(tx_hash)) if err != nil { return false, fmt.Errorf("check tx(%s) error: %v", tx_hash, err) } if receipt.Status == types.ReceiptStatusSuccessful { return true, nil } else { return false, nil } } // getEIP1559GasFees 获取EIP-1559的gas费用参数 func (e *ETHNode) getEIP1559GasFees() (*big.Int, *big.Int, error) { ctx := context.Background() // 获取基础费用 latestBlock, err := e.RpcClient.BlockByNumber(ctx, nil) if err != nil { return nil, nil, fmt.Errorf("failed to get latest block: %w", err) } baseFee := latestBlock.BaseFee() if baseFee == nil { return nil, nil, fmt.Errorf("base fee not available") } // 设置优先级费用(tip),这里设置为2 Gwei maxPriorityFeePerGas := new(big.Int).SetUint64(2000000000) // 2 Gwei // 计算最大费用 = 基础费用 + 优先级费用 maxFeePerGas := new(big.Int).Add(baseFee, maxPriorityFeePerGas) // 设置最大费用上限为30 Gwei maxFeeLimit := new(big.Int).SetUint64(30000000000) // 30 Gwei if maxFeePerGas.Cmp(maxFeeLimit) > 0 { log.Printf("⚠️ 计算的最大费用过高 (%v wei),使用上限 30 Gwei", new(big.Int).Div(maxFeePerGas, big.NewInt(1e18))) maxFeePerGas = maxFeeLimit } // log.Printf("✅ EIP-1559 Gas费用: BaseFee=%v wei, MaxPriorityFee=%v wei, MaxFee=%v wei", // new(big.Int).Div(baseFee, big.NewInt(1e18)), // new(big.Int).Div(maxPriorityFeePerGas, big.NewInt(1e18)), // new(big.Int).Div(maxFeePerGas, big.NewInt(1e18))) return maxFeePerGas, maxPriorityFeePerGas, nil } // ============================ 业务逻辑 ============================ // 监听ETH转账 func (e *ETHNode) listenETHTransactions(ch chan any) error { fmt.Println("🔍 开始ETH交易...") headers := make(chan *types.Header, 10) // 负责重连 for { // 订阅新区块头 sub, err := e.WsClient.SubscribeNewHead(e.Ctx, headers) if err != nil { fmt.Println("❌ 订阅ETH交易失败, 5秒后重试:", err) time.Sleep(5 * time.Second) continue } fmt.Println("✅ ETH交易订阅成功") // 处理新区块 for { select { case err := <-sub.Err(): fmt.Println("⚠️ ETH交易订阅异常,准备重连:", err) sub.Unsubscribe() time.Sleep(3 * time.Second) goto reconnect case header := <-headers: // 每当有新区块,检查待确认交易 currentHeight := header.Number.Uint64() go e.updateRealData(currentHeight) go e.handleETHEvent(header, ch) go e.confirm(ch) case <-e.Ctx.Done(): fmt.Println("🛑 收到停止信号,退出ETH交易监听") sub.Unsubscribe() return e.Ctx.Err() } } reconnect: } } // 监听USDT转账 func (e *ETHNode) listenUSDTTransactions(ch chan any) error { fmt.Println("🔍 开始USDT交易...") // 过滤掉非USDT数据 query := ethereum.FilterQuery{ Addresses: []common.Address{e.USDT.Address}, } // 负责重连 for { // 订阅日志 sub, err := e.WsClient.SubscribeFilterLogs(e.Ctx, query, e.USDT.LogsChan) if err != nil { fmt.Println("❌ USDT交易订阅失败, 5秒后重试:", err) time.Sleep(5 * time.Second) continue } fmt.Println("✅ USDT交易订阅成功") // 处理事件 for { select { case err := <-sub.Err(): fmt.Println("⚠️ USDT交易订阅异常,准备重连:", err) sub.Unsubscribe() // 清理旧订阅 time.Sleep(3 * time.Second) goto reconnect // 跳出内层循环,回到外层重新订阅 case vLog := <-e.USDT.LogsChan: go e.handleUSDTEvent(vLog, ch) // 事件解析 + 分类,传递链消息的通道是vLog而非ch,且一次只传递一笔交易 case <-e.Ctx.Done(): fmt.Println("🛑 收到停止信号,退出USDT交易监听") sub.Unsubscribe() return e.Ctx.Err() } } reconnect: } } func (e *ETHNode) updateRealData(height uint64) { // 创建 WaitGroup var wg sync.WaitGroup // 用于保存每个方法的结果 var gasLimit uint64 var suggestGasPrice *big.Int var maxFeePerGas, maxPriorityFeePerGas *big.Int var gasLimitErr, suggestGasPriceErr, eip1559GasFeesErr error // 启动协程并等待所有结果 wg.Add(3) // 获取 Gas Limit go func() { defer wg.Done() gasLimit, gasLimitErr = e.getGasLimit() if gasLimitErr != nil { log.Printf("Failed to get gas limit: %v", gasLimitErr) } else { // log.Printf("Gas Limit: %d", gasLimit) } }() // 获取建议 Gas Price go func() { defer wg.Done() suggestGasPrice, suggestGasPriceErr = e.getSuggestGasPrice() if suggestGasPriceErr != nil { log.Printf("Failed to get suggested gas price: %v", suggestGasPriceErr) } else { // log.Printf("Suggested Gas Price: %v Gwei", new(big.Int).Div(suggestGasPrice, big.NewInt(1000000000))) } }() // 获取 EIP-1559 Gas Fees go func() { defer wg.Done() maxFeePerGas, maxPriorityFeePerGas, eip1559GasFeesErr = e.getEIP1559GasFees() if eip1559GasFeesErr != nil { log.Printf("Failed to get EIP-1559 gas fees: %v", eip1559GasFeesErr) } else { // log.Printf("EIP-1559 Gas Fees: MaxFeePerGas: %v Gwei, MaxPriorityFeePerGas: %v Gwei", // new(big.Int).Div(maxFeePerGas, big.NewInt(1000000000)), // new(big.Int).Div(maxPriorityFeePerGas, big.NewInt(1000000000))) } }() // 等待所有协程完成 wg.Wait() // 检查是否有任何错误 if gasLimitErr != nil || suggestGasPriceErr != nil || eip1559GasFeesErr != nil { log.Println("One or more methods failed. Not updating RealData.") return } // 更新 RealData e.RealData.mu.Lock() defer e.RealData.mu.Unlock() e.RealData = &RealData{ Heihgt: height, GasLimit: gasLimit, GasTipCap: maxPriorityFeePerGas, GasFeeCap: maxFeePerGas, GasPrice: suggestGasPrice, } // log.Println("✅ RealData updated successfully.") } func (e *ETHNode) handleETHEvent(header *types.Header, ch chan any) { height := header.Number.Uint64() // 获取区块中的所有交易 block, err := e.RpcClient.BlockByHash(e.Ctx, header.Hash()) if err != nil { log.Printf("无法获取区块信息: %v", err) return } // 遍历区块中的每笔交易 for _, tx := range block.Transactions() { txHash := tx.Hash().Hex() // 只处理ETH转账(Value > 0) if tx.Value().Sign() <= 0 { continue } // 使用 types.Sender 获取发送方地址 signer := types.LatestSignerForChainID(e.NetID) from, err := types.Sender(signer, tx) if err != nil { log.Println("获取发送方地址失败:", err) continue } toAddr := "" if tx.To() != nil { toAddr = strings.ToLower(tx.To().Hex()) } fromAddr := strings.ToLower(from.Hex()) // 获取交易金额 amount := utils.BigIntETHToFloat64(tx.Value()) // 处理充值 for k, v := range e.TopupMsg { if k == toAddr { // 锁定并更新未确认的交易 e.mu.Lock() e.UnConfirmTxs[txHash] = &message.Transaction{ From: fromAddr, To: toAddr, Height: height, TxHash: txHash, Symbol: v.Symbol, Value: amount, Status: STATUS_PENDING, } e.TopupMsg[k].Status = STATUS_PENDING e.mu.Unlock() // 创建待确认充值消息 topup_unconfirm_msg_resp := message.TopupMsg_resp{ QueueId: v.QueueId, Address: v.Address, Status: STATUS_PENDING, Chain: v.Chain, Amount: amount, TxHash: txHash, BlockHeight: height, } // 异步发送消息到通道 go func(msg message.TopupMsg_resp) { select { case ch <- msg: log.Printf("✅ 待确认充值消息已发送") default: log.Printf("⚠️ 通道阻塞,待确认消息发送失败") } }(topup_unconfirm_msg_resp) } } // 处理提现 for k, v := range e.WithdrawMsg { if strings.EqualFold(v.FromAddress, fromAddr) && strings.EqualFold(v.ToAddress, toAddr) && v.Amount == amount { e.mu.Lock() e.UnConfirmTxs[txHash] = &message.Transaction{ From: fromAddr, To: toAddr, Height: height, TxHash: txHash, Symbol: v.Symbol, Value: amount, Status: STATUS_PENDING, } e.WithdrawMsg[k].Status = STATUS_PENDING e.mu.Unlock() } } // 处理支付 for k, v := range e.PayMsg { if strings.EqualFold(v.FromAddress, fromAddr) { for i, pay := range v.Trasnactions { if strings.EqualFold(pay.ToAddress, toAddr) && pay.Amount == amount { e.mu.Lock() e.UnConfirmTxs[txHash] = &message.Transaction{ From: fromAddr, To: toAddr, Height: height, TxHash: txHash, Symbol: v.Symbol, Value: amount, Status: STATUS_PENDING, } e.PayMsg[k].Trasnactions[i].Status = STATUS_PENDING e.mu.Unlock() } } } } } } func (e *ETHNode) handleUSDTEvent(vLog types.Log, ch chan any) { from := common.HexToAddress(vLog.Topics[1].Hex()) to := common.HexToAddress(vLog.Topics[2].Hex()) height := vLog.BlockNumber fromAddr := strings.ToLower(from.Hex()) toAddr := strings.ToLower(to.Hex()) var transferEvent struct{ Value *big.Int } if err := e.USDT.ABI.UnpackIntoInterface(&transferEvent, "Transfer", vLog.Data); err != nil { fmt.Println("ABI 解析错误:", err) return } tx_hash := vLog.TxHash.Hex() value_float := utils.BigIntUSDTToFloat64(transferEvent.Value) var status int = 2 // 分别验证3组消息 // 充值 for k, v := range e.TopupMsg { if k == toAddr { e.mu.Lock() e.UnConfirmTxs[tx_hash] = &message.Transaction{ From: fromAddr, To: toAddr, Height: height, TxHash: tx_hash, Symbol: v.Symbol, Value: value_float, Status: status, } e.mu.Unlock() topup_unconfirm_msg_resp := message.TopupMsg_resp{ QueueId: v.QueueId, Address: v.Address, Status: status, Symbol: v.Symbol, Chain: v.Chain, Amount: value_float, TxHash: tx_hash, BlockHeight: height, } // 异步发送 go func(msg message.TopupMsg_resp) { select { case ch <- msg: log.Printf("✅ 待确认充值消息已发送") default: log.Printf("⚠️ 通道阻塞,待确认消息发送失败") } }(topup_unconfirm_msg_resp) } } // 提现 for k, v := range e.WithdrawMsg { if k == fromAddr && v.ToAddress == toAddr && v.Amount == value_float { e.mu.Lock() e.UnConfirmTxs[tx_hash] = &message.Transaction{ From: fromAddr, To: toAddr, Height: height, TxHash: tx_hash, Symbol: v.Symbol, Value: value_float, Status: status, } e.WithdrawMsg[k].Status = STATUS_PENDING e.mu.Unlock() } } // 充值 for k, v := range e.PayMsg { if k == fromAddr { for i, pay := range v.Trasnactions { if pay.ToAddress == toAddr && pay.Amount == value_float { e.mu.Lock() e.UnConfirmTxs[tx_hash] = &message.Transaction{ From: fromAddr, To: toAddr, Height: height, TxHash: tx_hash, Symbol: v.Symbol, Value: value_float, Status: status, } e.PayMsg[k].Trasnactions[i].Status = STATUS_PENDING e.mu.Unlock() } } } } } func (e *ETHNode) decodePrivatekey(address string) string { // 统一转换为小写 address = strings.ToLower(address) // 查询加密后的私钥 querySql := "SELECT `private_key` FROM eth_balance WHERE address = ? LIMIT 1;" log.Println("查询私钥的钱包地址:", address) var encryptedKey string err := e.Db.QueryRow(querySql, address).Scan(&encryptedKey) if err != nil { log.Println("❌ 查询私钥失败:", err) return "" } // 使用key解密 privateKey := encryptedKey // 实际使用时替换成具体的解密代码 return privateKey } // 验证余额 func (e *ETHNode) verifyBalance(symbol, from string, amount float64) (bool, error) { var amount_b *big.Int e.mu.Lock() maxGas := e.RealData.GasFeeCap if maxGas == nil { return false, fmt.Errorf("chain data not initialized, maxGas = nil") } e.mu.Unlock() switch symbol { case "ETH": amount_b = utils.Float64ToBigIntETH(amount) totalAmount := new(big.Int).Add(amount_b, maxGas) ethBalance, err := e.getETHBalance(from) if err != nil { return false, fmt.Errorf("get (%s) eth-balance error: %v", from, err) } if ethBalance.Cmp(totalAmount) == -1 { return false, nil // 余额不足 } return true, nil case "USDT": amount_b = utils.Float64ToBigIntUSDT(amount) usdtBalance, err := e.getUSDTBalance(from) if err != nil { return false, fmt.Errorf("get (%s) usdt-balance error: %v", from, err) } if usdtBalance.Cmp(amount_b) == -1 { return false, nil // 余额不足 } return true, nil default: return false, fmt.Errorf("ETH NetWork error symbol: %s", symbol) } } // 构建交易 func (e *ETHNode) contractTx(symbol, from, to string, amount float64) (*types.Transaction, error) { nonce, err := e.getTransactionNonce(from) if err != nil { return nil, fmt.Errorf("failed to get nonce: %v", err) } return e.buildContractTxWithNonce(symbol, from, to, amount, nonce) } // 构建交易(指定 nonce) func (e *ETHNode) buildContractTxWithNonce(symbol, from, to string, amount float64, nonce uint64) (*types.Transaction, error) { e.mu.Lock() maxFeePerGas, maxPriorityFeePerGas, gasLimit := e.RealData.GasFeeCap, e.RealData.GasTipCap, e.RealData.GasLimit if maxFeePerGas == nil || maxPriorityFeePerGas == nil || gasLimit == 0 { e.mu.Unlock() return nil, fmt.Errorf("chain data not initialized!") } netID := e.NetID e.mu.Unlock() addr := common.HexToAddress(to) eip1559Tx := &types.DynamicFeeTx{ ChainID: netID, Nonce: nonce, GasTipCap: maxPriorityFeePerGas, GasFeeCap: maxFeePerGas, Gas: gasLimit, To: &addr, Data: []byte{}, } switch symbol { case "ETH": eip1559Tx.Value = utils.Float64ToBigIntETH(amount) case "USDT": eip1559Tx.Value = big.NewInt(0) data, err := e.USDT.ABI.Pack("transfer", common.HexToAddress(to), utils.Float64ToBigIntUSDT(amount)) if err != nil { return nil, fmt.Errorf("failed to pack transfer data: %v", err) } eip1559Tx.Data = data default: return nil, fmt.Errorf("ETH NetWork error symbol: %s", symbol) } return types.NewTx(eip1559Tx), nil } // 签名交易 func (e *ETHNode) signTx(tx *types.Transaction, from string) (*types.Transaction, error) { originalKey := e.decodePrivatekey(from) if originalKey == "" { return nil, fmt.Errorf("failed to query private key for address: %s", from) } privateKey, err := crypto.HexToECDSA(originalKey) if err != nil { return nil, fmt.Errorf("failed to parse private key: %v", err) } signer := types.LatestSignerForChainID(e.NetID) return types.SignTx(tx, signer, privateKey) } // 发送交易并存入待确认交易池 func (e *ETHNode) sendTransaction(tx *types.Transaction, txHash, symbol, from, to string, amount float64) error { // 发送交易 // err := e.RpcClient.SendTransaction(e.Ctx, tx) // if err != nil { // return fmt.Errorf("failed to send transaction: %v", err) // } // 获取当前区块高度 e.mu.Lock() height := e.RealData.Heihgt e.mu.Unlock() // 将交易存入待交易池 e.mu.Lock() e.UnConfirmTxs[txHash] = &message.Transaction{ Symbol: symbol, From: from, To: to, Height: height, TxHash: txHash, Value: amount, Status: STATUS_PENDING, } e.mu.Unlock() return nil } // 确认信息并返回 func (e *ETHNode) confirm(ch chan any) { e.mu.Lock() height := e.RealData.Heihgt e.mu.Unlock() var needSendMsg []any var toDeleteTxs []string // 用于记录需要从 UnConfirmTxs 中删除的交易 for k, v := range e.UnConfirmTxs { if v.Height+e.ConfirmHeight >= height { tx_result, err := e.checkTransaction(k) if err != nil { log.Printf("❌ check tx(%s) error: %v", k, err) continue } e.mu.Lock() if tx_result { // 交易成功 // 判断是否在充值消息中 for toAddr, topup_msg := range e.TopupMsg { if strings.EqualFold(toAddr, v.To) { msg := message.TopupMsg_resp{ QueueId: topup_msg.QueueId, Address: v.To, Status: STATUS_SUCCESS, Chain: topup_msg.Chain, Symbol: topup_msg.Symbol, Amount: v.Value, TxHash: v.TxHash, BlockHeight: height, } needSendMsg = append(needSendMsg, msg) break // 充值成功后不删除,可能还有后续充值 } } // 判断是否在提现消息中 for key, withdraw_msg := range e.WithdrawMsg { if v.TxHash == withdraw_msg.TxHash { msg := message.WithdrawMsg_resp{ QueueId: withdraw_msg.QueueId, Chain: withdraw_msg.Chain, Symbol: withdraw_msg.Symbol, Status: STATUS_SUCCESS, Amount: v.Value, TxHash: v.TxHash, FromAddress: v.From, ToAddress: v.To, BlockHeight: v.Height, } needSendMsg = append(needSendMsg, msg) // 删除提现消息 e.RemoveAddress(withdraw_msg) delete(e.WithdrawMsg, key) break } } // 判断是否在支付消息中 for _, pay_msg := range e.PayMsg { for payKey, pay := range pay_msg.Trasnactions { if v.TxHash == pay.TxHash { pay_msg.Trasnactions[payKey].Status = STATUS_SUCCESS } } } } else { // 交易失败 // 判断是否在充值消息中 for toAddr, topup_msg := range e.TopupMsg { if strings.EqualFold(toAddr, v.To) { msg := message.TopupMsg_resp{ QueueId: topup_msg.QueueId, Address: v.To, Status: STATUS_FAILED, Chain: topup_msg.Chain, Symbol: topup_msg.Symbol, Amount: v.Value, TxHash: v.TxHash, BlockHeight: height, } needSendMsg = append(needSendMsg, msg) // 充值失败,删除该监听地址 delete(e.TopupMsg, toAddr) break } } // 判断是否在提现消息中 for key, withdraw_msg := range e.WithdrawMsg { if v.TxHash == withdraw_msg.TxHash { msg := message.WithdrawMsg_resp{ QueueId: withdraw_msg.QueueId, Chain: withdraw_msg.Chain, Symbol: withdraw_msg.Symbol, Status: STATUS_FAILED, Amount: v.Value, TxHash: v.TxHash, FromAddress: v.From, ToAddress: v.To, BlockHeight: v.Height, } needSendMsg = append(needSendMsg, msg) // 删除提现消息 e.RemoveAddress(withdraw_msg) delete(e.WithdrawMsg, key) break } } // 判断是否在支付消息中 for _, pay_msg := range e.PayMsg { for payKey, pay := range pay_msg.Trasnactions { if v.TxHash == pay.TxHash { pay_msg.Trasnactions[payKey].Status = STATUS_FAILED } } } } e.mu.Unlock() // 标记为删除 toDeleteTxs = append(toDeleteTxs, k) } } // 删除已确认的交易 if len(toDeleteTxs) > 0 { e.mu.Lock() for _, txHash := range toDeleteTxs { delete(e.UnConfirmTxs, txHash) } e.mu.Unlock() } // 检查支付消息是否需要发送完整响应 e.mu.Lock() var payMsgsToDelete []string for key, pay_msg := range e.PayMsg { allConfirmed := true for _, tx := range pay_msg.Trasnactions { if tx.Status == STATUS_PENDING { allConfirmed = false break } } if allConfirmed { // 所有交易都已确认,发送完整响应 needSendMsg = append(needSendMsg, pay_msg) payMsgsToDelete = append(payMsgsToDelete, key) } } // 删除已全部确认的支付消息 for _, key := range payMsgsToDelete { e.RemoveAddress(e.PayMsg[key]) delete(e.PayMsg, key) } e.mu.Unlock() // 异步发送所有响应消息 if len(needSendMsg) != 0 { for _, data := range needSendMsg { go func(msg any) { select { case ch <- msg: log.Printf("✅ confirm message sent: %+v", msg) default: log.Printf("⚠️ 通道阻塞,待确认消息发送失败") } }(data) } } }