Critical fixes: - Release mutex before HTTP calls in GetStats() to prevent blocking - Fix m.cmd race between Stop() and Wait() goroutine by capturing locally - Add context support to GetStats() for proper request cancellation High priority fixes: - Add existence check in collectMinerStats() before operating on miners - Add mutex-protected httpClient getter/setter for thread-safe test mocking Changes: - Miner interface now requires context.Context for GetStats() - Stats HTTP requests timeout after 5 seconds (was 30s client default) - All callers updated to pass context (service uses request context) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
242 lines
10 KiB
Go
242 lines
10 KiB
Go
package mining
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
HighResolutionDuration = 5 * time.Minute
|
|
HighResolutionInterval = 10 * time.Second
|
|
LowResolutionInterval = 1 * time.Minute
|
|
LowResHistoryRetention = 24 * time.Hour
|
|
)
|
|
|
|
// Miner defines the standard interface for a cryptocurrency miner.
|
|
type Miner interface {
|
|
Install() error
|
|
Uninstall() error
|
|
Start(config *Config) error
|
|
Stop() error
|
|
GetStats(ctx context.Context) (*PerformanceMetrics, error)
|
|
GetName() string
|
|
GetPath() string
|
|
GetBinaryPath() string
|
|
CheckInstallation() (*InstallationDetails, error)
|
|
GetLatestVersion() (string, error)
|
|
GetHashrateHistory() []HashratePoint
|
|
AddHashratePoint(point HashratePoint)
|
|
ReduceHashrateHistory(now time.Time)
|
|
GetLogs() []string
|
|
WriteStdin(input string) error
|
|
}
|
|
|
|
// InstallationDetails contains information about an installed miner.
|
|
type InstallationDetails struct {
|
|
IsInstalled bool `json:"is_installed"`
|
|
Version string `json:"version"`
|
|
Path string `json:"path"`
|
|
MinerBinary string `json:"miner_binary"`
|
|
ConfigPath string `json:"config_path,omitempty"` // Add path to the miner-specific config
|
|
}
|
|
|
|
// SystemInfo provides general system and miner installation information.
|
|
type SystemInfo struct {
|
|
Timestamp time.Time `json:"timestamp"`
|
|
OS string `json:"os"`
|
|
Architecture string `json:"architecture"`
|
|
GoVersion string `json:"go_version"`
|
|
AvailableCPUCores int `json:"available_cpu_cores"`
|
|
TotalSystemRAMGB float64 `json:"total_system_ram_gb"`
|
|
InstalledMinersInfo []*InstallationDetails `json:"installed_miners_info"`
|
|
}
|
|
|
|
// Config represents the configuration for a miner.
|
|
type Config struct {
|
|
Miner string `json:"miner"`
|
|
Pool string `json:"pool"`
|
|
Wallet string `json:"wallet"`
|
|
Threads int `json:"threads"`
|
|
TLS bool `json:"tls"`
|
|
HugePages bool `json:"hugePages"`
|
|
Algo string `json:"algo,omitempty"`
|
|
Coin string `json:"coin,omitempty"`
|
|
Password string `json:"password,omitempty"`
|
|
UserPass string `json:"userPass,omitempty"`
|
|
Proxy string `json:"proxy,omitempty"`
|
|
Keepalive bool `json:"keepalive,omitempty"`
|
|
Nicehash bool `json:"nicehash,omitempty"`
|
|
RigID string `json:"rigId,omitempty"`
|
|
TLSSingerprint string `json:"tlsFingerprint,omitempty"`
|
|
Retries int `json:"retries,omitempty"`
|
|
RetryPause int `json:"retryPause,omitempty"`
|
|
UserAgent string `json:"userAgent,omitempty"`
|
|
DonateLevel int `json:"donateLevel,omitempty"`
|
|
DonateOverProxy bool `json:"donateOverProxy,omitempty"`
|
|
NoCPU bool `json:"noCpu,omitempty"`
|
|
CPUAffinity string `json:"cpuAffinity,omitempty"`
|
|
AV int `json:"av,omitempty"`
|
|
CPUPriority int `json:"cpuPriority,omitempty"`
|
|
CPUMaxThreadsHint int `json:"cpuMaxThreadsHint,omitempty"`
|
|
CPUMemoryPool int `json:"cpuMemoryPool,omitempty"`
|
|
CPUNoYield bool `json:"cpuNoYield,omitempty"`
|
|
HugepageSize int `json:"hugepageSize,omitempty"`
|
|
HugePagesJIT bool `json:"hugePagesJIT,omitempty"`
|
|
ASM string `json:"asm,omitempty"`
|
|
Argon2Impl string `json:"argon2Impl,omitempty"`
|
|
RandomXInit int `json:"randomXInit,omitempty"`
|
|
RandomXNoNUMA bool `json:"randomXNoNuma,omitempty"`
|
|
RandomXMode string `json:"randomXMode,omitempty"`
|
|
RandomX1GBPages bool `json:"randomX1GBPages,omitempty"`
|
|
RandomXWrmsr string `json:"randomXWrmsr,omitempty"`
|
|
RandomXNoRdmsr bool `json:"randomXNoRdmsr,omitempty"`
|
|
RandomXCacheQoS bool `json:"randomXCacheQoS,omitempty"`
|
|
APIWorkerID string `json:"apiWorkerId,omitempty"`
|
|
APIID string `json:"apiId,omitempty"`
|
|
HTTPHost string `json:"httpHost,omitempty"`
|
|
HTTPPort int `json:"httpPort,omitempty"`
|
|
HTTPAccessToken string `json:"httpAccessToken,omitempty"`
|
|
HTTPNoRestricted bool `json:"httpNoRestricted,omitempty"`
|
|
Syslog bool `json:"syslog,omitempty"`
|
|
LogFile string `json:"logFile,omitempty"`
|
|
PrintTime int `json:"printTime,omitempty"`
|
|
HealthPrintTime int `json:"healthPrintTime,omitempty"`
|
|
NoColor bool `json:"noColor,omitempty"`
|
|
Verbose bool `json:"verbose,omitempty"`
|
|
LogOutput bool `json:"logOutput,omitempty"`
|
|
Background bool `json:"background,omitempty"`
|
|
Title string `json:"title,omitempty"`
|
|
NoTitle bool `json:"noTitle,omitempty"`
|
|
PauseOnBattery bool `json:"pauseOnBattery,omitempty"`
|
|
PauseOnActive int `json:"pauseOnActive,omitempty"`
|
|
Stress bool `json:"stress,omitempty"`
|
|
Bench string `json:"bench,omitempty"`
|
|
Submit bool `json:"submit,omitempty"`
|
|
Verify string `json:"verify,omitempty"`
|
|
Seed string `json:"seed,omitempty"`
|
|
Hash string `json:"hash,omitempty"`
|
|
NoDMI bool `json:"noDMI,omitempty"`
|
|
// GPU-specific options (for XMRig dual CPU+GPU mining)
|
|
GPUEnabled bool `json:"gpuEnabled,omitempty"` // Enable GPU mining
|
|
GPUPool string `json:"gpuPool,omitempty"` // Separate pool for GPU (can differ from CPU)
|
|
GPUWallet string `json:"gpuWallet,omitempty"` // Wallet for GPU pool (defaults to main Wallet)
|
|
GPUAlgo string `json:"gpuAlgo,omitempty"` // Algorithm for GPU (e.g., "kawpow", "ethash")
|
|
GPUPassword string `json:"gpuPassword,omitempty"` // Password for GPU pool
|
|
GPUIntensity int `json:"gpuIntensity,omitempty"` // GPU mining intensity (0-100)
|
|
GPUThreads int `json:"gpuThreads,omitempty"` // GPU threads per card
|
|
Devices string `json:"devices,omitempty"` // GPU device selection (e.g., "0,1,2")
|
|
OpenCL bool `json:"opencl,omitempty"` // Enable OpenCL (AMD/Intel GPUs)
|
|
CUDA bool `json:"cuda,omitempty"` // Enable CUDA (NVIDIA GPUs)
|
|
Intensity int `json:"intensity,omitempty"` // Mining intensity for GPU miners
|
|
CLIArgs string `json:"cliArgs,omitempty"` // Additional CLI arguments
|
|
}
|
|
|
|
// PerformanceMetrics represents the performance metrics for a miner.
|
|
type PerformanceMetrics struct {
|
|
Hashrate int `json:"hashrate"`
|
|
Shares int `json:"shares"`
|
|
Rejected int `json:"rejected"`
|
|
Uptime int `json:"uptime"`
|
|
LastShare int64 `json:"lastShare"`
|
|
Algorithm string `json:"algorithm"`
|
|
AvgDifficulty int `json:"avgDifficulty"` // Average difficulty per accepted share (HashesTotal/SharesGood)
|
|
DiffCurrent int `json:"diffCurrent"` // Current job difficulty from pool
|
|
ExtraData map[string]interface{} `json:"extraData,omitempty"`
|
|
}
|
|
|
|
// HashratePoint represents a single hashrate measurement at a specific time.
|
|
type HashratePoint struct {
|
|
Timestamp time.Time `json:"timestamp"`
|
|
Hashrate int `json:"hashrate"`
|
|
}
|
|
|
|
// API represents the miner's API configuration.
|
|
type API struct {
|
|
Enabled bool `json:"enabled"`
|
|
ListenHost string `json:"listenHost"`
|
|
ListenPort int `json:"listenPort"`
|
|
}
|
|
|
|
// XMRigSummary represents the full JSON response from the XMRig API.
|
|
type XMRigSummary struct {
|
|
ID string `json:"id"`
|
|
WorkerID string `json:"worker_id"`
|
|
Uptime int `json:"uptime"`
|
|
Restricted bool `json:"restricted"`
|
|
Resources struct {
|
|
Memory struct {
|
|
Free int64 `json:"free"`
|
|
Total int64 `json:"total"`
|
|
ResidentSetMemory int64 `json:"resident_set_memory"`
|
|
} `json:"memory"`
|
|
LoadAverage []float64 `json:"load_average"`
|
|
HardwareConcurrency int `json:"hardware_concurrency"`
|
|
} `json:"resources"`
|
|
Features []string `json:"features"`
|
|
Results struct {
|
|
DiffCurrent int `json:"diff_current"`
|
|
SharesGood int `json:"shares_good"`
|
|
SharesTotal int `json:"shares_total"`
|
|
AvgTime int `json:"avg_time"`
|
|
AvgTimeMS int `json:"avg_time_ms"`
|
|
HashesTotal int `json:"hashes_total"`
|
|
Best []int `json:"best"`
|
|
} `json:"results"`
|
|
Algo string `json:"algo"`
|
|
Connection struct {
|
|
Pool string `json:"pool"`
|
|
IP string `json:"ip"`
|
|
Uptime int `json:"uptime"`
|
|
UptimeMS int `json:"uptime_ms"`
|
|
Ping int `json:"ping"`
|
|
Failures int `json:"failures"`
|
|
TLS string `json:"tls"`
|
|
TLSFingerprint string `json:"tls-fingerprint"`
|
|
Algo string `json:"algo"`
|
|
Diff int `json:"diff"`
|
|
Accepted int `json:"accepted"`
|
|
Rejected int `json:"rejected"`
|
|
AvgTime int `json:"avg_time"`
|
|
AvgTimeMS int `json:"avg_time_ms"`
|
|
HashesTotal int `json:"hashes_total"`
|
|
} `json:"connection"`
|
|
Version string `json:"version"`
|
|
Kind string `json:"kind"`
|
|
UA string `json:"ua"`
|
|
CPU struct {
|
|
Brand string `json:"brand"`
|
|
Family int `json:"family"`
|
|
Model int `json:"model"`
|
|
Stepping int `json:"stepping"`
|
|
ProcInfo int `json:"proc_info"`
|
|
AES bool `json:"aes"`
|
|
AVX2 bool `json:"avx2"`
|
|
X64 bool `json:"x64"`
|
|
Is64Bit bool `json:"64_bit"`
|
|
L2 int `json:"l2"`
|
|
L3 int `json:"l3"`
|
|
Cores int `json:"cores"`
|
|
Threads int `json:"threads"`
|
|
Packages int `json:"packages"`
|
|
Nodes int `json:"nodes"`
|
|
Backend string `json:"backend"`
|
|
MSR string `json:"msr"`
|
|
Assembly string `json:"assembly"`
|
|
Arch string `json:"arch"`
|
|
Flags []string `json:"flags"`
|
|
} `json:"cpu"`
|
|
DonateLevel int `json:"donate_level"`
|
|
Paused bool `json:"paused"`
|
|
Algorithms []string `json:"algorithms"`
|
|
Hashrate struct {
|
|
Total []float64 `json:"total"`
|
|
Highest float64 `json:"highest"`
|
|
} `json:"hashrate"`
|
|
Hugepages []int `json:"hugepages"`
|
|
}
|
|
|
|
// AvailableMiner represents a miner that is available for use.
|
|
type AvailableMiner struct {
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
}
|