diff --git a/pkg/mining/manager.go b/pkg/mining/manager.go index 0bd462a..3affb7e 100644 --- a/pkg/mining/manager.go +++ b/pkg/mining/manager.go @@ -8,17 +8,30 @@ import ( "time" ) -// Manager handles miner lifecycle and operations +// Manager handles the lifecycle and operations of multiple miners. +// It provides a centralized way to start, stop, and manage different miner +// instances, while also collecting and exposing their performance data. +// The Manager is safe for concurrent use. type Manager struct { miners map[string]Miner - mu sync.RWMutex // Mutex to protect the miners map + mu sync.RWMutex stopChan chan struct{} waitGroup sync.WaitGroup } var _ ManagerInterface = (*Manager)(nil) -// NewManager creates a new miner manager +// NewManager creates a new miner manager. +// It initializes the manager and starts a background goroutine for periodic +// statistics collection from the miners. +// +// Example: +// +// // Create a new manager +// manager := mining.NewManager() +// defer manager.Stop() +// +// // Now you can use the manager to start and stop miners func NewManager() *Manager { m := &Manager{ miners: make(map[string]Miner), @@ -29,7 +42,33 @@ func NewManager() *Manager { return m } -// StartMiner starts a new miner with the given configuration +// StartMiner starts a new miner with the given configuration. +// It takes the miner type and a configuration object, and returns the +// created miner instance or an error if the miner could not be started. +// +// Example: +// +// // Create a new manager +// manager := mining.NewManager() +// defer manager.Stop() +// +// // Create a new configuration for the XMRig miner +// config := &mining.Config{ +// Miner: "xmrig", +// Pool: "your-pool-address", +// Wallet: "your-wallet-address", +// Threads: 4, +// TLS: true, +// } +// +// // Start the miner +// miner, err := manager.StartMiner("xmrig", config) +// if err != nil { +// log.Fatalf("Failed to start miner: %v", err) +// } +// +// // Stop the miner when you are done +// defer manager.StopMiner(miner.GetName()) func (m *Manager) StartMiner(minerType string, config *Config) (Miner, error) { m.mu.Lock() defer m.mu.Unlock() @@ -61,7 +100,9 @@ func (m *Manager) StartMiner(minerType string, config *Config) (Miner, error) { return miner, nil } -// StopMiner stops a running miner +// StopMiner stops a running miner. +// It takes the name of the miner to be stopped and returns an error if the +// miner could not be stopped. func (m *Manager) StopMiner(name string) error { m.mu.Lock() defer m.mu.Unlock() @@ -80,7 +121,8 @@ func (m *Manager) StopMiner(name string) error { return nil } -// GetMiner retrieves a miner by ID +// GetMiner retrieves a running miner by its name. +// It returns the miner instance or an error if the miner is not found. func (m *Manager) GetMiner(name string) (Miner, error) { m.mu.RLock() defer m.mu.RUnlock() @@ -93,7 +135,7 @@ func (m *Manager) GetMiner(name string) (Miner, error) { return miner, nil } -// ListMiners returns all miners +// ListMiners returns a slice of all running miners. func (m *Manager) ListMiners() []Miner { m.mu.RLock() defer m.mu.RUnlock() @@ -105,7 +147,8 @@ func (m *Manager) ListMiners() []Miner { return miners } -// ListAvailableMiners returns a list of available miners +// ListAvailableMiners returns a list of available miners that can be started. +// This provides a way to discover the types of miners supported by the manager. func (m *Manager) ListAvailableMiners() []AvailableMiner { return []AvailableMiner{ { @@ -115,7 +158,7 @@ func (m *Manager) ListAvailableMiners() []AvailableMiner { } } -// startStatsCollection starts a goroutine to periodically collect stats from active miners +// startStatsCollection starts a goroutine to periodically collect stats from active miners. func (m *Manager) startStatsCollection() { m.waitGroup.Add(1) go func() { @@ -134,7 +177,7 @@ func (m *Manager) startStatsCollection() { }() } -// collectMinerStats iterates through active miners and collects their stats +// collectMinerStats iterates through active miners and collects their stats. func (m *Manager) collectMinerStats() { m.mu.RLock() minersToCollect := make([]Miner, 0, len(m.miners)) @@ -159,7 +202,9 @@ func (m *Manager) collectMinerStats() { } } -// GetMinerHashrateHistory returns the hashrate history for a specific miner +// GetMinerHashrateHistory returns the hashrate history for a specific miner. +// It takes the name of the miner and returns a slice of hashrate points +// or an error if the miner is not found. func (m *Manager) GetMinerHashrateHistory(name string) ([]HashratePoint, error) { m.mu.RLock() defer m.mu.RUnlock() @@ -172,7 +217,9 @@ func (m *Manager) GetMinerHashrateHistory(name string) ([]HashratePoint, error) return miner.GetHashrateHistory(), nil } -// Stop stops the manager and its background goroutines +// Stop stops the manager and its background goroutines. +// It should be called when the manager is no longer needed to ensure a +// graceful shutdown of the statistics collection goroutine. func (m *Manager) Stop() { close(m.stopChan) m.waitGroup.Wait() // Wait for the stats collection goroutine to finish diff --git a/pkg/mining/manager_interface.go b/pkg/mining/manager_interface.go index ed3f1b2..46a17c4 100644 --- a/pkg/mining/manager_interface.go +++ b/pkg/mining/manager_interface.go @@ -1,12 +1,38 @@ package mining // ManagerInterface defines the interface for a miner manager. +// This interface abstracts the core functionalities of a miner manager, +// allowing for different implementations to be used interchangeably. It provides +// a standard way to manage the lifecycle of miners and retrieve their data. type ManagerInterface interface { + // StartMiner starts a new miner with the given configuration. + // It takes the miner type and a configuration object, and returns the + // created miner instance or an error if the miner could not be started. StartMiner(minerType string, config *Config) (Miner, error) + + // StopMiner stops a running miner. + // It takes the name of the miner to be stopped and returns an error if the + // miner could not be stopped. StopMiner(name string) error + + // GetMiner retrieves a running miner by its name. + // It returns the miner instance or an error if the miner is not found. GetMiner(name string) (Miner, error) + + // ListMiners returns a slice of all running miners. ListMiners() []Miner + + // ListAvailableMiners returns a list of available miners that can be started. + // This provides a way to discover the types of miners supported by the manager. ListAvailableMiners() []AvailableMiner + + // GetMinerHashrateHistory returns the hashrate history for a specific miner. + // It takes the name of the miner and returns a slice of hashrate points + // or an error if the miner is not found. GetMinerHashrateHistory(name string) ([]HashratePoint, error) + + // Stop stops the manager and its background goroutines. + // It should be called when the manager is no longer needed to ensure a + // graceful shutdown of any background processes. Stop() } diff --git a/pkg/mining/mining.go b/pkg/mining/mining.go index 4aadc45..ca9f976 100644 --- a/pkg/mining/mining.go +++ b/pkg/mining/mining.go @@ -17,184 +17,351 @@ const ( LowResHistoryRetention = 24 * time.Hour // Example: keep 24 hours of 1-minute averages ) -// Miner is the interface for a miner +// Miner defines the standard interface for a cryptocurrency miner. +// This interface abstracts the core functionalities of a miner, such as installation, +// starting, stopping, and statistics retrieval, allowing for different miner +// implementations to be used interchangeably. type Miner interface { + // Install handles the setup and installation of the miner software. + // This may include downloading binaries, creating configuration files, + // and setting up necessary permissions. Install() error + + // Uninstall removes the miner software and any related configuration files. Uninstall() error Start(config *Config) error Stop() error GetStats() (*PerformanceMetrics, error) GetName() string GetPath() string - GetBinaryPath() string // New method to get the full path to the miner executable + GetBinaryPath() string + + // CheckInstallation verifies if the miner is installed correctly and returns + // details about the installation, such as the version and path. CheckInstallation() (*InstallationDetails, error) + + // GetLatestVersion retrieves the latest available version of the miner software. GetLatestVersion() (string, error) - GetHashrateHistory() []HashratePoint // New method to get hashrate history - AddHashratePoint(point HashratePoint) // New method to add a hashrate point - ReduceHashrateHistory(now time.Time) // New method to trigger history reduction + + // GetHashrateHistory returns the recent hashrate history of the miner. + GetHashrateHistory() []HashratePoint + + // AddHashratePoint adds a new hashrate data point to the miner's history. + AddHashratePoint(point HashratePoint) + + // ReduceHashrateHistory processes the raw hashrate data, potentially + // aggregating high-resolution data into a lower-resolution format for + // long-term storage. + ReduceHashrateHistory(now time.Time) } -// InstallationDetails contains information about an installed miner +// InstallationDetails contains information about an installed miner. +// It provides a standard structure for reporting the status of a miner's +// installation, including whether it's present, its version, and its location. type InstallationDetails struct { - IsInstalled bool `json:"is_installed"` - Version string `json:"version"` - Path string `json:"path"` + // IsInstalled is true if the miner is installed, false otherwise. + IsInstalled bool `json:"is_installed"` + // Version is the detected version of the installed miner. + Version string `json:"version"` + // Path is the installation path of the miner. + Path string `json:"path"` + // MinerBinary is the name of the miner's executable file. MinerBinary string `json:"miner_binary"` } -// SystemInfo provides general system and miner installation information +// SystemInfo provides general system and miner installation information. +// This struct aggregates various details about the system's environment, +// such as operating system, architecture, and available resources, as well +// as information about installed miners. 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"` + // Timestamp is the time when the system information was collected. + Timestamp time.Time `json:"timestamp"` + // OS is the operating system of the host. + OS string `json:"os"` + // Architecture is the system's hardware architecture (e.g., amd64, arm64). + Architecture string `json:"architecture"` + // GoVersion is the version of the Go runtime. + GoVersion string `json:"go_version"` + // AvailableCPUCores is the number of available CPU cores. + AvailableCPUCores int `json:"available_cpu_cores"` + // TotalSystemRAMGB is the total system RAM in gigabytes. + TotalSystemRAMGB float64 `json:"total_system_ram_gb"` + // InstalledMinersInfo is a slice containing details of all installed miners. InstalledMinersInfo []*InstallationDetails `json:"installed_miners_info"` } +// Config represents the configuration for a miner. +// This struct includes general mining parameters as well as specific options +// for different miner implementations like XMRig. It is designed to be -// Config represents the config for a miner, including XMRig specific options +// flexible and comprehensive, covering a wide range of settings from network +// and CPU configurations to logging and miscellaneous options. +// +// Example: +// +// // Create a new configuration for the XMRig miner +// config := &mining.Config{ +// Miner: "xmrig", +// Pool: "your-pool-address", +// Wallet: "your-wallet-address", +// Threads: 4, +// TLS: true, +// } 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"` + // Miner is the name of the miner to be used (e.g., "xmrig"). + Miner string `json:"miner"` + // Pool is the address of the mining pool. + Pool string `json:"pool"` + // Wallet is the user's wallet address for receiving mining rewards. + Wallet string `json:"wallet"` + // Threads is the number of CPU threads to be used for mining. + Threads int `json:"threads"` + // TLS indicates whether to use a secure TLS connection to the pool. + TLS bool `json:"tls"` + // HugePages enables or disables the use of huge pages for performance optimization. + HugePages bool `json:"hugePages"` // Network options - Algo string `json:"algo,omitempty"` - Coin string `json:"coin,omitempty"` - Password string `json:"password,omitempty"` // Corresponds to -p, not --userpass - UserPass string `json:"userPass,omitempty"` // Corresponds to -O - 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"` + // Algo specifies the mining algorithm to be used. + Algo string `json:"algo,omitempty"` + // Coin specifies the cryptocurrency to be mined. + Coin string `json:"coin,omitempty"` + // Password is the pool password. + Password string `json:"password,omitempty"` + // UserPass is the username and password for the pool. + UserPass string `json:"userPass,omitempty"` + // Proxy is the address of a proxy to be used for the connection. + Proxy string `json:"proxy,omitempty"` + // Keepalive enables or disables the TCP keepalive feature. + Keepalive bool `json:"keepalive,omitempty"` + // Nicehash enables or disables Nicehash support. + Nicehash bool `json:"nicehash,omitempty"` + // RigID is the identifier of the mining rig. + RigID string `json:"rigId,omitempty"` + // TLSSingerprint is the TLS fingerprint of the pool. + TLSSingerprint string `json:"tlsFingerprint,omitempty"` + // Retries is the number of times to retry connecting to the pool. + Retries int `json:"retries,omitempty"` + // RetryPause is the pause in seconds between connection retries. + RetryPause int `json:"retryPause,omitempty"` + // UserAgent is the user agent string to be used for the connection. + UserAgent string `json:"userAgent,omitempty"` + // DonateLevel is the donation level to the miner developers. + DonateLevel int `json:"donateLevel,omitempty"` + // DonateOverProxy enables or disables donation over a proxy. + DonateOverProxy bool `json:"donateOverProxy,omitempty"` // CPU backend options - 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"` + // NoCPU disables the CPU backend. + NoCPU bool `json:"noCpu,omitempty"` + // CPUAffinity sets the CPU affinity for the miner. + CPUAffinity string `json:"cpuAffinity,omitempty"` + // AV is the algorithm variation. + AV int `json:"av,omitempty"` + // CPUPriority is the CPU priority for the miner. + CPUPriority int `json:"cpuPriority,omitempty"` + // CPUMaxThreadsHint is the maximum number of threads hint for the CPU. + CPUMaxThreadsHint int `json:"cpuMaxThreadsHint,omitempty"` + // CPUMemoryPool is the CPU memory pool size. + CPUMemoryPool int `json:"cpuMemoryPool,omitempty"` + // CPUNoYield enables or disables CPU yield. + CPUNoYield bool `json:"cpuNoYield,omitempty"` + // HugepageSize is the size of huge pages in kilobytes. + HugepageSize int `json:"hugepageSize,omitempty"` + // HugePagesJIT enables or disables huge pages for JIT compiled code. + HugePagesJIT bool `json:"hugePagesJIT,omitempty"` + // ASM enables or disables the ASM compiler. + ASM string `json:"asm,omitempty"` + // Argon2Impl is the Argon2 implementation. + Argon2Impl string `json:"argon2Impl,omitempty"` + // RandomXInit is the RandomX initialization value. + RandomXInit int `json:"randomXInit,omitempty"` + // RandomXNoNUMA enables or disables NUMA support for RandomX. + RandomXNoNUMA bool `json:"randomXNoNuma,omitempty"` + // RandomXMode is the RandomX mode. + RandomXMode string `json:"randomXMode,omitempty"` + // RandomX1GBPages enables or disables 1GB pages for RandomX. + RandomX1GBPages bool `json:"randomX1GBPages,omitempty"` + // RandomXWrmsr is the RandomX MSR value. + RandomXWrmsr string `json:"randomXWrmsr,omitempty"` + // RandomXNoRdmsr enables or disables MSR reading for RandomX. + RandomXNoRdmsr bool `json:"randomXNoRdmsr,omitempty"` + // RandomXCacheQoS enables or disables QoS for the RandomX cache. + RandomXCacheQoS bool `json:"randomXCacheQoS,omitempty"` // API options (can be overridden or supplemented here) - 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"` + // APIWorkerID is the worker ID for the API. + APIWorkerID string `json:"apiWorkerId,omitempty"` + // APIID is the ID for the API. + APIID string `json:"apiId,omitempty"` + // HTTPHost is the host for the HTTP API. + HTTPHost string `json:"httpHost,omitempty"` + // HTTPPort is the port for the HTTP API. + HTTPPort int `json:"httpPort,omitempty"` + // HTTPAccessToken is the access token for the HTTP API. + HTTPAccessToken string `json:"httpAccessToken,omitempty"` + // HTTPNoRestricted enables or disables restricted access to the HTTP API. + HTTPNoRestricted bool `json:"httpNoRestricted,omitempty"` // Logging options - 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"` // New field to control stdout/stderr logging + // Syslog enables or disables logging to the system log. + Syslog bool `json:"syslog,omitempty"` + // LogFile is the path to the log file. + LogFile string `json:"logFile,omitempty"` + // PrintTime is the interval in seconds for printing performance metrics. + PrintTime int `json:"printTime,omitempty"` + // HealthPrintTime is the interval in seconds for printing health metrics. + HealthPrintTime int `json:"healthPrintTime,omitempty"` + // NoColor disables color output in the logs. + NoColor bool `json:"noColor,omitempty"` + // Verbose enables verbose logging. + Verbose bool `json:"verbose,omitempty"` + // LogOutput enables or disables logging of stdout/stderr. + LogOutput bool `json:"logOutput,omitempty"` // Misc options - 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"` + // Background runs the miner in the background. + Background bool `json:"background,omitempty"` + // Title sets the title of the miner window. + Title string `json:"title,omitempty"` + // NoTitle disables the miner window title. + NoTitle bool `json:"noTitle,omitempty"` + // PauseOnBattery pauses the miner when the system is on battery power. + PauseOnBattery bool `json:"pauseOnBattery,omitempty"` + // PauseOnActive pauses the miner when the user is active. + PauseOnActive int `json:"pauseOnActive,omitempty"` + // Stress enables stress testing mode. + Stress bool `json:"stress,omitempty"` + // Bench enables benchmark mode. + Bench string `json:"bench,omitempty"` + // Submit enables or disables submitting shares. + Submit bool `json:"submit,omitempty"` + // Verify enables or disables share verification. + Verify string `json:"verify,omitempty"` + // Seed is the seed for the random number generator. + Seed string `json:"seed,omitempty"` + // Hash is the hash for the random number generator. + Hash string `json:"hash,omitempty"` + // NoDMI disables DMI/SMBIOS probing. + NoDMI bool `json:"noDMI,omitempty"` } -// PerformanceMetrics represents the performance metrics for a miner +// PerformanceMetrics represents the performance metrics for a miner. +// This struct provides a standardized way to report key performance indicators +// such as hashrate, shares, and uptime, allowing for consistent monitoring +// and comparison across different miners. 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"` + // Hashrate is the current hashrate of the miner in H/s. + Hashrate int `json:"hashrate"` + // Shares is the number of shares submitted by the miner. + Shares int `json:"shares"` + // Rejected is the number of rejected shares. + Rejected int `json:"rejected"` + // Uptime is the duration the miner has been running, in seconds. + Uptime int `json:"uptime"` + // LastShare is the timestamp of the last submitted share. + LastShare int64 `json:"lastShare"` + // Algorithm is the mining algorithm currently in use. + Algorithm string `json:"algorithm"` + // ExtraData provides a flexible way to include additional, miner-specific + // performance data that is not covered by the standard fields. ExtraData map[string]interface{} `json:"extraData,omitempty"` } -// History represents the history of a miner +// History represents the historical performance data for a miner. +// It contains a collection of performance metrics snapshots, allowing for +// the tracking of a miner's performance over time. type History struct { - Miner string `json:"miner"` - Stats []PerformanceMetrics `json:"stats"` - Updated int64 `json:"updated"` + // Miner is the name of the miner. + Miner string `json:"miner"` + // Stats is a slice of performance metrics, representing the historical data. + Stats []PerformanceMetrics `json:"stats"` + // Updated is the timestamp of the last update to the history. + Updated int64 `json:"updated"` } -// HashratePoint represents a single hashrate measurement at a specific time +// HashratePoint represents a single hashrate measurement at a specific time. +// This struct is used to build a time-series history of a miner's hashrate, +// which is essential for performance analysis and visualization. type HashratePoint struct { + // Timestamp is the time at which the hashrate was measured. Timestamp time.Time `json:"timestamp"` - Hashrate int `json:"hashrate"` + // Hashrate is the measured hashrate in H/s. + Hashrate int `json:"hashrate"` } -// XMRigMiner represents an XMRig miner +// XMRigMiner represents an XMRig miner, encapsulating its configuration, +// state, and operational details. This struct provides a comprehensive +// representation of an XMRig miner instance, including its identity, +// connection details, and performance history. type XMRigMiner struct { - Name string `json:"name"` - Version string `json:"version"` - URL string `json:"url"` - Path string `json:"path"` // This will now be the versioned folder path - MinerBinary string `json:"miner_binary"` // New field for the full path to the miner executable - Running bool `json:"running"` - LastHeartbeat int64 `json:"lastHeartbeat"` - ConfigPath string `json:"configPath"` - API *API `json:"api"` - mu sync.Mutex - cmd *exec.Cmd `json:"-"` - HashrateHistory []HashratePoint `json:"hashrateHistory"` // High-resolution (10s) - LowResHashrateHistory []HashratePoint `json:"lowResHashrateHistory"` // Low-resolution (1m averages) - LastLowResAggregation time.Time `json:"-"` // Timestamp of the last low-res aggregation + // Name is the name of the miner. + Name string `json:"name"` + // Version is the version of the XMRig miner. + Version string `json:"version"` + // URL is the download URL for the XMRig miner. + URL string `json:"url"` + // Path is the installation path of the miner. + Path string `json:"path"` + // MinerBinary is the full path to the miner's executable file. + MinerBinary string `json:"miner_binary"` + // Running indicates whether the miner is currently running. + Running bool `json:"running"` + // LastHeartbeat is the timestamp of the last heartbeat from the miner. + LastHeartbeat int64 `json:"lastHeartbeat"` + // ConfigPath is the path to the miner's configuration file. + ConfigPath string `json:"configPath"` + // API provides access to the miner's API for statistics and control. + API *API `json:"api"` + // mu is a mutex to protect against concurrent access to the miner's state. + mu sync.Mutex + // cmd is the command used to execute the miner process. + cmd *exec.Cmd `json:"-"` + // HashrateHistory is a slice of high-resolution hashrate data points. + HashrateHistory []HashratePoint `json:"hashrateHistory"` + // LowResHashrateHistory is a slice of low-resolution hashrate data points. + LowResHashrateHistory []HashratePoint `json:"lowResHashrateHistory"` + // LastLowResAggregation is the timestamp of the last low-resolution aggregation. + LastLowResAggregation time.Time `json:"-"` } -// API represents the XMRig API configuration +// API represents the XMRig API configuration. +// It specifies the details needed to connect to the miner's API, +// enabling programmatic monitoring and control. type API struct { - Enabled bool `json:"enabled"` + // Enabled indicates whether the API is enabled. + Enabled bool `json:"enabled"` + // ListenHost is the host on which the API is listening. ListenHost string `json:"listenHost"` - ListenPort int `json:"listenPort"` + // ListenPort is the port on which the API is listening. + ListenPort int `json:"listenPort"` } -// XMRigSummary represents the summary from the XMRig API +// XMRigSummary represents the summary of an XMRig miner's performance, +// as retrieved from its API. This struct provides a structured way to +// access key performance indicators from the miner's API. type XMRigSummary struct { + // Hashrate contains the hashrate data from the API. Hashrate struct { Total []float64 `json:"total"` } `json:"hashrate"` + // Results contains the share statistics from the API. Results struct { SharesGood uint64 `json:"shares_good"` SharesTotal uint64 `json:"shares_total"` } `json:"results"` - Uptime uint64 `json:"uptime"` + // Uptime is the duration the miner has been running, in seconds. + Uptime uint64 `json:"uptime"` + // Algorithm is the mining algorithm currently in use. Algorithm string `json:"algorithm"` } -// AvailableMiner represents a miner that is available to be started +// AvailableMiner represents a miner that is available for use. +// It provides a simple way to list and describe the miners that can be +// started and managed by the system. type AvailableMiner struct { - Name string `json:"name"` + // Name is the name of the available miner. + Name string `json:"name"` + // Description is a brief description of the miner. Description string `json:"description"` } diff --git a/pkg/mining/xmrig.go b/pkg/mining/xmrig.go index 9a524c6..d9763a5 100644 --- a/pkg/mining/xmrig.go +++ b/pkg/mining/xmrig.go @@ -26,10 +26,20 @@ var httpClient = &http.Client{ Timeout: 30 * time.Second, } -// NewXMRigMiner creates a new XMRig miner +// NewXMRigMiner creates a new XMRig miner instance with default settings. +// This is the entry point for creating a new XMRig miner that can be managed +// by the Manager. The returned miner is ready to be installed and started. +// +// Example: +// +// // Create a new XMRig miner +// xmrigMiner := mining.NewXMRigMiner() +// +// // Now you can use the miner to perform actions like +// // installing, starting, and stopping. func NewXMRigMiner() *XMRigMiner { return &XMRigMiner{ - Name: "xmrig", // Changed to lowercase for consistency + Name: "xmrig", Version: "latest", URL: "https://github.com/xmrig/xmrig/releases", API: &API{ @@ -43,28 +53,30 @@ func NewXMRigMiner() *XMRigMiner { } } -// GetName returns the name of the miner +// GetName returns the name of the miner. func (m *XMRigMiner) GetName() string { return m.Name } -// GetPath returns the path of the miner -// This now returns the base installation directory for xmrig, not the versioned one. +// GetPath returns the base installation directory for the XMRig miner. +// This is the directory where different versions of the miner are stored. func (m *XMRigMiner) GetPath() string { dataPath, err := xdg.DataFile("lethean-desktop/miners/xmrig") if err != nil { - // Fallback for safety, though it should ideally not fail if Install works. return "" } return dataPath } -// GetBinaryPath returns the full path to the miner executable. +// GetBinaryPath returns the full path to the miner's executable file. +// This path is set after a successful installation or check of the installation status. func (m *XMRigMiner) GetBinaryPath() string { return m.MinerBinary } -// GetLatestVersion returns the latest version of XMRig +// GetLatestVersion fetches the latest version of XMRig from the GitHub API. +// It returns the version string (e.g., "v6.18.0") or an error if the +// version could not be retrieved. func (m *XMRigMiner) GetLatestVersion() (string, error) { resp, err := httpClient.Get("https://api.github.com/repos/xmrig/xmrig/releases/latest") if err != nil { @@ -85,7 +97,9 @@ func (m *XMRigMiner) GetLatestVersion() (string, error) { return release.TagName, nil } -// Download and install the latest version of XMRig +// Install downloads and installs the latest version of the XMRig miner. +// It determines the correct release for the current operating system, +// downloads it, and extracts it to the appropriate installation directory. func (m *XMRigMiner) Install() error { version, err := m.GetLatestVersion() if err != nil { @@ -156,14 +170,17 @@ func (m *XMRigMiner) Install() error { return nil } -// Uninstall removes the miner files +// Uninstall removes all files related to the XMRig miner. +// This is a destructive operation that will remove the entire installation +// directory of the miner. func (m *XMRigMiner) Uninstall() error { - // Uninstall should remove the base path, which contains the versioned folder return os.RemoveAll(m.GetPath()) } -// findMinerBinary searches for the miner executable, first in the standard installation path, -// then falls back to the system's PATH. +// findMinerBinary searches for the miner's executable file. +// It first checks the standard installation path, and if not found, falls +// back to searching the system's PATH. This allows for both managed +// installations and pre-existing installations to be used. func (m *XMRigMiner) findMinerBinary() (string, error) { executableName := "xmrig" if runtime.GOOS == "windows" { @@ -198,7 +215,10 @@ func (m *XMRigMiner) findMinerBinary() (string, error) { return "", errors.New("miner executable not found in standard directory or system PATH") } -// CheckInstallation checks if the miner is installed and returns its details +// CheckInstallation verifies if the XMRig miner is installed correctly. +// It returns details about the installation, such as whether it is installed, +// its version, and the path to the executable. This method also updates the +// miner's internal state with the installation details. func (m *XMRigMiner) CheckInstallation() (*InstallationDetails, error) { details := &InstallationDetails{} @@ -236,7 +256,28 @@ func (m *XMRigMiner) CheckInstallation() (*InstallationDetails, error) { return details, nil } -// Start the miner +// Start launches the XMRig miner with the specified configuration. +// It creates a configuration file, constructs the necessary command-line +// arguments, and starts the miner process in the background. +// +// Example: +// +// // Create a new XMRig miner and a configuration +// xmrigMiner := mining.NewXMRigMiner() +// config := &mining.Config{ +// Pool: "your-pool-address", +// Wallet: "your-wallet-address", +// Threads: 4, +// } +// +// // Start the miner +// err := xmrigMiner.Start(config) +// if err != nil { +// log.Fatalf("Failed to start miner: %v", err) +// } +// +// // Stop the miner when you are done +// defer xmrigMiner.Stop() func (m *XMRigMiner) Start(config *Config) error { m.mu.Lock() defer m.mu.Unlock() @@ -500,7 +541,8 @@ func (m *XMRigMiner) Start(config *Config) error { return nil } -// Stop the miner +// Stop terminates the XMRig miner process. +// It sends a kill signal to the running miner process. func (m *XMRigMiner) Stop() error { m.mu.Lock() defer m.mu.Unlock() @@ -512,7 +554,9 @@ func (m *XMRigMiner) Stop() error { return m.cmd.Process.Kill() } -// GetStats returns the stats for the miner +// GetStats retrieves the performance statistics from the running XMRig miner. +// It queries the miner's API and returns a PerformanceMetrics struct +// containing the hashrate, share counts, and uptime. func (m *XMRigMiner) GetStats() (*PerformanceMetrics, error) { m.mu.Lock() running := m.Running @@ -552,6 +596,7 @@ func (m *XMRigMiner) GetStats() (*PerformanceMetrics, error) { } // GetHashrateHistory returns the combined high-resolution and low-resolution hashrate history. +// This provides a complete view of the miner's performance over time. func (m *XMRigMiner) GetHashrateHistory() []HashratePoint { m.mu.Lock() defer m.mu.Unlock() @@ -565,73 +610,69 @@ func (m *XMRigMiner) GetHashrateHistory() []HashratePoint { } // AddHashratePoint adds a new hashrate measurement to the high-resolution history. +// This method is called periodically by the Manager to record the miner's performance. func (m *XMRigMiner) AddHashratePoint(point HashratePoint) { m.mu.Lock() defer m.mu.Unlock() m.HashrateHistory = append(m.HashrateHistory, point) - // No trimming here; trimming is handled by ReduceHashrateHistory } -// ReduceHashrateHistory aggregates older high-resolution data into 1-minute averages -// and adds them to the low-resolution history. +// GetHighResHistoryLength returns the number of data points in the high-resolution hashrate history. func (m *XMRigMiner) GetHighResHistoryLength() int { m.mu.Lock() defer m.mu.Unlock() return len(m.HashrateHistory) } +// GetLowResHistoryLength returns the number of data points in the low-resolution hashrate history. func (m *XMRigMiner) GetLowResHistoryLength() int { m.mu.Lock() defer m.mu.Unlock() return len(m.LowResHashrateHistory) } +// ReduceHashrateHistory aggregates older high-resolution hashrate data into +// lower-resolution data, and trims the history to a manageable size. +// This method is called periodically by the Manager to maintain the hashrate +// history. func (m *XMRigMiner) ReduceHashrateHistory(now time.Time) { m.mu.Lock() defer m.mu.Unlock() // Only aggregate if enough time has passed since the last aggregation - // or if it's the first aggregation if !m.LastLowResAggregation.IsZero() && now.Sub(m.LastLowResAggregation) < LowResolutionInterval { return } - // Find points in HashrateHistory that are older than HighResolutionDuration - // These are the candidates for aggregation into low-resolution history. var pointsToAggregate []HashratePoint var newHighResHistory []HashratePoint // The cutoff is exclusive: points *at or before* this time are candidates for aggregation. // We want to aggregate points that are *strictly older* than HighResolutionDuration ago. - // So, if HighResolutionDuration is 5 minutes, points older than (now - 5 minutes) are aggregated. cutoff := now.Add(-HighResolutionDuration) for _, p := range m.HashrateHistory { - if p.Timestamp.Before(cutoff) { // Use Before to ensure strict older-than + if p.Timestamp.Before(cutoff) { pointsToAggregate = append(pointsToAggregate, p) } else { newHighResHistory = append(newHighResHistory, p) } } - m.HashrateHistory = newHighResHistory // Update high-res history to only contain recent points + m.HashrateHistory = newHighResHistory if len(pointsToAggregate) == 0 { - // If no points to aggregate, just update LastLowResAggregation and return m.LastLowResAggregation = now return } - // Aggregate into 1-minute slices - // Group points by minute (truncated timestamp) + // Group points by minute and calculate average hashrate minuteGroups := make(map[time.Time][]int) for _, p := range pointsToAggregate { - // Round timestamp down to the nearest minute for grouping minute := p.Timestamp.Truncate(LowResolutionInterval) minuteGroups[minute] = append(minuteGroups[minute], p.Hashrate) } - // Calculate average for each minute and add to low-res history var newLowResPoints []HashratePoint for minute, hashrates := range minuteGroups { if len(hashrates) > 0 { @@ -647,7 +688,6 @@ func (m *XMRigMiner) ReduceHashrateHistory(now time.Time) { } } - // Sort new low-res points by timestamp to maintain chronological order sort.Slice(newLowResPoints, func(i, j int) bool { return newLowResPoints[i].Timestamp.Before(newLowResPoints[j].Timestamp) }) @@ -656,15 +696,14 @@ func (m *XMRigMiner) ReduceHashrateHistory(now time.Time) { // Trim low-resolution history to LowResHistoryRetention lowResCutoff := now.Add(-LowResHistoryRetention) - // Find the first point that is *after* or equal to the lowResCutoff firstValidLowResIndex := 0 for i, p := range m.LowResHashrateHistory { if p.Timestamp.After(lowResCutoff) || p.Timestamp.Equal(lowResCutoff) { firstValidLowResIndex = i break } - if i == len(m.LowResHashrateHistory)-1 { // All points are older than cutoff - firstValidLowResIndex = len(m.LowResHashrateHistory) // Clear all + if i == len(m.LowResHashrateHistory)-1 { + firstValidLowResIndex = len(m.LowResHashrateHistory) } } m.LowResHashrateHistory = m.LowResHashrateHistory[firstValidLowResIndex:] @@ -672,6 +711,9 @@ func (m *XMRigMiner) ReduceHashrateHistory(now time.Time) { m.LastLowResAggregation = now } +// createConfig creates a JSON configuration file for the XMRig miner. +// This allows for a consistent and reproducible way to configure the miner, +// based on the provided Config struct. func (m *XMRigMiner) createConfig(config *Config) error { configPath, err := xdg.ConfigFile("lethean-desktop/xmrig.json") if err != nil { @@ -717,6 +759,8 @@ func (m *XMRigMiner) createConfig(config *Config) error { return os.WriteFile(m.ConfigPath, data, 0644) } +// unzip extracts a zip archive to a destination directory. +// This is a helper function used during the installation of the miner. func (m *XMRigMiner) unzip(src, dest string) error { r, err := zip.OpenReader(src) if err != nil { @@ -762,6 +806,8 @@ func (m *XMRigMiner) unzip(src, dest string) error { return nil } +// untar extracts a tar.gz archive to a destination directory. +// This is a helper function used during the installation of the miner. func (m *XMRigMiner) untar(src, dest string) error { file, err := os.Open(src) if err != nil {