Files
m2pool_payment/internal/blockchain/eth/eth.go
2025-11-14 17:43:25 +08:00

232 lines
6.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 := &ETHNode{
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.initTables()
if err != nil {
return nil, 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("🛑 停止监听...")
}