/* 持续挖矿 1,在主配置文件中选择是否开启 2,开启后将读取挖矿配置(mining.conf) 3,根据配置,启动相应的挖矿软件开始挖矿 4,接收到tcp协议传送过来的新挖矿任务后,停止当前挖矿,并开启新的挖矿 5,新挖矿任务到期或结束后,重新开启挖矿 配置文件:算法、挖矿软件、挖矿配置(算法、钱包、矿工号、挖矿地址) */ package sustain import ( message "client/internal/msg" "client/internal/src" "fmt" "log" "os" "os/exec" "path/filepath" "runtime" "strings" "sync" "time" "gopkg.in/ini.v1" ) // SustainMiningConfig 持续挖矿配置 type SustainMiningConfig struct { Enabled bool // 是否启用持续挖矿 Algo string // 算法 Coin string // 币种 Miner string // 挖矿软件 (lolminer/bzminer/rigel) PoolUrl string // 矿池地址 Wallet string // 钱包地址 WorkerID string // 矿工号 PoolUser string // 矿池账号(可选) WalletMining bool // 是否使用钱包挖矿 } // SustainMiner 持续挖矿管理器 type SustainMiner struct { mu sync.Mutex config SustainMiningConfig miningConfig message.MiningConfig os *src.SystemServer osName string currentProcess *exec.Cmd // 当前挖矿进程 isRunning bool // 是否正在运行持续挖矿 isPaused bool // 是否被暂停(因为有新任务) stopChan chan struct{} // 停止信号 } // NewSustainMiner 创建持续挖矿管理器 func NewSustainMiner(os *src.SystemServer, osName string, miningConfig message.MiningConfig) *SustainMiner { return &SustainMiner{ os: os, osName: osName, miningConfig: miningConfig, stopChan: make(chan struct{}), } } // LoadConfig 从配置文件加载持续挖矿配置 func (s *SustainMiner) LoadConfig() error { var confFile string if runtime.GOOS == "windows" { confFile = "mining.windows.conf" } else { confFile = "mining.linux.conf" } cfg, err := ini.Load(confFile) if err != nil { return fmt.Errorf("读取配置文件失败: %v", err) } // 读取 [sustain] 部分 section := cfg.Section("sustain") s.config.Enabled, _ = section.Key("enabled").Bool() if !s.config.Enabled { log.Println("持续挖矿未启用") return nil } // 读取配置值,并去掉可能的引号 s.config.Algo = strings.Trim(section.Key("algo").String(), `"`) s.config.Coin = strings.Trim(section.Key("coin").String(), `"`) s.config.Miner = strings.Trim(section.Key("miner").String(), `"`) s.config.PoolUrl = strings.Trim(section.Key("pool_url").String(), `"`) s.config.Wallet = strings.Trim(section.Key("wallet").String(), `"`) s.config.WorkerID = strings.Trim(section.Key("worker_id").String(), `"`) s.config.PoolUser = strings.Trim(section.Key("pool_user").String(), `"`) s.config.WalletMining, _ = section.Key("wallet_mining").Bool() // 验证配置 if s.config.Algo == "" || s.config.Coin == "" || s.config.Miner == "" || s.config.PoolUrl == "" || s.config.Wallet == "" || s.config.WorkerID == "" { return fmt.Errorf("持续挖矿配置不完整") } // 验证挖矿软件路径 switch strings.ToLower(s.config.Miner) { case "lolminer": if s.miningConfig.LolMinerPath == "" { return fmt.Errorf("lolminer 路径未配置") } case "bzminer": if s.miningConfig.BzMinerPath == "" { return fmt.Errorf("bzminer 路径未配置") } case "rigel": if s.miningConfig.RigelPath == "" { return fmt.Errorf("rigel 路径未配置") } default: return fmt.Errorf("不支持的挖矿软件: %s", s.config.Miner) } log.Printf("持续挖矿配置加载成功: 算法=%s, 币种=%s, 挖矿软件=%s", s.config.Algo, s.config.Coin, s.config.Miner) return nil } // Start 启动持续挖矿 func (s *SustainMiner) Start() error { s.mu.Lock() defer s.mu.Unlock() if !s.config.Enabled { return nil } if s.isRunning { log.Println("持续挖矿已在运行中") return nil } s.isRunning = true s.isPaused = false s.stopChan = make(chan struct{}) log.Println("启动持续挖矿...") go s.run() return nil } // Stop 停止持续挖矿 func (s *SustainMiner) Stop() { s.mu.Lock() defer s.mu.Unlock() if !s.isRunning { return } log.Println("停止持续挖矿...") close(s.stopChan) s.isRunning = false // 停止当前挖矿进程 if s.currentProcess != nil && s.currentProcess.Process != nil { log.Println("停止当前挖矿进程...") if runtime.GOOS == "linux" && s.config.Miner == "rigel" { // Linux rigel 使用 Interrupt 信号 s.currentProcess.Process.Signal(os.Interrupt) } else { s.currentProcess.Process.Kill() } s.currentProcess = nil } } // Pause 暂停持续挖矿(当有新任务时调用) func (s *SustainMiner) Pause() { s.mu.Lock() defer s.mu.Unlock() if !s.isRunning || s.isPaused { return } log.Println("暂停持续挖矿(有新任务)...") s.isPaused = true // 停止当前挖矿进程 if s.currentProcess != nil && s.currentProcess.Process != nil { if runtime.GOOS == "linux" && s.config.Miner == "rigel" { s.currentProcess.Process.Signal(os.Interrupt) } else { s.currentProcess.Process.Kill() } s.currentProcess = nil } } // Resume 恢复持续挖矿(当任务结束后调用) func (s *SustainMiner) Resume() { s.mu.Lock() defer s.mu.Unlock() if !s.isRunning || !s.isPaused { return } log.Println("恢复持续挖矿...") s.isPaused = false // 重新启动挖矿 go s.startMining() } // run 持续挖矿主循环 func (s *SustainMiner) run() { for { select { case <-s.stopChan: log.Println("持续挖矿已停止") return default: s.mu.Lock() paused := s.isPaused s.mu.Unlock() if !paused { s.startMining() // 等待挖矿进程结束(正常情况下不会结束,除非被停止) if s.currentProcess != nil { s.currentProcess.Wait() } } else { // 如果被暂停,等待恢复 time.Sleep(1 * time.Second) } } } } // startMining 启动挖矿进程 func (s *SustainMiner) startMining() { s.mu.Lock() defer s.mu.Unlock() if s.isPaused { return } var cmd *exec.Cmd var err error address := s.config.Wallet if !s.config.WalletMining && s.config.PoolUser != "" { address = s.config.PoolUser } switch strings.ToLower(s.config.Miner) { case "lolminer": cmd, err = s.startLolMiner(address) case "bzminer": cmd, err = s.startBzMiner(address) case "rigel": cmd, err = s.startRigel(address) default: log.Printf("不支持的挖矿软件: %s", s.config.Miner) return } if err != nil { log.Printf("启动持续挖矿失败: %v", err) return } s.currentProcess = cmd log.Printf("持续挖矿已启动: %s (PID: %d)", s.config.Miner, cmd.Process.Pid) } // startLolMiner 启动 lolminer func (s *SustainMiner) startLolMiner(address string) (*exec.Cmd, error) { dir := s.miningConfig.LolMinerPath var name string if runtime.GOOS == "windows" { name = filepath.Join(dir, "lolMiner.exe") } else { name = filepath.Join(dir, "lolMiner") } args := []string{"--algo", s.config.Coin, "--pool", s.config.PoolUrl, "--user", address + "." + s.config.WorkerID} cmd := exec.Command(name, args...) cmd.Dir = dir cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr log.Printf("启动持续挖矿 lolminer: %s %s", name, strings.Join(args, " ")) err := cmd.Start() return cmd, err } // startBzMiner 启动 bzminer func (s *SustainMiner) startBzMiner(address string) (*exec.Cmd, error) { dir := s.miningConfig.BzMinerPath var name string if runtime.GOOS == "windows" { name = filepath.Join(dir, "bzminer.exe") } else { name = filepath.Join(dir, "bzminer") } args := []string{"-a", s.config.Coin, "-w", address + "." + s.config.WorkerID, "-p", s.config.PoolUrl} cmd := exec.Command(name, args...) cmd.Dir = dir cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr log.Printf("启动持续挖矿 bzminer: %s %s", name, strings.Join(args, " ")) err := cmd.Start() return cmd, err } // startRigel 启动 rigel func (s *SustainMiner) startRigel(address string) (*exec.Cmd, error) { dir := s.miningConfig.RigelPath var name string if runtime.GOOS == "windows" { name = filepath.Join(dir, "rigel.exe") } else { name = filepath.Join(dir, "rigel") } args := []string{"--no-watchdog", "-a", strings.ToLower(s.config.Algo), "-o", s.config.PoolUrl, "-u", address, "-w", s.config.WorkerID, "--log-file", "logs/miner.log"} cmd := exec.Command(name, args...) cmd.Dir = dir cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr log.Printf("启动持续挖矿 rigel: %s %s", name, strings.Join(args, " ")) err := cmd.Start() return cmd, err } // IsRunning 检查是否正在运行 func (s *SustainMiner) IsRunning() bool { s.mu.Lock() defer s.mu.Unlock() return s.isRunning && !s.isPaused } // IsPaused 检查是否被暂停 func (s *SustainMiner) IsPaused() bool { s.mu.Lock() defer s.mu.Unlock() return s.isPaused }