package eth import ( "context" "database/sql" "fmt" "log" "m2pool-payment/internal/constant" message "m2pool-payment/internal/msg" "m2pool-payment/internal/utils" "math/big" "os" "strconv" "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" ) func init_USDT() *USDT { // 构造USDT合约相关 usdt := &USDT{} usdt.Address = common.HexToAddress(constant.ETH_ERC20_USDT_CONTRACT_ADDRESS) // 解析合约地址 usdt.ABI = func() abi.ABI { a, _ := abi.JSON(strings.NewReader(constant.ETH_ERC20_ABI)); 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) return usdt } func (e *ETHNode) initTables() error { sql_script_path := "../public/eth.sql" sql_byte, err := os.ReadFile(sql_script_path) if err != nil { return fmt.Errorf("open msg-sql file error: %v", err) } err = e.SqliteDB.CreateTable(string(sql_byte)) if err != nil { return fmt.Errorf("exec eth-sql error: %v", err) } return nil } func (e *ETHNode) updateNetInfo(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.NetInfo.mu.Lock() defer e.NetInfo.mu.Unlock() e.NetInfo = &NetInfo{ Height: height, GasLimit: gasLimit, GasTipCap: maxPriorityFeePerGas, GasFeeCap: maxFeePerGas, GasPrice: suggestGasPrice, } // log.Println("✅ RealData updated successfully.") } 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 (2 * 10^9 wei) maxGasPrice := new(big.Int).SetUint64(2000000000) // 2 Gwei if gasPrice.Cmp(maxGasPrice) > 0 { log.Printf("⚠️ 建议gas price过高 (%v wei),使用上限 2 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 } // 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(2000000) // 0.2 Gwei // 计算最大费用 = 基础费用 + 优先级费用 maxFeePerGas := new(big.Int).Add(baseFee, maxPriorityFeePerGas) // 设置最大费用上限为2 Gwei maxFeeLimit := new(big.Int).SetUint64(2000000000) // 2 Gwei if maxFeePerGas.Cmp(maxFeeLimit) > 0 { log.Printf("⚠️ 计算的最大费用过高 (%v wei),使用上限 2 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 } func (e *ETHNode) getHeight() (uint64, error) { height, err := e.RpcClient.BlockNumber(e.Ctx) if err != nil { return 0, fmt.Errorf("[inital net info]get block height error: %v", err) } return height, nil } // Helper function to convert []string to []interface{} func toInterfaceSlice(addresses []string) []interface{} { result := make([]interface{}, len(addresses)) for i, v := range addresses { result[i] = v } return result } func (e *ETHNode) getAddressesPks(addresses []string) (map[string]string, error) { // If no addresses are provided, return an error if len(addresses) == 0 { return nil, fmt.Errorf("no addresses provided") } // Build a placeholder string for the IN clause placeholders := make([]string, len(addresses)) for i := range addresses { placeholders[i] = "?" } inClause := strings.Join(placeholders, ", ") // Construct the SQL query str := fmt.Sprintf("SELECT address, private_key FROM eth_balance WHERE address IN (%s);", inClause) // Execute the query with the addresses rows, err := e.MysqlDB.Query(str, toInterfaceSlice(addresses)...) if err != nil { return nil, err } defer rows.Close() // Ensure the rows are closed after processing // Prepare a map to store the results result := make(map[string]string) // Iterate over the rows and populate the map var storedAddress, privateKey string for rows.Next() { err := rows.Scan(&storedAddress, &privateKey) if err != nil { return nil, err } // Add the address and private key to the map result[storedAddress] = privateKey } // Check if there were any errors during row iteration if err := rows.Err(); err != nil { return nil, err } return result, nil } func (e *ETHNode) getAddressPk(address string) (string, error) { str := "SELECT address, private_key FROM eth_balance WHERE address = ? LIMIT 1;" row := e.MysqlDB.QueryRow(str, address) // Variables to store the result var storedAddress, privateKey string // Scan the result into the variables err := row.Scan(&storedAddress, &privateKey) if err != nil { if err == sql.ErrNoRows { // Return an error if no rows are found return "", fmt.Errorf("no address found for %s", address) } // Return any other error return "", fmt.Errorf("error scanning row: %v", err) } // Return the private key return privateKey, nil } // 检查钱包余额是否足够 // symbol=ETH时,target_amount_eth传入实际gas费用+转账金额,target_amount_usdt传入0即可 // symbol=USDT时,target_amount_eth传入实际gas费用,target_amount_usdt传入转账金额+冻结金额 func (e *ETHNode) checkBalance(symbol, address string, target_amount_eth, target_amount_usdt *big.Int) (bool, error) { e.Wallets[address].mu.Lock() defer e.Wallets[address].mu.Unlock() if wallet, ok := e.Wallets[address]; ok { log.Printf("需要的USDT:%d,钱包的USDT:%d", target_amount_usdt, e.Wallets[address].usdt_balance.balance) switch symbol { case "ETH": // ETH余额不足支付转账金额+GAS费用 if wallet.eth_balance.balance.Cmp(target_amount_eth) < 0 { return false, nil } else { return true, nil } case "USDT": // gas费不足或USDT余额不足 if wallet.eth_balance.balance.Cmp(target_amount_eth) < 0 || wallet.usdt_balance.balance.Cmp(target_amount_usdt) < 0 { return false, nil } else { return true, nil } default: return false, fmt.Errorf("Symbol(%s) Error", symbol) } } else { return false, fmt.Errorf("Balance(%s) not found", address) } } func (e *ETHNode) loadWallets() error { // 使用 JOIN 查询 ETH_wallets, ETH_balances 和 USDT_balances,筛选 status 为 1 的钱包 query := ` SELECT ew.address, ew.queue_id, ew.timestamp, ew.sign, ew.status, eb.used_gas AS eth_used_gas, eb.balance AS eth_balance, eb.success_tx_hash AS eth_successed_tx_hash, eb.failed_tx_hash AS eth_failed_tx_hash, ub.freeze_num AS usdt_freeze_num, ub.balance AS usdt_balance, ub.success_tx_hash AS usdt_successed_tx_hash, ub.failed_tx_hash AS usdt_failed_tx_hash FROM ETH_wallets ew LEFT JOIN ETH_balances eb ON ew.address = eb.address LEFT JOIN USDT_balances ub ON ew.address = ub.address WHERE ew.status = ?` // 执行查询 rows, err := e.MysqlDB.Query(query, 1) if err != nil { return fmt.Errorf("failed to get wallets: %v", err) } defer rows.Close() // 解析字段为 big.Int 类型 parseBigInt := func(val sql.NullString) *big.Int { if val.Valid && strings.TrimSpace(val.String) != "" { if bi, ok := new(big.Int).SetString(strings.TrimSpace(val.String), 10); ok { return bi } } log.Printf("Invalid or empty string for big.Int: %s", val.String) // 添加调试日志 return big.NewInt(0) // 如果无法转换,返回 0 } // 解析字符串为交易哈希列表 parseHashList := func(val sql.NullString) []string { if !val.Valid || val.String == "" { return []string{} } parts := strings.Split(val.String, ",") res := make([]string, 0, len(parts)) for _, p := range parts { p = strings.TrimSpace(p) if p != "" { res = append(res, p) } } return res } // 用来存储钱包信息 wallets := make(map[string]*Wallets) addresses := []string{} // 逐行处理查询结果 for rows.Next() { var ( addrStr, queueID, sign sql.NullString timestamp sql.NullInt64 status sql.NullInt64 ethUsedGas, ethBalance sql.NullString ethSuccess, ethFailed sql.NullString usdtFreeze, usdtBalance sql.NullString usdtSuccess, usdtFailed sql.NullString ) // 扫描查询结果到变量 if err := rows.Scan( &addrStr, &queueID, ×tamp, &sign, &status, ðUsedGas, ðBalance, ðSuccess, ðFailed, &usdtFreeze, &usdtBalance, &usdtSuccess, &usdtFailed, ); err != nil { return fmt.Errorf("failed to scan row: %v", err) } // 如果地址字段为空,则跳过该行 if !addrStr.Valid { continue } addr := strings.ToLower(addrStr.String) // 创建钱包对象并填充数据 wallet := &Wallets{ address: addr, queueId: queueID.String, sign: sign.String, timestamp: func() uint64 { if timestamp.Valid { return uint64(timestamp.Int64) } return 0 }(), status: func() int { if status.Valid { return int(status.Int64) } return 0 }(), } // 解析 ETH 余额和信息 ethBalanceStr := strings.TrimSpace(ethBalance.String) log.Printf("Parsed ETH balance: %s for address: %s", ethBalanceStr, addrStr.String) // 调试日志 ethBalanceValue, err := strconv.ParseFloat(ethBalanceStr, 64) if err != nil { log.Printf("Failed to parse ETH balance: %v, address: %s", err, addrStr.String) ethBalanceValue = 0 // 给一个默认值 } wallet.eth_balance = ð_balance{ symbol: "ETH", used_gas: parseBigInt(ethUsedGas), // balance: big.NewInt(int64(ethBalanceValue)), // 使用 big.Int 存储数值 balance: utils.Float64ToBigInt("ETH", ethBalanceValue), successed_tx_hash: parseHashList(ethSuccess), failed_tx_hash: parseHashList(ethFailed), } // 解析 USDT 余额和信息 usdtBalanceStr := strings.TrimSpace(usdtBalance.String) log.Printf("Parsed USDT balance: %s for address: %s", usdtBalanceStr, addrStr.String) // 调试日志 usdtBalanceValue, err := strconv.ParseFloat(usdtBalanceStr, 64) if err != nil { log.Printf("Failed to parse USDT balance: %v, address: %s", err, addrStr.String) usdtBalanceValue = 0 // 给一个默认值 } wallet.usdt_balance = &usdt_balance{ symbol: "USDT", freeze_num: parseBigInt(usdtFreeze), balance: utils.Float64ToBigInt("USDT", usdtBalanceValue), // 使用 big.Int 存储数值 successed_tx_hash: parseHashList(usdtSuccess), failed_tx_hash: parseHashList(usdtFailed), } // 添加钱包地址到列表并将钱包信息存储到 map 中 addresses = append(addresses, addr) wallets[addr] = wallet } // 检查是否有查询错误 if err := rows.Err(); err != nil { return fmt.Errorf("error occurred while iterating rows: %v", err) } // 打印已加载的地址 log.Printf("ETH钱包加载成功:%v", addresses) // 如果有地址,获取钱包的私钥 if len(addresses) > 0 { pks, err := e.getAddressesPks(addresses) if err != nil { return fmt.Errorf("initial balance private key error: %v", err) } e.mu.Lock() e.Wallets = wallets // 将私钥绑定到钱包对象中 for address, pk := range pks { e.Wallets[address].pk = pk } e.mu.Unlock() } // 打印每个钱包的余额 for addr, balance := range e.Wallets { log.Printf("钱包:%s, ETH余额: %s, USDT余额: %s", addr, balance.eth_balance.balance.String(), balance.usdt_balance.balance.String()) } return nil } func (e *ETHNode) loadUnConfirmedTxs() error { // 查询语句 query_str := "SELECT queue_id, tx_type, chain, symbol, from_addr, to_addr, tx_hash, height, amount, status FROM ETH_unconfirmed_tx WHERE status = ?" // 执行查询 rows, err := e.MysqlDB.Query(query_str, 2) if err != nil { return fmt.Errorf("failed to query unconfirmed transactions: %v", err) } defer rows.Close() // 确保查询结束后关闭 rows // 遍历查询结果 for rows.Next() { var queueId, chain, symbol, fromAddr, toAddr, txHash, amount string var txType, height, status int // 读取当前行数据 if err := rows.Scan(&queueId, &txType, &chain, &symbol, &fromAddr, &toAddr, &txHash, &height, &amount, &status); err != nil { return fmt.Errorf("failed to scan row: %v", err) } // 将 amount 转换为 big.Int bigAmount := new(big.Int) bigAmountParsed, success := bigAmount.SetString(amount, 10) if !success { return fmt.Errorf("failed to convert amount '%s' to big.Int", amount) } // 锁定并填充数据 e.UnConfirmedTxs.mu.Lock() e.UnConfirmedTxs.Transactions[queueId] = message.Transaction{ QueueId: queueId, TxType: txType, Chain: chain, Symbol: symbol, From: fromAddr, To: toAddr, TxHash: txHash, Height: uint64(height), Amount: bigAmountParsed, // 使用转换后的 big.Int Status: status, } e.UnConfirmedTxs.mu.Unlock() } // 检查是否存在查询错误 if err := rows.Err(); err != nil { return fmt.Errorf("error during row iteration: %v", err) } return nil } // 获取交易nonce func (e *ETHNode) getTransactionNonce(address string) (uint64, error) { nonce, err := e.RpcClient.PendingNonceAt(e.Ctx, common.HexToAddress(address)) if err != nil { return 0, fmt.Errorf("failed to get nonce: %w", err) } return nonce, nil } // 构建交易 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, to, amount, nonce) } // 构建交易(指定 nonce) func (e *ETHNode) buildContractTxWithNonce(symbol, to string, amount float64, nonce uint64) (*types.Transaction, error) { e.mu.Lock() maxFeePerGas, maxPriorityFeePerGas, gasLimit := e.NetInfo.GasFeeCap, e.NetInfo.GasTipCap, e.NetInfo.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) eip1559Tx.To = &e.USDT.Address 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) // } e.mu.Lock() originalKey := e.Wallets[from].pk e.mu.Unlock() 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) (string, error) { // 发送交易 err := e.RpcClient.SendTransaction(e.Ctx, tx) if err != nil { return "", fmt.Errorf("failed to send transaction: %v", err) } txHash := tx.Hash().Hex() return txHash, nil } // 验证交易 func (e *ETHNode) checkTransaction(tx_hash string) (bool, *big.Int, error) { receipt, err := e.RpcClient.TransactionReceipt(e.Ctx, common.HexToHash(tx_hash)) if err != nil { return false, nil, fmt.Errorf("check tx(%s) error: %v", tx_hash, err) } if receipt.Status == types.ReceiptStatusSuccessful { effectiveGasPrice := receipt.EffectiveGasPrice actual_gas := big.NewInt(int64(receipt.GasUsed)) actualGasCost := new(big.Int).Mul(actual_gas, effectiveGasPrice) return true, actualGasCost, nil } else { return false, nil, nil } } // 完整的执行交易 func (e *ETHNode) handleTx(symbol, from, to string, amount float64) error { unsignedTx, err := e.contractTx(symbol, from, to, amount) if err != nil { return err } signedTx, err := e.signTx(unsignedTx, from) if err != nil { return err } tx_hash, err := e.sendTransaction(signedTx) if err != nil { return err } var big_amount *big.Int switch symbol { case "ETH": big_amount = utils.Float64ToBigIntETH(amount) case "USDT": big_amount = utils.Float64ToBigIntUSDT(amount) default: return fmt.Errorf("symbol(%s) error at func handleTx", symbol) } e.UnConfirmedTxs.mu.Lock() e.UnConfirmedTxs.Transactions[tx_hash] = message.Transaction{ Chain: "ETH", Symbol: symbol, From: from, To: to, TxHash: tx_hash, Amount: big_amount, Status: constant.STATUS_UNTRANSFER, } e.UnConfirmedTxs.mu.Unlock() return nil } func (e *ETHNode) listenETHTransactions() error { 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() e.updateNetInfo(currentHeight) go e.handleETHEvent(header) go e.confirm() case <-e.Ctx.Done(): fmt.Println("🛑 收到停止信号,退出ETH交易监听") sub.Unsubscribe() return e.Ctx.Err() } } reconnect: } } func (e *ETHNode) handleHistoryETHEvent(height uint64) { block, err := e.RpcClient.BlockByNumber(e.Ctx, big.NewInt(int64(height))) if err != nil { log.Println(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()) if _, ok := e.Wallets[toAddr]; ok { msg, err := e.MessageServer.FindTopupMsgWithToaddress("ETH", toAddr) if err != nil { log.Printf("❌ 未查找到ETH充值消息中有address(%s)信息", toAddr) continue } log.Printf("当前交易txHash:%s, fromAddr: %s, toAddr: %s, amount: %fETH", txHash, fromAddr, toAddr, amount) log.Println(msg) } } } func (e *ETHNode) handleETHEvent(header *types.Header) { height := header.Number.Uint64() log.Printf("当前区块高度:%d", height) // 获取区块中的所有交易 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()) // toAddr和监听钱包一致,表示(充值) if _, ok := e.Wallets[toAddr]; ok { log.Printf("监听地址: %s,交易信息:%v", toAddr, tx) msg, err := e.MessageServer.FindTopupMsgWithToaddress("ETH", toAddr) if err != nil { log.Printf("❌ 未查找到ETH充值消息中有address(%s)信息", toAddr) continue } resp := message.TopupMsg_resp{ QueueId: msg.QueueId, Chain: "ETH", Symbol: "ETH", FromAddress: fromAddr, Address: toAddr, TxHash: txHash, Amount: amount, BlockHeight: height, Status: constant.STATUS_PENDING, } go e.asyncSendMsgToListen(resp, 3, 5*time.Second) e.UnConfirmedTxs.mu.Lock() e.UnConfirmedTxs.Transactions[txHash] = message.Transaction{ QueueId: msg.QueueId, TxType: 0, Chain: "ETH", Symbol: "ETH", From: fromAddr, To: toAddr, TxHash: txHash, Height: height, Amount: tx.Value(), Status: constant.STATUS_PENDING, } e.UnConfirmedTxs.mu.Unlock() go func() { str := "INSERT INTO ETH_unconfirmed_tx(queue_id, tx_type, chain, symbol, from_addr, to_addr, tx_hash, height, amount) VALUES (?,?,?,?,?,?,?,?,?)" params := []any{msg.QueueId, 0, "ETH", "ETH", fromAddr, toAddr, txHash, height, amount} _, err := e.MysqlDB.Insert(str, [][]any{params}) if err != nil { log.Fatalf("INSERT ETH_unconfirmed_tx table error: %v", err) return } }() continue } // fromAddr和监听钱包一致,表示(提现/支付) if _, ok := e.Wallets[fromAddr]; ok { var msg any var ee error msg, ee = e.MessageServer.FindPayMsgWithToaddress("ETH", fromAddr) if ee != nil { msg, ee = e.MessageServer.FindWithdrawMsgWithToaddress("ETH", fromAddr) if ee != nil { log.Printf("❌ 未查找到ETH提现和支付消息中有address(%s)信息", fromAddr) continue } } switch v := msg.(type) { case message.WithdrawMsg_req: if v.FromAddress == fromAddr && v.ToAddress == toAddr && v.Amount == amount { resp := message.WithdrawMsg_resp{ QueueId: v.QueueId, Chain: "ETH", Symbol: "ETH", // 使用消息中的Symbol(应该是ETH) FromAddress: fromAddr, ToAddress: toAddr, TxHash: txHash, Amount: amount, Fee: v.Fee, BlockHeight: height, Status: constant.STATUS_PENDING, } go e.asyncSendMsgToListen(resp, 3, 5*time.Second) e.UnConfirmedTxs.mu.Lock() e.UnConfirmedTxs.Transactions[txHash] = message.Transaction{ QueueId: v.QueueId, TxType: 1, // 提现类型 Chain: "ETH", Symbol: "ETH", // 使用消息中的Symbol(应该是ETH) From: fromAddr, To: toAddr, TxHash: txHash, Height: height, Amount: tx.Value(), Status: constant.STATUS_PENDING, } e.UnConfirmedTxs.mu.Unlock() go func() { str := "INSERT INTO ETH_unconfirmed_tx(queue_id, tx_type, chain, symbol, from_addr, to_addr, tx_hash, height, amount) VALUES (?,?,?,?,?,?,?,?,?)" params := []any{v.QueueId, 1, "ETH", "ETH", fromAddr, toAddr, txHash, height, amount} _, err := e.MysqlDB.Insert(str, [][]any{params}) if err != nil { log.Fatalf("INSERT ETH_unconfirmed_tx table error: %v", err) return } }() continue } case message.PayMsg_req: if v.FromAddress == fromAddr && v.ToAddress == toAddr && v.Amount == amount { resp := message.PayMsg_resp{ QueueId: v.QueueId, Chain: "ETH", Symbol: "ETH", // 使用消息中的Symbol(应该是ETH) FromAddress: fromAddr, ToAddress: toAddr, TxHash: txHash, Amount: amount, Fee: v.Fee, BlockHeight: height, Status: constant.STATUS_PENDING, } go e.asyncSendMsgToListen(resp, 3, 5*time.Second) e.UnConfirmedTxs.mu.Lock() e.UnConfirmedTxs.Transactions[txHash] = message.Transaction{ QueueId: v.QueueId, TxType: 2, // 提现类型 Chain: "ETH", Symbol: "ETH", // 使用消息中的Symbol(应该是ETH) From: fromAddr, To: toAddr, TxHash: txHash, Height: height, Amount: tx.Value(), Status: constant.STATUS_PENDING, } e.UnConfirmedTxs.mu.Unlock() go func() { str := "INSERT INTO ETH_unconfirmed_tx(queue_id, tx_type, chain, symbol, from_addr, to_addr, tx_hash, height, amount) VALUES (?,?,?,?,?,?,?,?,?)" params := []any{v.QueueId, 2, "ETH", "ETH", fromAddr, toAddr, txHash, height, amount} _, err := e.MysqlDB.Insert(str, [][]any{params}) if err != nil { log.Fatalf("INSERT ETH_unconfirmed_tx table error: %v", err) return } }() continue } default: } } } } func (e *ETHNode) listenUSDTTransactions() error { // 过滤掉非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) // 事件解析 + 分类,传递链消息的通道是vLog而非ch,且一次只传递一笔交易 case <-e.Ctx.Done(): fmt.Println("🛑 收到停止信号,退出USDT交易监听") sub.Unsubscribe() return e.Ctx.Err() } } reconnect: } } func (e *ETHNode) handleUSDTEvent(vLog types.Log) { 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 } txHash := vLog.TxHash.Hex() value_float := utils.BigIntUSDTToFloat64(transferEvent.Value) // toAddr和监听钱包一致,表示(充值) if _, ok := e.Wallets[toAddr]; ok { msg, err := e.MessageServer.FindTopupMsgWithToaddress("ETH", toAddr) if err != nil { log.Printf("❌ 未查找到ETH充值消息中有address(%s)信息", toAddr) return } resp := message.TopupMsg_resp{ QueueId: msg.QueueId, Chain: "ETH", Symbol: "USDT", FromAddress: fromAddr, Address: toAddr, TxHash: txHash, Amount: value_float, BlockHeight: height, Status: constant.STATUS_PENDING, } go e.asyncSendMsgToListen(resp, 3, 5*time.Second) e.UnConfirmedTxs.mu.Lock() e.UnConfirmedTxs.Transactions[txHash] = message.Transaction{ QueueId: msg.QueueId, TxType: 0, Chain: "ETH", Symbol: "USDT", From: fromAddr, To: toAddr, TxHash: txHash, Height: height, Amount: transferEvent.Value, Status: constant.STATUS_PENDING, } e.UnConfirmedTxs.mu.Unlock() go func() { str := "INSERT INTO ETH_unconfirmed_tx(queue_id, tx_type, chain, symbol, from_addr, to_addr, tx_hash, height, amount) VALUES (?,?,?,?,?,?,?,?,?)" params := []any{msg.QueueId, 0, "ETH", "USDT", fromAddr, toAddr, txHash, height, value_float} _, err := e.MysqlDB.Insert(str, [][]any{params}) if err != nil { log.Fatalf("INSERT ETH_unconfirmed_tx table error: %v", err) return } }() return } // fromAddr和监听钱包一致,表示(提现/支付) if _, ok := e.Wallets[fromAddr]; ok { var msg any var ee error msg, ee = e.MessageServer.FindPayMsgWithToaddress("ETH", fromAddr) if ee != nil { msg, ee = e.MessageServer.FindWithdrawMsgWithToaddress("ETH", fromAddr) if ee != nil { log.Printf("❌ 未查找到ETH提现和支付消息中有address(%s)信息", fromAddr) return } } switch v := msg.(type) { case message.WithdrawMsg_req: if v.FromAddress == fromAddr && v.ToAddress == toAddr && v.Amount == value_float { resp := message.WithdrawMsg_resp{ QueueId: v.QueueId, Chain: "ETH", Symbol: "USDT", FromAddress: fromAddr, ToAddress: toAddr, TxHash: txHash, Amount: value_float, Fee: v.Fee, BlockHeight: height, Status: constant.STATUS_PENDING, } go e.asyncSendMsgToListen(resp, 3, 5*time.Second) e.UnConfirmedTxs.mu.Lock() e.UnConfirmedTxs.Transactions[txHash] = message.Transaction{ QueueId: v.QueueId, TxType: 1, // 提现类型 Chain: "ETH", Symbol: "USDT", // 使用消息中的Symbol(应该是USDT) From: fromAddr, To: toAddr, TxHash: txHash, Height: height, Amount: transferEvent.Value, Status: constant.STATUS_PENDING, } e.UnConfirmedTxs.mu.Unlock() go func() { str := "INSERT INTO ETH_unconfirmed_tx(queue_id, tx_type, chain, symbol, from_addr, to_addr, tx_hash, height, amount) VALUES (?,?,?,?,?,?,?,?,?)" params := []any{v.QueueId, 1, "ETH", "USDT", fromAddr, toAddr, txHash, height, value_float} _, err := e.MysqlDB.Insert(str, [][]any{params}) if err != nil { log.Fatalf("INSERT ETH_unconfirmed_tx table error: %v", err) return } }() return } case message.PayMsg_req: if v.FromAddress == fromAddr && v.ToAddress == toAddr && v.Amount == value_float { resp := message.PayMsg_resp{ QueueId: v.QueueId, Chain: "ETH", Symbol: "USDT", FromAddress: fromAddr, ToAddress: toAddr, TxHash: txHash, Amount: value_float, Fee: v.Fee, BlockHeight: height, Status: constant.STATUS_PENDING, } go e.asyncSendMsgToListen(resp, 3, 5*time.Second) e.UnConfirmedTxs.mu.Lock() e.UnConfirmedTxs.Transactions[txHash] = message.Transaction{ QueueId: v.QueueId, TxType: 1, // 提现类型 Chain: "ETH", Symbol: "USDT", // 使用消息中的Symbol(应该是USDT) From: fromAddr, To: toAddr, TxHash: txHash, Height: height, Amount: transferEvent.Value, Status: constant.STATUS_PENDING, } e.UnConfirmedTxs.mu.Unlock() go func() { str := "INSERT INTO ETH_unconfirmed_tx(queue_id, tx_type, chain, symbol, from_addr, to_addr, tx_hash, height, amount) VALUES (?,?,?,?,?,?,?,?,?)" params := []any{v.QueueId, 2, "ETH", "USDT", fromAddr, toAddr, txHash, height, value_float} _, err := e.MysqlDB.Insert(str, [][]any{params}) if err != nil { log.Fatalf("INSERT ETH_unconfirmed_tx table error: %v", err) return } }() return } default: } } } var tableMap = map[string]string{ "ETH": "ETH_balances", "USDT": "USDT_balances", } func (e *ETHNode) confirm() { e.mu.Lock() unconfirmedTxs := e.UnConfirmedTxs now_height := e.NetInfo.Height e.mu.Unlock() var responses []any e.UnConfirmedTxs.mu.Lock() for txHash, tx := range unconfirmedTxs.Transactions { // 高度成熟:当前高度 >= 交易高度 + 确认高度 if now_height >= tx.Height+e.ConfirmHeight { check_result, actual_gas, err := e.checkTransaction(txHash) if err != nil { log.Printf("Transaction(%s) check error: %v, 跳过此交易", txHash, err) continue } var status int var msg any var ee error // 交易检查成功,通过queue_id找到对应的消息,并根据消息发送回 // 统一处理交易结果 if check_result { msg, ee = e.MessageServer.FindMsgWithQueueID("ETH", tx.QueueId, tx.TxType) if ee != nil { log.Printf("Query Message error: %v, queue_id: %s, tx_type: %d", ee, tx.QueueId, tx.TxType) status = constant.STATUS_FAILED // 如果找不到消息,仍然需要处理交易失败的情况 msg = nil } else { status = constant.STATUS_SUCCESS } } else { status = constant.STATUS_FAILED // 交易失败时也需要查找消息以发送响应 msg, ee = e.MessageServer.FindMsgWithQueueID("ETH", tx.QueueId, tx.TxType) if ee != nil { log.Printf("Query Message error for failed tx: %v, queue_id: %s, tx_type: %d", ee, tx.QueueId, tx.TxType) msg = nil } } var float_amount = utils.BigIntToFloat64(tx.Symbol, tx.Amount) // 如果找不到消息,跳过处理 if msg == nil { delete(e.UnConfirmedTxs.Transactions, txHash) continue } switch v := msg.(type) { case message.TopupMsg_req: if status == constant.STATUS_SUCCESS { // 确认充值消息,通道返回resp,并修改钱包、数据库 switch tx.Symbol { case "ETH": e.Wallets[tx.To].eth_balance.balance = new(big.Int).Add(e.Wallets[tx.To].eth_balance.balance, tx.Amount) case "USDT": e.Wallets[tx.To].usdt_balance.balance = new(big.Int).Add(e.Wallets[tx.To].usdt_balance.balance, tx.Amount) default: log.Printf("error symbol(%s): %v, 跳过此交易", tx.Symbol, tx.Symbol) delete(e.UnConfirmedTxs.Transactions, txHash) continue } } response := message.TopupMsg_resp{ QueueId: tx.QueueId, Address: tx.To, FromAddress: tx.From, Status: status, Chain: tx.Chain, Symbol: tx.Symbol, Amount: float_amount, TxHash: txHash, BlockHeight: tx.Height, } // 修改钱包表 go func() { var str string var params []any if status == constant.STATUS_SUCCESS { str = "UPDATE " + tableMap[tx.Symbol] + " SET success_tx_hash = CONCAT(success_tx_hash, ?), balance = balance + ? WHERE address = ?" params = []any{txHash + ",", float_amount, v.Address} } else { str = "UPDATE " + tableMap[tx.Symbol] + " SET failed_tx_hash = CONCAT(failed_tx_hash, ?) WHERE address = ?" params = []any{txHash + ",", v.Address} } _, err := e.MysqlDB.Update(str, params) if err != nil { // 更详细的错误日志,包括 QueueId 和 Status log.Printf("Failed to update success_tx_hash/failed_tx_hash for queue_id %s: %v", v.QueueId, err) } }() // 修改待确认交易表 go func() { str := "UPDATE ETH_unconfirmed_tx SET status = ? WHERE tx_hash = ?" params := []any{status, txHash} _, err := e.MysqlDB.Update(str, params) if err != nil { log.Printf("Failed to update ETH_unconfirmed_tx: %v", err) } }() responses = append(responses, response) // 将消息提交至responses case message.WithdrawMsg_req: if status == constant.STATUS_SUCCESS { // 将gas费添加到钱包中 e.Wallets[tx.From].eth_balance.used_gas = new(big.Int).Add(e.Wallets[tx.From].eth_balance.used_gas, actual_gas) switch tx.Symbol { case "ETH": e.Wallets[tx.From].eth_balance.balance = new(big.Int).Sub(e.Wallets[tx.From].eth_balance.balance, tx.Amount) case "USDT": e.Wallets[tx.From].usdt_balance.balance = new(big.Int).Sub(e.Wallets[tx.From].usdt_balance.balance, tx.Amount) // 提现USDT时会有一定数量的USDT作为gas费冻结 e.Wallets[tx.From].usdt_balance.freeze_num = new(big.Int).Add(e.Wallets[tx.From].usdt_balance.freeze_num, utils.Float64ToBigIntUSDT(v.Fee)) default: log.Printf("error symbol(%s): %v, 跳过此交易", tx.Symbol, tx.Symbol) delete(e.UnConfirmedTxs.Transactions, txHash) continue } } response := message.WithdrawMsg_resp{ QueueId: tx.QueueId, Chain: tx.Chain, Symbol: tx.Symbol, Status: status, Amount: float_amount, Fee: v.Fee, TxHash: txHash, FromAddress: tx.From, ToAddress: tx.To, BlockHeight: tx.Height, } // 修改钱包表 go func() { var str, str1 string var params, params1 []any txHashWithComma := txHash + "," if status == constant.STATUS_SUCCESS { if tableMap[tx.Symbol] == "ETH_balances" { str = "UPDATE ETH_balances SET success_tx_hash = CONCAT(success_tx_hash, ?), balance = balance - ?, used_gas = used_gas + ? WHERE address = ?" params = []any{txHashWithComma, float_amount, utils.BigIntETHToFloat64(actual_gas), v.FromAddress} str1 = "" params1 = []any{} } else { str = "UPDATE USDT_balances SET success_tx_hash = CONCAT(success_tx_hash, ?), balance = balance - ?, freeze_num = freeze_num + ? WHERE address = ?" params = []any{txHashWithComma, float_amount, v.Fee, v.FromAddress} str1 = "UPDATE ETH_balances SET used_gas = used_gas + ? WHERE address = ?" params1 = []any{utils.BigIntETHToFloat64(actual_gas), v.FromAddress} } } else { str = "UPDATE " + tableMap[tx.Symbol] + " SET failed_tx_hash = CONCAT(failed_tx_hash, ?) WHERE address = ?" params = []any{txHashWithComma, v.FromAddress} str1 = "" params1 = []any{} } err := e.MysqlDB.ExecuteTransactions([]string{str, str1}, [][]any{params, params1}) if err != nil { // 更详细的错误日志,包括 QueueId 和 Status log.Printf("Failed to update success_tx_hash/failed_tx_hash for queue_id %s: %v", v.QueueId, err) } }() // 修改待确认交易表 go func() { str := "UPDATE ETH_unconfirmed_tx SET status = ? WHERE tx_hash = ?" params := []any{status, txHash} _, err := e.MysqlDB.Update(str, params) if err != nil { log.Printf("Failed to update ETH_unconfirmed_tx: %v", err) } }() responses = append(responses, response) // 将消息提交至responses case message.PayMsg_req: e.Wallets[tx.From].eth_balance.used_gas = new(big.Int).Add(e.Wallets[tx.From].eth_balance.used_gas, actual_gas) switch tx.Symbol { case "ETH": e.Wallets[tx.From].eth_balance.balance = new(big.Int).Sub(e.Wallets[tx.From].eth_balance.balance, tx.Amount) case "USDT": e.Wallets[tx.From].usdt_balance.balance = new(big.Int).Sub(e.Wallets[tx.From].usdt_balance.balance, tx.Amount) // 支付USDT时需要冻结部分USDT作为gas费(暂不执行) // e.Wallets[tx.From].usdt_balance.freeze_num = new(big.Int).Add(e.Wallets[tx.From].usdt_balance.freeze_num, tx.Amount) default: log.Printf("error symbol(%s): %v, 跳过此交易", tx.Symbol, tx.Symbol) delete(e.UnConfirmedTxs.Transactions, txHash) continue } response := message.PayMsg_resp{ QueueId: tx.QueueId, Chain: tx.Chain, Symbol: tx.Symbol, Status: status, Amount: float_amount, Fee: v.Fee, TxHash: txHash, FromAddress: tx.From, ToAddress: tx.To, BlockHeight: tx.Height, } // 修改钱包表 go func() { var str, str1 string var params, params1 []any txHashWithComma := txHash + "," if status == constant.STATUS_SUCCESS { if tableMap[tx.Symbol] == "ETH_balances" { str = "UPDATE ETH_balances SET success_tx_hash = CONCAT(success_tx_hash, ?), balance = balance - ?, used_gas = used_gas + ? WHERE address = ?" params = []any{txHashWithComma, float_amount, utils.BigIntETHToFloat64(actual_gas), v.FromAddress} str1 = "" params1 = []any{} } else { str = "UPDATE USDT_balances SET success_tx_hash = CONCAT(success_tx_hash, ?), balance = balance - ?, freeze_num = freeze_num + ? WHERE address = ?" params = []any{txHashWithComma, float_amount, v.Fee, v.FromAddress} str1 = "UPDATE ETH_balances SET used_gas = used_gas + ? WHERE address = ?" params1 = []any{utils.BigIntETHToFloat64(actual_gas), v.FromAddress} } } else { str = "UPDATE " + tableMap[tx.Symbol] + " SET failed_tx_hash = CONCAT(failed_tx_hash, ?) WHERE address = ?" params = []any{txHashWithComma, v.FromAddress} str1 = "" params1 = []any{} } err := e.MysqlDB.ExecuteTransactions([]string{str, str1}, [][]any{params, params1}) if err != nil { // 更详细的错误日志,包括 QueueId 和 Status log.Printf("Failed to update success_tx_hash/failed_tx_hash for queue_id %s: %v", v.QueueId, err) } }() // 修改待确认交易表 go func() { str := "UPDATE ETH_unconfirmed_tx SET status = ? WHERE tx_hash = ?" params := []any{status, txHash} _, err := e.MysqlDB.Update(str, params) if err != nil { log.Printf("Failed to update ETH_unconfirmed_tx: %v", err) } }() responses = append(responses, response) // 将消息提交至responses default: log.Printf("未知的消息类型: %v, 跳过此交易", v) delete(e.UnConfirmedTxs.Transactions, txHash) continue } delete(e.UnConfirmedTxs.Transactions, txHash) } } e.UnConfirmedTxs.mu.Unlock() if len(responses) > 0 { for _, v := range responses { go e.asyncSendMsgToListen(v, 3, 5*time.Second) } } } func (e *ETHNode) handleListen_Topup_req(msg message.TopupMsg_req) { pk, err := e.getAddressPk(msg.Address) if err != nil { log.Printf("Query balance(%s-%s) private_key error: %v", msg.Chain, msg.Address, err) go e.asyncSendMsgToListen(message.TopupMsg_resp{ QueueId: msg.QueueId, Chain: msg.Chain, Symbol: msg.Symbol, Address: msg.Address, Status: msg.Status, }, 3, 5*time.Second) return } // 添加到钱包 e.mu.Lock() e.Wallets[msg.Address] = &Wallets{ address: msg.Address, queueId: msg.QueueId, pk: pk, eth_balance: ð_balance{ symbol: "ETH", used_gas: big.NewInt(0), balance: big.NewInt(0), successed_tx_hash: []string{}, failed_tx_hash: []string{}, }, usdt_balance: &usdt_balance{ symbol: "USDT", freeze_num: big.NewInt(0), balance: big.NewInt(0), successed_tx_hash: []string{}, failed_tx_hash: []string{}, }, timestamp: msg.Timestamp, sign: msg.Sign, status: constant.STATUS_SUCCESS, } e.mu.Unlock() // 记录到钱包数据库 go func() { str1 := "INSERT INTO ETH_wallets (address, queue_id, timestamp, sign, status) VALUES (?,?,?,?,?)" params1 := []any{msg.Address, msg.QueueId, msg.Timestamp, msg.Sign, constant.STATUS_SUCCESS} str2 := "INSERT INTO ETH_balances (address) VALUES (?)" params2 := []any{msg.Address} str3 := "INSERT INTO USDT_balances (address) VALUES (?)" params3 := []any{msg.Address} err = e.MysqlDB.ExecuteTransactions([]string{str1, str2, str3}, [][]any{params1, params2, params3}) if err != nil { log.Printf("Received ListenServer Topup_req msg: insert sqlite3 db error: %v", err) } go e.asyncSendMsgToListen(message.UpdateReqState{ QueueId: msg.QueueId, MsgType: 0, Status: constant.STATUS_SUCCESS, }, 3, 5*time.Second) }() } func (e *ETHNode) handleListen_Withdraw_req(msg message.WithdrawMsg_req) { // 先获得当前最高gas费用 e.NetInfo.mu.Lock() maxGas := e.NetInfo.GasFeeCap e.NetInfo.mu.Unlock() var target_amount_eth, target_amount_usdt *big.Int // 将 msg.Amount 和 msg.Fee 转换为 big.Int,只调用一次 amountBigInt := utils.Float64ToBigInt(msg.Symbol, msg.Amount) feeBigInt := utils.Float64ToBigInt(msg.Symbol, msg.Fee) switch msg.Symbol { case "ETH": // 计算目标金额 target_amount_eth = new(big.Int).Add(maxGas, amountBigInt) // maxGas + msg.Amount target_amount_eth.Add(target_amount_eth, feeBigInt) // (maxGas + msg.Amount) + msg.Fee target_amount_usdt = big.NewInt(0) case "USDT": target_amount_eth = maxGas target_amount_usdt = new(big.Int).Add(amountBigInt, feeBigInt) default: return } // 构建相应通用数据,Status根据后续情况变化 result_msg := message.WithdrawMsg_resp{ QueueId: msg.QueueId, Chain: msg.Chain, Symbol: msg.Symbol, Amount: msg.Amount, Fee: msg.Fee, FromAddress: msg.FromAddress, ToAddress: msg.ToAddress, } check_result, err := e.checkBalance(msg.Symbol, msg.FromAddress, target_amount_eth, target_amount_usdt) // 余额校验错误,绕过转账,返回错误响应 if err != nil { log.Printf("check balance error: %v", err) result_msg.Status = constant.STATUS_ERROR go e.asyncSendMsgToListen(result_msg, 3, 5*time.Second) return } // 校验成功 if check_result { // 开始转账 err := e.Transfer(msg.FromAddress, msg.ToAddress, msg.Symbol, msg.Amount, msg.Fee) // 转账失败,返回转账失败结果 if err != nil { log.Printf("withdraw - transfer error: %v", err) result_msg.Status = constant.STATUS_ERROR // 提现转账错误 go e.asyncSendMsgToListen(result_msg, 3, 5*time.Second) return } // 转账成功,等待chain server listen监听到该笔交易 go e.asyncSendMsgToListen(message.UpdateReqState{ QueueId: msg.QueueId, MsgType: 1, Status: constant.STATUS_SUCCESS, }, 3, 5*time.Second) log.Printf("withdraw - transfer success: QueueId(%s)", msg.QueueId) } else { // 校验失败 // 提现转账账户余额不足 result_msg.Status = constant.STATUS_BALANCE_NOT_ENOUGH go e.asyncSendMsgToListen(result_msg, 3, 5*time.Second) return } } func (e *ETHNode) handleListen_Pay_req(msg message.PayMsg_req) { // 先获得当前最高gas费用 e.NetInfo.mu.Lock() maxGas := e.NetInfo.GasFeeCap e.NetInfo.mu.Unlock() var target_amount_eth, target_amount_usdt *big.Int // 将 msg.Amount 和 msg.Fee 转换为 big.Int,只调用一次 amountBigInt := utils.Float64ToBigInt(msg.Symbol, msg.Amount) feeBigInt := utils.Float64ToBigInt(msg.Symbol, msg.Fee) switch msg.Symbol { case "ETH": // 计算目标金额 target_amount_eth = new(big.Int).Add(maxGas, amountBigInt) // maxGas + msg.Amount target_amount_eth.Add(target_amount_eth, feeBigInt) // (maxGas + msg.Amount) + msg.Fee target_amount_usdt = big.NewInt(0) case "USDT": target_amount_eth = maxGas target_amount_usdt = new(big.Int).Add(amountBigInt, feeBigInt) default: return } // 构建相应通用数据,Status根据后续情况变化 result_msg := message.PayMsg_resp{ QueueId: msg.QueueId, Chain: msg.Chain, Symbol: msg.Symbol, Amount: msg.Amount, Fee: msg.Fee, FromAddress: msg.FromAddress, ToAddress: msg.ToAddress, } check_result, err := e.checkBalance(msg.Symbol, msg.FromAddress, target_amount_eth, target_amount_usdt) // 余额校验错误,绕过转账,返回错误响应 if err != nil { log.Printf("check balance error: %v", err) result_msg.Status = constant.STATUS_ERROR go e.asyncSendMsgToListen(result_msg, 3, 5*time.Second) return } // 校验成功 if check_result { // 开始转账 err := e.Transfer(msg.FromAddress, msg.ToAddress, msg.Symbol, msg.Amount, msg.Fee) // 转账失败,返回转账失败结果 if err != nil { log.Printf("withdraw - transfer error: %v", err) result_msg.Status = constant.STATUS_ERROR // 提现转账错误 go e.asyncSendMsgToListen(result_msg, 3, 5*time.Second) return } // 转账成功,等待chain server listen监听到该笔交易 go e.asyncSendMsgToListen(message.UpdateReqState{ QueueId: msg.QueueId, MsgType: 1, Status: constant.STATUS_SUCCESS, }, 3, 5*time.Second) log.Printf("withdraw - transfer success: QueueId(%s)", msg.QueueId) } else { // 校验失败 // 提现转账账户余额不足 result_msg.Status = constant.STATUS_BALANCE_NOT_ENOUGH go e.asyncSendMsgToListen(result_msg, 3, 5*time.Second) return } } func (e *ETHNode) handleListen_Remove_req(msg message.RemoveListenMsg_req) { e.mu.Lock() delete(e.Wallets, msg.Address) e.mu.Unlock() result_msg := message.RemoveListenMsg_resp{ QueueId: msg.QueueId, MsgType: msg.MsgType, Chain: msg.Chain, Symbol: msg.Symbol, Status: constant.STATUS_SUCCESS, } str := "UPDATE ETH_wallets SET status = ? WHERE address = ?" params := []any{0, msg.Address} _, err := e.MysqlDB.Update(str, params) if err != nil { log.Printf("Remove address(%s) error: %v", msg.Address, err) // result_msg.Status = constant.STATUS_FAILED } go e.asyncSendMsgToListen(result_msg, 3, 5*time.Second) } func (e *ETHNode) asyncSendMsgToListen(msg any, retries int, timeout time.Duration) { for retries > 0 { select { case e.MessageServer.ChFromChainServer["ETH"] <- msg: // 如果通道没有满,就发送消息 // log.Printf("Sent message to rmq_ch_out: %v", msg) return case <-time.After(timeout): // 超时控制 log.Printf("Timeout sending message ETH-ChainServer to ListenServer: %v", msg) retries-- if retries == 0 { log.Printf("Max retries reached, giving up on sending message: %v", msg) return } // 在超时后进行重试 log.Printf("Retrying sending message: %v, retries left: %d", msg, retries) } } }