Merge pull request #7 from Snider/add-docstrings-to-mining-package

Add comprehensive docstrings to the mining package
This commit is contained in:
Snider 2025-11-14 14:35:28 +00:00 committed by GitHub
commit d9dfb242d6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 448 additions and 162 deletions

View file

@ -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

View file

@ -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()
}

View file

@ -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"`
}

View file

@ -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 {