Align mining AX naming and comments
Some checks are pending
Security Scan / security (push) Waiting to run
Test / test (push) Waiting to run

This commit is contained in:
Virgil 2026-04-04 05:33:08 +00:00
parent c7f86cf5b9
commit 68c826a3d8
16 changed files with 134 additions and 136 deletions

View file

@ -15,8 +15,8 @@ import (
const installationCachePointerFileName = ".installed-miners"
// validateConfigPath("/home/alice/.config/lethean-desktop/miners/config.json") // nil
// validateConfigPath("/tmp/config.json") // rejects paths outside XDG_CONFIG_HOME.
// validateConfigPath("/home/alice/.config/lethean-desktop/miners/config.json") returns nil.
// validateConfigPath("/tmp/config.json") rejects paths outside XDG_CONFIG_HOME.
func validateConfigPath(configPath string) error {
expectedBase := filepath.Join(xdg.ConfigHome, "lethean-desktop")
@ -29,7 +29,7 @@ func validateConfigPath(configPath string) error {
return nil
}
// doctor refreshes the miner installation cache and refreshes the cached summary on disk.
// doctor refreshes the miner installation cache and writes the refreshed summary on disk.
var doctorCmd = &cobra.Command{
Use: "doctor",
Short: "Check and refresh the status of installed miners",
@ -42,7 +42,7 @@ var doctorCmd = &cobra.Command{
if err := updateDoctorCache(); err != nil {
return fmt.Errorf("failed to run doctor check: %w", err)
}
// loadAndDisplayCache() // prints the refreshed miner summary after updateDoctorCache().
// loadAndDisplayCache() prints the refreshed miner summary after updateDoctorCache().
_, err := loadAndDisplayCache()
return err
},
@ -55,10 +55,10 @@ func loadAndDisplayCache() (bool, error) {
}
signpostPath := filepath.Join(homeDir, installationCachePointerFileName)
// os.Stat(signpostPath) // returns os.ErrNotExist when no cache has been written yet.
// os.Stat(signpostPath) returns os.ErrNotExist when no cache has been written yet.
if _, err := os.Stat(signpostPath); os.IsNotExist(err) {
fmt.Println("No cached data found. Run 'install' for a miner first.")
return false, nil // loadAndDisplayCache() returns false until install() writes the first cache file.
return false, nil // loadAndDisplayCache returns false until install writes the first cache file.
}
configPathBytes, err := os.ReadFile(signpostPath)
@ -67,7 +67,7 @@ func loadAndDisplayCache() (bool, error) {
}
configPath := strings.TrimSpace(string(configPathBytes))
// validateConfigPath("/home/alice/.config/lethean-desktop/miners/config.json") // blocks path traversal outside XDG_CONFIG_HOME.
// validateConfigPath("/home/alice/.config/lethean-desktop/miners/config.json") blocks path traversal outside XDG_CONFIG_HOME.
if err := validateConfigPath(configPath); err != nil {
return false, fmt.Errorf("security error: %w", err)
}
@ -91,7 +91,7 @@ func loadAndDisplayCache() (bool, error) {
fmt.Println()
for _, details := range systemInfo.InstalledMinersInfo {
// details.Path = "/home/alice/.local/share/lethean-desktop/miners/xmrig" // maps to a friendly miner label like "XMRig".
// details.Path = "/home/alice/.local/share/lethean-desktop/miners/xmrig" maps to a friendly miner label like "XMRig".
var minerName string
if details.Path != "" {
if strings.Contains(details.Path, "xmrig") {

View file

@ -43,8 +43,7 @@ to exchange public keys and establish a secure connection.`,
return fmt.Errorf("failed to get peer registry: %w", err)
}
// For now, just add to registry - actual connection happens with 'node serve'
// In a full implementation, we'd connect here and get the peer's identity
// For now, just add to the registry. `node serve` performs the handshake when the peer connects.
peer := &node.Peer{
ID: fmt.Sprintf("pending-%d", time.Now().UnixNano()),
Name: name,
@ -152,7 +151,7 @@ var peerPingCmd = &cobra.Command{
}
fmt.Printf("Pinging %s (%s)...\n", peer.Name, peer.Address)
// TODO: Actually send ping via transport
// `node serve` performs the live ping over transport once the peer is connected.
fmt.Println("Ping functionality requires active connection via 'node serve'")
return nil
},
@ -213,21 +212,21 @@ ping latency, hop count, geographic distance, and reliability score.`,
func init() {
rootCmd.AddCommand(peerCmd)
// rootCmd.AddCommand(peerAddCmd) // exposes `peer add --address 10.0.0.2:9090 --name worker-1`
// rootCmd.AddCommand(peerAddCmd) exposes `peer add --address 10.0.0.2:9090 --name worker-1`.
peerCmd.AddCommand(peerAddCmd)
peerAddCmd.Flags().StringP("address", "a", "", "Peer address (host:port)")
peerAddCmd.Flags().StringP("name", "n", "", "Peer name")
// rootCmd.AddCommand(peerListCmd) // exposes `peer list`
// rootCmd.AddCommand(peerListCmd) exposes `peer list`.
peerCmd.AddCommand(peerListCmd)
// rootCmd.AddCommand(peerRemoveCmd) // exposes `peer remove <peer-id>`
// rootCmd.AddCommand(peerRemoveCmd) exposes `peer remove <peer-id>`.
peerCmd.AddCommand(peerRemoveCmd)
// rootCmd.AddCommand(peerPingCmd) // exposes `peer ping <peer-id>`
// rootCmd.AddCommand(peerPingCmd) exposes `peer ping <peer-id>`.
peerCmd.AddCommand(peerPingCmd)
// rootCmd.AddCommand(peerOptimalCmd) // exposes `peer optimal --count 4`
// rootCmd.AddCommand(peerOptimalCmd) exposes `peer optimal --count 4`.
peerCmd.AddCommand(peerOptimalCmd)
peerOptimalCmd.Flags().IntP("count", "c", 1, "Number of optimal peers to show")
}

View file

@ -11,10 +11,10 @@ import (
)
var (
controller *node.Controller
transport *node.Transport
controllerOnce sync.Once
controllerErr error
remoteController *node.Controller
peerTransport *node.Transport
remoteControllerOnce sync.Once
remoteControllerErr error
)
// remote status, remote start, remote stop, remote logs, remote connect, remote disconnect, and remote ping live under this command group.
@ -24,13 +24,13 @@ var remoteCmd = &cobra.Command{
Long: `Send commands to remote worker nodes and retrieve their status.`,
}
// remote status a1b2c3d4e5f6 prints stats for one peer, while remote status prints the whole fleet.
// remote status a1b2c3d4e5f6 prints stats for one peer, while `remote status` prints the whole fleet.
var remoteStatusCmd = &cobra.Command{
Use: "status [peer-id]",
Short: "Get mining status from remote peers",
Long: `Display mining statistics from all connected peers or a specific peer.`,
RunE: func(cmd *cobra.Command, args []string) error {
controller, err := getController()
remoteController, err := getController()
if err != nil {
return err
}
@ -43,7 +43,7 @@ var remoteStatusCmd = &cobra.Command{
return fmt.Errorf("peer not found: %s", peerID)
}
stats, err := controller.GetRemoteStats(peer.ID)
stats, err := remoteController.GetRemoteStats(peer.ID)
if err != nil {
return fmt.Errorf("failed to get stats: %w", err)
}
@ -51,7 +51,7 @@ var remoteStatusCmd = &cobra.Command{
printPeerStats(peer, stats)
} else {
// Get stats from all peers
allStats := controller.GetAllStats()
allStats := remoteController.GetAllStats()
if len(allStats) == 0 {
fmt.Println("No connected peers.")
return nil
@ -87,7 +87,7 @@ var remoteStartCmd = &cobra.Command{
RunE: func(cmd *cobra.Command, args []string) error {
minerType, _ := cmd.Flags().GetString("type")
if minerType == "" {
return fmt.Errorf("--type is required (e.g., xmrig, tt-miner)")
return fmt.Errorf("--type is required, for example `xmrig` or `tt-miner`")
}
profileID, _ := cmd.Flags().GetString("profile")
@ -97,13 +97,13 @@ var remoteStartCmd = &cobra.Command{
return fmt.Errorf("peer not found: %s", peerID)
}
controller, err := getController()
remoteController, err := getController()
if err != nil {
return err
}
fmt.Printf("Starting %s miner on %s with profile %s...\n", minerType, peer.Name, profileID)
if err := controller.StartRemoteMiner(peer.ID, minerType, profileID, nil); err != nil {
if err := remoteController.StartRemoteMiner(peer.ID, minerType, profileID, nil); err != nil {
return fmt.Errorf("failed to start miner: %w", err)
}
@ -136,13 +136,13 @@ var remoteStopCmd = &cobra.Command{
return fmt.Errorf("miner name required (as argument or --miner flag)")
}
controller, err := getController()
remoteController, err := getController()
if err != nil {
return err
}
fmt.Printf("Stopping miner %s on %s...\n", minerName, peer.Name)
if err := controller.StopRemoteMiner(peer.ID, minerName); err != nil {
if err := remoteController.StopRemoteMiner(peer.ID, minerName); err != nil {
return fmt.Errorf("failed to stop miner: %w", err)
}
@ -167,12 +167,12 @@ var remoteLogsCmd = &cobra.Command{
return fmt.Errorf("peer not found: %s", peerID)
}
controller, err := getController()
remoteController, err := getController()
if err != nil {
return err
}
logLines, err := controller.GetRemoteLogs(peer.ID, minerName, lines)
logLines, err := remoteController.GetRemoteLogs(peer.ID, minerName, lines)
if err != nil {
return fmt.Errorf("failed to get logs: %w", err)
}
@ -200,13 +200,13 @@ var remoteConnectCmd = &cobra.Command{
return fmt.Errorf("peer not found: %s", peerID)
}
controller, err := getController()
remoteController, err := getController()
if err != nil {
return err
}
fmt.Printf("Connecting to %s at %s...\n", peer.Name, peer.Address)
if err := controller.ConnectToPeer(peer.ID); err != nil {
if err := remoteController.ConnectToPeer(peer.ID); err != nil {
return fmt.Errorf("failed to connect: %w", err)
}
@ -228,13 +228,13 @@ var remoteDisconnectCmd = &cobra.Command{
return fmt.Errorf("peer not found: %s", peerID)
}
controller, err := getController()
remoteController, err := getController()
if err != nil {
return err
}
fmt.Printf("Disconnecting from %s...\n", peer.Name)
if err := controller.DisconnectFromPeer(peer.ID); err != nil {
if err := remoteController.DisconnectFromPeer(peer.ID); err != nil {
return fmt.Errorf("failed to disconnect: %w", err)
}
@ -258,7 +258,7 @@ var remotePingCmd = &cobra.Command{
return fmt.Errorf("peer not found: %s", peerID)
}
controller, err := getController()
remoteController, err := getController()
if err != nil {
return err
}
@ -269,7 +269,7 @@ var remotePingCmd = &cobra.Command{
var successfulPings int
for i := 0; i < count; i++ {
rtt, err := controller.PingPeer(peer.ID)
rtt, err := remoteController.PingPeer(peer.ID)
if err != nil {
fmt.Printf(" Ping %d: timeout\n", i+1)
continue
@ -302,7 +302,7 @@ func init() {
// remoteCmd.AddCommand(remoteStartCmd) // exposes `remote start <peer-id> --type xmrig --profile default`
remoteCmd.AddCommand(remoteStartCmd)
remoteStartCmd.Flags().StringP("profile", "p", "", "Profile ID to use for starting the miner")
remoteStartCmd.Flags().StringP("type", "t", "", "Miner type (e.g., xmrig, tt-miner)")
remoteStartCmd.Flags().StringP("type", "t", "", "Miner type, for example xmrig or tt-miner")
// remoteCmd.AddCommand(remoteStopCmd) // exposes `remote stop <peer-id> --miner xmrig-1`
remoteCmd.AddCommand(remoteStopCmd)
@ -325,46 +325,45 @@ func init() {
// getController returns or creates the controller instance (thread-safe).
func getController() (*node.Controller, error) {
controllerOnce.Do(func() {
remoteControllerOnce.Do(func() {
nodeManager, err := getNodeManager()
if err != nil {
controllerErr = fmt.Errorf("failed to get node manager: %w", err)
remoteControllerErr = fmt.Errorf("failed to get node manager: %w", err)
return
}
if !nodeManager.HasIdentity() {
controllerErr = fmt.Errorf("no node identity found. Run 'node init' first")
remoteControllerErr = fmt.Errorf("no node identity found. Run `node init` first")
return
}
peerRegistry, err := getPeerRegistry()
if err != nil {
controllerErr = fmt.Errorf("failed to get peer registry: %w", err)
remoteControllerErr = fmt.Errorf("failed to get peer registry: %w", err)
return
}
// node.DefaultTransportConfig() // provides the transport settings used by `remote` commands
config := node.DefaultTransportConfig()
transport = node.NewTransport(nodeManager, peerRegistry, config)
controller = node.NewController(nodeManager, peerRegistry, transport)
transportConfig := node.DefaultTransportConfig()
peerTransport = node.NewTransport(nodeManager, peerRegistry, transportConfig)
remoteController = node.NewController(nodeManager, peerRegistry, peerTransport)
})
return controller, controllerErr
return remoteController, remoteControllerErr
}
// findPeerByPartialID("a1b2c3") // returns the matching peer by full or partial ID.
// findPeerByPartialID("a1b2c3") returns the peer whose ID starts with `a1b2c3`.
func findPeerByPartialID(partialID string) *node.Peer {
peerRegistry, err := getPeerRegistry()
if err != nil {
return nil
}
// peerRegistry.GetPeer(partialID) // exact match first
// peerRegistry.GetPeer(partialID) tries the exact peer ID first.
peer := peerRegistry.GetPeer(partialID)
if peer != nil {
return peer
}
// peerRegistry.ListPeers() // then fall back to partial ID or exact name matches
// peerRegistry.ListPeers() falls back to partial IDs such as `a1b2c3`.
for _, p := range peerRegistry.ListPeers() {
if strings.HasPrefix(p.ID, partialID) {
return p
@ -378,7 +377,7 @@ func findPeerByPartialID(partialID string) *node.Peer {
return nil
}
// printPeerStats(peer, stats) // formats the remote stats output for `remote status`.
// printPeerStats(peer, stats) formats the remote stats output for `remote status`.
func printPeerStats(peer *node.Peer, stats *node.StatsPayload) {
fmt.Printf("\n%s (%s)\n", peer.Name, peer.ID[:16])
fmt.Printf(" Address: %s\n", peer.Address)

View file

@ -42,17 +42,17 @@ var serveCmd = &cobra.Command{
displayAddr := fmt.Sprintf("%s:%d", displayHost, port)
listenAddr := fmt.Sprintf("%s:%d", host, port)
// manager := getServiceManager() // `mining serve` and `mining start` share the same manager instance.
manager := getServiceManager()
// miningManager := getServiceManager() shares the same miner lifecycle state across CLI commands.
miningManager := getServiceManager()
service, err := mining.NewService(manager, listenAddr, displayAddr, namespace)
miningService, err := mining.NewService(miningManager, listenAddr, displayAddr, namespace)
if err != nil {
return fmt.Errorf("failed to create new service: %w", err)
}
// service.ServiceStartup(ctx) // starts the HTTP server on 127.0.0.1:9090 while the shell keeps reading stdin.
// miningService.ServiceStartup(ctx) starts the HTTP server while the shell keeps reading stdin.
go func() {
if err := service.ServiceStartup(ctx); err != nil {
if err := miningService.ServiceStartup(ctx); err != nil {
fmt.Fprintf(os.Stderr, "Failed to start service: %v\n", err)
cancel()
}
@ -65,7 +65,7 @@ var serveCmd = &cobra.Command{
// go func() { fmt.Print(">> ") } // keeps the interactive shell responsive while the API serves requests.
go func() {
fmt.Printf("Mining service started on http://%s:%d\n", displayHost, port)
fmt.Printf("Swagger documentation is available at http://%s:%d%s/index.html\n", displayHost, port, service.SwaggerUIPath)
fmt.Printf("Swagger documentation is available at http://%s:%d%s/index.html\n", displayHost, port, miningService.SwaggerUIPath)
fmt.Println("Entering interactive shell. Type 'exit' or 'quit' to stop.")
fmt.Print(">> ")
@ -101,7 +101,7 @@ var serveCmd = &cobra.Command{
poolURL := commandArgs[1]
walletAddress := commandArgs[2]
// poolURL := "stratum+tcp://pool.example.com:3333" // required scheme for the miner configuration.
// poolURL := "stratum+tcp://pool.example.com:3333" keeps the miner configuration valid.
if !strings.HasPrefix(poolURL, "stratum+tcp://") &&
!strings.HasPrefix(poolURL, "stratum+ssl://") &&
!strings.HasPrefix(poolURL, "stratum://") {
@ -115,7 +115,7 @@ var serveCmd = &cobra.Command{
continue
}
// walletAddress := "44Affq5kSiGBoZ..." // keeps the wallet field within the 256-character limit.
// walletAddress := "44Affq5kSiGBoZ..." keeps the wallet field within the 256-character limit.
if len(walletAddress) > 256 {
fmt.Fprintf(os.Stderr, "Error: Wallet address too long (max 256 chars)\n")
fmt.Print(">> ")
@ -135,7 +135,7 @@ var serveCmd = &cobra.Command{
continue
}
miner, err := manager.StartMiner(context.Background(), minerType, config)
miner, err := miningManager.StartMiner(context.Background(), minerType, config)
if err != nil {
fmt.Fprintf(os.Stderr, "Error starting miner: %v\n", err)
} else {
@ -144,10 +144,10 @@ var serveCmd = &cobra.Command{
}
case "status":
if len(commandArgs) < 1 {
fmt.Println("Error: status command requires miner name (e.g., 'status xmrig')")
fmt.Println("Error: status command requires miner name, for example `status xmrig`")
} else {
minerName := commandArgs[0]
miner, err := manager.GetMiner(minerName)
miner, err := miningManager.GetMiner(minerName)
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting miner status: %v\n", err)
} else {
@ -166,10 +166,10 @@ var serveCmd = &cobra.Command{
}
case "stop":
if len(commandArgs) < 1 {
fmt.Println("Error: stop command requires miner name (e.g., 'stop xmrig')")
fmt.Println("Error: stop command requires miner name, for example `stop xmrig`")
} else {
minerName := commandArgs[0]
err := manager.StopMiner(context.Background(), minerName)
err := miningManager.StopMiner(context.Background(), minerName)
if err != nil {
fmt.Fprintf(os.Stderr, "Error stopping miner: %v\n", err)
} else {
@ -177,7 +177,7 @@ var serveCmd = &cobra.Command{
}
}
case "list":
miners := manager.ListMiners()
miners := miningManager.ListMiners()
if len(miners) == 0 {
fmt.Println("No miners currently running.")
} else {
@ -188,12 +188,12 @@ var serveCmd = &cobra.Command{
}
default:
fmt.Fprintf(os.Stderr, "Unknown command: %s. Only 'start', 'status', 'stop', 'list' are directly supported in this shell.\n", command)
fmt.Fprintf(os.Stderr, "For other commands, please run them directly from your terminal (e.g., 'mining doctor').\n")
fmt.Fprintf(os.Stderr, "For other commands, please run them directly from your terminal, for example `mining doctor`.\n")
}
fmt.Print(">> ")
}
// scanner.Err() // reports stdin failures such as a closed terminal.
// scanner.Err() reports stdin failures such as a closed terminal.
if err := scanner.Err(); err != nil {
fmt.Fprintf(os.Stderr, "Error reading input: %v\n", err)
}
@ -206,8 +206,8 @@ var serveCmd = &cobra.Command{
case <-ctx.Done():
}
// manager.Stop() // stops miner goroutines and closes the shared manager before exit.
manager.Stop()
// miningManager.Stop() stops miner goroutines and closes the shared manager before exit.
miningManager.Stop()
fmt.Println("Mining service stopped.")
return nil

View file

@ -17,9 +17,9 @@ var statusCmd = &cobra.Command{
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
minerName := args[0]
manager := getServiceManager()
miningManager := getServiceManager()
miner, err := manager.GetMiner(minerName)
miner, err := miningManager.GetMiner(minerName)
if err != nil {
return fmt.Errorf("failed to get miner: %w", err)
}

View file

@ -6,48 +6,49 @@ import (
"sync"
)
// buf := bufferPool.Get().(*bytes.Buffer); buf.Reset(); defer bufferPool.Put(buf)
var bufferPool = sync.Pool{
New: func() interface{} {
// buffer := jsonBufferPool.Get().(*bytes.Buffer)
// buffer.Reset()
// defer jsonBufferPool.Put(buffer)
var jsonBufferPool = sync.Pool{
New: func() any {
return bytes.NewBuffer(make([]byte, 0, 1024))
},
}
// buf := getBuffer()
// defer putBuffer(buf)
func getBuffer() *bytes.Buffer {
buf := bufferPool.Get().(*bytes.Buffer)
buf.Reset()
return buf
// buffer := acquireJSONBuffer()
// defer releaseJSONBuffer(buffer)
func acquireJSONBuffer() *bytes.Buffer {
buffer := jsonBufferPool.Get().(*bytes.Buffer)
buffer.Reset()
return buffer
}
// defer putBuffer(buf) // return buf after use; buffers >64KB are discarded
func putBuffer(buf *bytes.Buffer) {
// Don't pool buffers that grew too large (>64KB)
if buf.Cap() <= 65536 {
bufferPool.Put(buf)
// releaseJSONBuffer(buffer) returns the buffer to the pool when it stays under 64 KB.
func releaseJSONBuffer(buffer *bytes.Buffer) {
if buffer.Cap() <= 65536 {
jsonBufferPool.Put(buffer)
}
}
// UnmarshalJSON(data, &msg)
// UnmarshalJSON(data, &message)
func UnmarshalJSON(data []byte, target interface{}) error {
return json.Unmarshal(data, target)
}
// data, err := MarshalJSON(stats) // pooled buffer; safe to hold after call
// data, err := MarshalJSON(stats) // safe to keep after the call returns.
func MarshalJSON(value interface{}) ([]byte, error) {
buf := getBuffer()
defer putBuffer(buf)
buffer := acquireJSONBuffer()
defer releaseJSONBuffer(buffer)
encoder := json.NewEncoder(buf)
// Don't escape HTML characters (matches json.Marshal behavior for these use cases)
encoder := json.NewEncoder(buffer)
// Keep characters like < and > unchanged so API responses match json.Marshal.
encoder.SetEscapeHTML(false)
if err := encoder.Encode(value); err != nil {
return nil, err
}
// json.Encoder.Encode adds a newline; remove it to match json.Marshal
data := buf.Bytes()
// json.Encoder.Encode adds a newline; trim it so callers get compact JSON.
data := buffer.Bytes()
if len(data) > 0 && data[len(data)-1] == '\n' {
data = data[:len(data)-1]
}

View file

@ -13,20 +13,20 @@ import (
"forge.lthn.ai/Snider/Mining/pkg/logging"
)
// equalFold("xmrig", "XMRig") // true
// equalFold("tt-miner", "TT-Miner") // true
// equalFold("xmrig", "XMRig") returns true.
// equalFold("tt-miner", "TT-Miner") returns true.
func equalFold(left, right string) bool {
return bytes.EqualFold([]byte(left), []byte(right))
}
// hasPrefix("xmrig-rx0", "xmrig") // true
// hasPrefix("ttminer-rtx", "xmrig") // false
// hasPrefix("xmrig-rx0", "xmrig") returns true.
// hasPrefix("ttminer-rtx", "xmrig") returns false.
func hasPrefix(input, prefix string) bool {
return len(input) >= len(prefix) && input[:len(prefix)] == prefix
}
// containsStr("peer not found", "not found") // true
// containsStr("connection ok", "not found") // false
// containsStr("peer not found", "not found") returns true.
// containsStr("connection ok", "not found") returns false.
func containsStr(haystack, needle string) bool {
if len(needle) == 0 {
return true
@ -42,7 +42,7 @@ func containsStr(haystack, needle string) bool {
return false
}
// safeName := instanceNameRegex.ReplaceAllString("my algo!", "_") // "my_algo_"
// safeName := instanceNameRegex.ReplaceAllString("my algo!", "_") // `my_algo_`
var instanceNameRegex = regexp.MustCompile(`[^a-zA-Z0-9_/-]`)
// var managerInterface ManagerInterface = mining.NewManager()
@ -122,7 +122,7 @@ func NewManagerForSimulation() *Manager {
return manager
}
// manager.initDatabase() // miners.json with Database.Enabled=true enables database.Initialize(database.Config{Enabled: true, RetentionDays: 30})
// manager.initDatabase() loads `miners.json` and enables database persistence when `Database.Enabled` is true.
func (manager *Manager) initDatabase() {
minersConfiguration, err := LoadMinersConfig()
if err != nil {
@ -154,11 +154,11 @@ func (manager *Manager) initDatabase() {
logging.Info("database persistence enabled", logging.Fields{"retention_days": manager.databaseRetention})
// manager.startDBCleanup() // database.Cleanup(30) runs once per hour after NewManager() enables persistence
// manager.startDBCleanup() runs once per hour after NewManager enables persistence.
manager.startDBCleanup()
}
// manager.startDBCleanup() // with manager.databaseRetention=30, database.Cleanup(30) runs every hour
// manager.startDBCleanup() runs `database.Cleanup(manager.databaseRetention)` on startup and every hour.
func (manager *Manager) startDBCleanup() {
manager.waitGroup.Add(1)
go func() {
@ -168,18 +168,18 @@ func (manager *Manager) startDBCleanup() {
logging.Error("panic in database cleanup goroutine", logging.Fields{"panic": r})
}
}()
// ticker := time.NewTicker(time.Hour) // checks for expired rows every 60 minutes
ticker := time.NewTicker(time.Hour)
defer ticker.Stop()
// cleanupTicker := time.NewTicker(time.Hour) checks for expired rows every 60 minutes.
cleanupTicker := time.NewTicker(time.Hour)
defer cleanupTicker.Stop()
// database.Cleanup(30) // removes rows older than 30 days during startup
// database.Cleanup(manager.databaseRetention) removes rows older than the configured retention period during startup.
if err := database.Cleanup(manager.databaseRetention); err != nil {
logging.Warn("database cleanup failed", logging.Fields{"error": err})
}
for {
select {
case <-ticker.C:
case <-cleanupTicker.C:
if err := database.Cleanup(manager.databaseRetention); err != nil {
logging.Warn("database cleanup failed", logging.Fields{"error": err})
}
@ -190,7 +190,7 @@ func (manager *Manager) startDBCleanup() {
}()
}
// manager.syncMinersConfig() // miners.json with only "tt-miner" adds MinerAutostartConfig{MinerType: "xmrig", Autostart: false}
// manager.syncMinersConfig() adds missing entries such as `MinerAutostartConfig{MinerType: "xmrig", Autostart: false}`.
func (manager *Manager) syncMinersConfig() {
minersConfiguration, err := LoadMinersConfig()
if err != nil {
@ -227,7 +227,7 @@ func (manager *Manager) syncMinersConfig() {
}
}
// manager.autostartMiners() // miners.json with Autostart=true starts xmrig from context.Background()
// manager.autostartMiners() starts entries with `Autostart: true` from `context.Background()`.
func (manager *Manager) autostartMiners() {
minersConfiguration, err := LoadMinersConfig()
if err != nil {

View file

@ -94,7 +94,7 @@ func (logBuffer *LogBuffer) Clear() {
// func NewXMRigMiner() *XMRigMiner { return &XMRigMiner{BaseMiner: BaseMiner{MinerType: "xmrig"}} }
type BaseMiner struct {
Name string `json:"name"`
MinerType string `json:"miner_type"` // Type identifier (e.g., "xmrig", "tt-miner")
MinerType string `json:"miner_type"` // Type identifier such as `xmrig` or `tt-miner`.
Version string `json:"version"`
URL string `json:"url"`
Path string `json:"path"`
@ -112,19 +112,19 @@ type BaseMiner struct {
LogBuffer *LogBuffer `json:"-"`
}
// minerType := miner.GetType() // "xmrig" or "tt-miner"
// minerType := miner.GetType() // returns values such as `xmrig` or `tt-miner`.
func (b *BaseMiner) GetType() string {
return b.MinerType
}
// name := miner.GetName() // e.g. "xmrig-randomx" or "tt-miner-kawpow"
// name := miner.GetName() // returns values such as `xmrig-randomx` or `tt-miner-kawpow`.
func (b *BaseMiner) GetName() string {
b.mutex.RLock()
defer b.mutex.RUnlock()
return b.Name
}
// path := miner.GetPath() // e.g. "/home/user/.local/share/lethean-desktop/miners/xmrig"
// path := miner.GetPath() // returns paths such as `/home/alice/.local/share/lethean-desktop/miners/xmrig`.
func (b *BaseMiner) GetPath() string {
dataPath, err := xdg.DataFile("lethean-desktop/miners/" + b.ExecutableName)
if err != nil {
@ -137,7 +137,7 @@ func (b *BaseMiner) GetPath() string {
return dataPath
}
// binary := miner.GetBinaryPath() // e.g. "/home/user/.local/share/lethean-desktop/miners/xmrig/xmrig"
// binary := miner.GetBinaryPath() // returns paths such as `/home/alice/.local/share/lethean-desktop/miners/xmrig/xmrig`.
func (b *BaseMiner) GetBinaryPath() string {
b.mutex.RLock()
defer b.mutex.RUnlock()
@ -346,7 +346,7 @@ func (b *BaseMiner) findMinerBinary() (string, error) {
if err == nil {
for _, entry := range dirs {
if entry.IsDir() && strings.HasPrefix(entry.Name(), b.ExecutableName+"-") {
// Extract version string, e.g., "xmrig-6.24.0" -> "6.24.0"
// Extract the version suffix from a directory name such as `xmrig-6.24.0`.
versionStr := strings.TrimPrefix(entry.Name(), b.ExecutableName+"-")
currentVersion := parseVersion(versionStr)

View file

@ -135,11 +135,11 @@ type Config struct {
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")
GPUAlgo string `json:"gpuAlgo,omitempty"` // GPU algorithm such as `kawpow` or `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")
Devices string `json:"devices,omitempty"` // GPU device selection such as `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

View file

@ -1,20 +1,19 @@
package mining
// var raw RawConfig
// _ = json.Unmarshal([]byte(`{"pool":"pool.lthn.io:3333"}`), &raw)
// var rawConfig RawConfig
// _ = json.Unmarshal([]byte(`{"pool":"pool.lthn.io:3333"}`), &rawConfig)
type RawConfig []byte
// profile := MiningProfile{ID: "abc", Name: "LTHN RandomX", MinerType: "xmrig", Config: raw}
// profile := MiningProfile{ID: "abc", Name: "LTHN RandomX", MinerType: "xmrig", Config: rawConfig}
// manager.SaveProfile(profile)
type MiningProfile struct {
ID string `json:"id"`
Name string `json:"name"`
MinerType string `json:"minerType"` // e.g., "xmrig", "ttminer"
Config RawConfig `json:"config" swaggertype:"object"` // The raw JSON config for the specific miner
MinerType string `json:"minerType"` // Miner type such as `xmrig` or `ttminer`.
Config RawConfig `json:"config" swaggertype:"object"` // Raw JSON config for the selected miner profile.
}
// data, err := profile.Config.MarshalJSON()
// data, err := profile.Config.MarshalJSON() // returns the config blob unchanged.
func (m RawConfig) MarshalJSON() ([]byte, error) {
if m == nil {
return []byte("null"), nil
@ -22,7 +21,7 @@ func (m RawConfig) MarshalJSON() ([]byte, error) {
return m, nil
}
// if err := json.Unmarshal(raw, &profile.Config); err != nil { ... }
// if err := json.Unmarshal(rawConfig, &profile.Config); err != nil { ... }
func (m *RawConfig) UnmarshalJSON(data []byte) error {
if m == nil {
return ErrInternal("RawConfig: UnmarshalJSON on nil pointer")

View file

@ -8,7 +8,7 @@ import (
"github.com/gin-gonic/gin"
)
// limiter := NewRateLimiter(10, 20) // 10 req/s, burst 20
// limiter := NewRateLimiter(10, 20) // 10 requests per second with a burst of 20.
// router.Use(limiter.Middleware())
type RateLimiter struct {
requestsPerSecond int

View file

@ -34,7 +34,7 @@ import (
// service, err := mining.NewService(manager, "127.0.0.1:9090", "localhost:9090", "/api/v1/mining")
// if err != nil { return err }
// service.ServiceStartup(context.Background()) // starts the REST API on 127.0.0.1:9090.
// service.ServiceStartup(context.Background()) starts the REST API on 127.0.0.1:9090.
type Service struct {
Manager ManagerInterface
ProfileManager *ProfileManager
@ -1089,14 +1089,14 @@ func (service *Service) handleGetMinerLogs(c *gin.Context) {
c.JSON(http.StatusOK, encodedLogs)
}
// c.ShouldBindJSON(&StdinInput{Input: "h"}) // "h" for hashrate, "p" for pause
// c.ShouldBindJSON(&StdinInput{Input: "h"}) // `h` prints hash rate and `p` pauses mining.
type StdinInput struct {
Input string `json:"input" binding:"required"`
}
// handleMinerStdin godoc
// @Summary Send input to miner stdin
// @Description Send console commands to a running miner's stdin (e.g., 'h' for hashrate, 'p' for pause)
// @Description Send console commands to a running miner's stdin. Use `h` to print hash rate and `p` to pause mining.
// @Tags miners
// @Accept json
// @Produce json

View file

@ -44,8 +44,8 @@ type SimulatedMiner struct {
// SimulatedMinerConfig{Name: "sim-001", Algorithm: "rx/0", BaseHashrate: 5000, Variance: 0.1}
type SimulatedMinerConfig struct {
Name string // Miner instance name (e.g., "sim-xmrig-001")
Algorithm string // Algorithm name (e.g., "rx/0", "kawpow", "ethash")
Name string // Miner instance name such as `sim-xmrig-001`.
Algorithm string // Algorithm name such as `rx/0`, `kawpow`, or `ethash`.
BaseHashrate int // Base hashrate in H/s
Variance float64 // Variance as percentage (0.0-0.2 for 20% variance)
PoolName string // Simulated pool name

View file

@ -191,7 +191,7 @@ func isValidCLIArg(arg string) bool {
// Must start with dash (standard CLI argument format)
// This is an allowlist approach - only accept valid argument patterns
if !strings.HasPrefix(arg, "-") {
// Allow values for flags (e.g., the "3" in "-i 3")
// Allow flag values such as the `3` in `-i 3`.
// Values must not contain shell metacharacters
return isValidArgValue(arg)
}

View file

@ -79,7 +79,7 @@ func NewXMRigMiner() *XMRigMiner {
var getXMRigConfigPath = func(instanceName string) (string, error) {
configFileName := "xmrig.json"
if instanceName != "" && instanceName != "xmrig" {
// Use instance-specific config file (e.g., xmrig-78.json)
// Use an instance-specific config file such as `xmrig-78.json`.
configFileName = instanceName + ".json"
}

View file

@ -266,7 +266,7 @@ func (m *XMRigMiner) createConfig(config *Config) error {
"enabled": config.GPUEnabled && config.OpenCL && config.Devices != "",
}
if config.GPUEnabled && config.OpenCL && config.Devices != "" {
// User must explicitly specify devices (e.g., "0" or "0,1")
// User must explicitly specify devices such as `0` or `0,1`.
openclConfig["devices"] = config.Devices
if config.GPUIntensity > 0 {
openclConfig["intensity"] = config.GPUIntensity
@ -282,7 +282,7 @@ func (m *XMRigMiner) createConfig(config *Config) error {
"enabled": config.GPUEnabled && config.CUDA && config.Devices != "",
}
if config.GPUEnabled && config.CUDA && config.Devices != "" {
// User must explicitly specify devices (e.g., "0" or "0,1")
// User must explicitly specify devices such as `0` or `0,1`.
cudaConfig["devices"] = config.Devices
if config.GPUIntensity > 0 {
cudaConfig["intensity"] = config.GPUIntensity