2025-10-16 18:54:27 +08:00
|
|
|
|
package eth
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"context"
|
|
|
|
|
|
"fmt"
|
|
|
|
|
|
"log"
|
|
|
|
|
|
"m2pool-payment/internal/db"
|
2025-11-13 17:08:38 +08:00
|
|
|
|
"m2pool-payment/internal/listen"
|
2025-10-16 18:54:27 +08:00
|
|
|
|
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 {
|
2025-11-13 17:08:38 +08:00
|
|
|
|
decodeKey string // 消息解密密钥,用于解密RMQ消息的Sign
|
|
|
|
|
|
NetID *big.Int
|
2025-10-31 13:46:58 +08:00
|
|
|
|
Config message.ETHConfig
|
|
|
|
|
|
ConfirmHeight uint64
|
|
|
|
|
|
WsClient *ethclient.Client
|
|
|
|
|
|
RpcClient *ethclient.Client
|
2025-11-13 17:08:38 +08:00
|
|
|
|
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
|
2025-10-16 18:54:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type USDT struct {
|
2025-10-31 13:46:58 +08:00
|
|
|
|
Address common.Address // USDT合约地址
|
|
|
|
|
|
ListeningAddresses map[string]any // 监听的USDT转账消息
|
|
|
|
|
|
ABI abi.ABI // USDT ABI
|
|
|
|
|
|
TransferSig common.Hash // USDT函数签名
|
|
|
|
|
|
LogsChan chan types.Log
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-13 17:08:38 +08:00
|
|
|
|
type UnConfirmedTxs struct {
|
|
|
|
|
|
mu sync.Mutex
|
|
|
|
|
|
Transactions map[string]message.Transaction // {"queue_id": message.Transactions{}, ...}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type NetInfo struct {
|
2025-10-31 13:46:58 +08:00
|
|
|
|
mu sync.Mutex
|
2025-11-13 17:08:38 +08:00
|
|
|
|
Height uint64
|
2025-10-31 13:46:58 +08:00
|
|
|
|
GasLimit uint64
|
|
|
|
|
|
GasTipCap *big.Int
|
|
|
|
|
|
GasFeeCap *big.Int
|
|
|
|
|
|
GasPrice *big.Int // 老版本转账使用的gas
|
2025-10-16 18:54:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-13 17:08:38 +08:00
|
|
|
|
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) {
|
2025-10-16 18:54:27 +08:00
|
|
|
|
// 连入ETH节点的ws
|
2025-11-13 17:08:38 +08:00
|
|
|
|
ws_client, err := ethclient.Dial(cfg.ETHConfig.WsUrl)
|
2025-10-16 18:54:27 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, fmt.Errorf("failed to connect to Ethereum node: %w", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
// 连入ETH节点的rpc
|
2025-11-13 17:08:38 +08:00
|
|
|
|
rpc_client, err := ethclient.Dial(cfg.ETHConfig.RpcUrl)
|
2025-10-16 18:54:27 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, fmt.Errorf("failed to connect to Ethereum node rpc: %w", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
// 创建可取消的 context
|
|
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
2025-11-13 17:08:38 +08:00
|
|
|
|
|
2025-10-16 18:54:27 +08:00
|
|
|
|
// 获得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)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-13 17:08:38 +08:00
|
|
|
|
// 初始化MySQL数据库
|
|
|
|
|
|
dbConn, err := db.NewMySQLPool(cfg.MysqlConfig["wallet"])
|
2025-10-16 18:54:27 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
cancel()
|
|
|
|
|
|
return nil, fmt.Errorf("mysql connect error: %w", err)
|
|
|
|
|
|
}
|
2025-10-31 13:46:58 +08:00
|
|
|
|
|
2025-11-13 17:08:38 +08:00
|
|
|
|
// 初始化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{
|
2025-10-31 13:46:58 +08:00
|
|
|
|
decodeKey: decodeKey,
|
|
|
|
|
|
NetID: netId,
|
2025-11-13 17:08:38 +08:00
|
|
|
|
Config: cfg.ETHConfig,
|
|
|
|
|
|
ConfirmHeight: cfg.ETHConfig.ConfirmHeight,
|
2025-10-31 13:46:58 +08:00
|
|
|
|
WsClient: ws_client,
|
|
|
|
|
|
RpcClient: rpc_client,
|
|
|
|
|
|
USDT: usdt,
|
2025-11-13 17:08:38 +08:00
|
|
|
|
MysqlDB: dbConn,
|
|
|
|
|
|
SqliteDB: sqlite,
|
|
|
|
|
|
NetInfo: &NetInfo{},
|
|
|
|
|
|
UnConfirmedTxs: &UnConfirmedTxs{
|
|
|
|
|
|
Transactions: make(map[string]message.Transaction),
|
|
|
|
|
|
},
|
|
|
|
|
|
Wallets: make(map[string]*Wallets),
|
|
|
|
|
|
MessageServer: l,
|
|
|
|
|
|
LogsChan: logchan,
|
2025-10-31 13:46:58 +08:00
|
|
|
|
Ctx: ctx,
|
|
|
|
|
|
Cancel: cancel,
|
|
|
|
|
|
}
|
2025-11-14 17:43:25 +08:00
|
|
|
|
// 初始化表
|
2025-11-18 11:10:16 +08:00
|
|
|
|
// err = ethnode.MysqlDB.ExecuteSQLFile("../public/eth_mysql.sql")
|
|
|
|
|
|
// if err != nil {
|
|
|
|
|
|
// log.Fatalf("ETH-Chain初始化数据库表失败:%v", err)
|
|
|
|
|
|
// }
|
2025-11-13 17:08:38 +08:00
|
|
|
|
// 更新网络公共数据和加载钱包
|
|
|
|
|
|
height, err := ethnode.getHeight()
|
2025-10-16 18:54:27 +08:00
|
|
|
|
if err != nil {
|
2025-11-13 17:08:38 +08:00
|
|
|
|
return nil, fmt.Errorf("failed to get blockHeight: %v", err)
|
2025-10-16 18:54:27 +08:00
|
|
|
|
}
|
2025-11-13 17:08:38 +08:00
|
|
|
|
// 更新网络公共数据
|
|
|
|
|
|
ethnode.updateNetInfo(height)
|
|
|
|
|
|
// 加载钱包
|
|
|
|
|
|
if err := ethnode.loadWallets(); err != nil {
|
|
|
|
|
|
return nil, fmt.Errorf("[inital eth wallets]: %v", err)
|
2025-10-16 18:54:27 +08:00
|
|
|
|
}
|
2025-11-13 17:08:38 +08:00
|
|
|
|
// 加载未确认交易
|
|
|
|
|
|
if err := ethnode.loadUnConfirmedTxs(); err != nil {
|
|
|
|
|
|
return nil, fmt.Errorf("load unconfirmtxs error: %v", err)
|
2025-10-16 18:54:27 +08:00
|
|
|
|
}
|
2025-11-13 17:08:38 +08:00
|
|
|
|
log.Println("✅ ETH节点已启动")
|
2025-11-14 17:43:25 +08:00
|
|
|
|
// ethnode.handleHistoryETHEvent(23795552)
|
2025-11-13 17:08:38 +08:00
|
|
|
|
return ethnode, nil
|
2025-10-16 18:54:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-13 17:08:38 +08:00
|
|
|
|
// 转账
|
|
|
|
|
|
func (e *ETHNode) Transfer(from, to, symbol string, amount, fee float64) error {
|
|
|
|
|
|
// 执行转账
|
|
|
|
|
|
err := e.handleTx(symbol, from, to, amount)
|
2025-10-27 16:27:33 +08:00
|
|
|
|
if err != nil {
|
2025-11-13 17:08:38 +08:00
|
|
|
|
return fmt.Errorf("%s-tranfer error: %v", symbol, err)
|
2025-10-31 13:46:58 +08:00
|
|
|
|
}
|
2025-11-13 17:08:38 +08:00
|
|
|
|
return nil
|
2025-10-31 13:46:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-13 17:08:38 +08:00
|
|
|
|
// 监听消息
|
|
|
|
|
|
func (e *ETHNode) ListenMsg() {
|
|
|
|
|
|
log.Printf("✅ 开始监听msg...")
|
|
|
|
|
|
if e.MessageServer.ChToChainServer["ETH"] == nil {
|
|
|
|
|
|
log.Printf("ETH消息通道还未建立")
|
|
|
|
|
|
return
|
2025-10-16 18:54:27 +08:00
|
|
|
|
}
|
2025-10-27 16:27:33 +08:00
|
|
|
|
|
2025-11-13 17:08:38 +08:00
|
|
|
|
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)
|
2025-10-16 18:54:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-13 17:08:38 +08:00
|
|
|
|
// 监听区块链数据
|
|
|
|
|
|
func (e *ETHNode) Listen() error {
|
|
|
|
|
|
log.Println("✅ 开始监听 ETH 和 USDT 转账事件...")
|
2025-10-31 13:46:58 +08:00
|
|
|
|
go func() {
|
2025-11-13 17:08:38 +08:00
|
|
|
|
err := e.listenETHTransactions()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
log.Printf("Listen ETH Transactions Error: %v", err)
|
2025-10-31 13:46:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
|
|
go func() {
|
2025-11-13 17:08:38 +08:00
|
|
|
|
err := e.listenUSDTTransactions()
|
2025-10-27 16:27:33 +08:00
|
|
|
|
if err != nil {
|
2025-11-13 17:08:38 +08:00
|
|
|
|
log.Printf("Listen USDT Transactions Error: %v", err)
|
2025-10-27 16:27:33 +08:00
|
|
|
|
}
|
2025-11-13 17:08:38 +08:00
|
|
|
|
}()
|
2025-10-27 16:27:33 +08:00
|
|
|
|
|
2025-10-31 13:46:58 +08:00
|
|
|
|
return nil
|
|
|
|
|
|
}
|
2025-10-27 16:27:33 +08:00
|
|
|
|
|
2025-11-13 17:08:38 +08:00
|
|
|
|
func (e *ETHNode) Stop() {
|
|
|
|
|
|
if e.Cancel != nil {
|
|
|
|
|
|
e.Cancel()
|
2025-10-31 13:46:58 +08:00
|
|
|
|
}
|
2025-11-13 17:08:38 +08:00
|
|
|
|
log.Println("🛑 停止监听...")
|
2025-10-16 18:54:27 +08:00
|
|
|
|
}
|