csminer/miner.go

267 lines
7.8 KiB
Go
Raw Normal View History

2020-06-20 01:21:08 +03:00
// Copyright 2020 cryptonote.social. All rights reserved. Use of this source code is governed by
// the license found in the LICENSE file.
package csminer
import (
"bufio"
2020-08-17 02:31:54 +03:00
"errors"
2020-06-20 01:21:08 +03:00
"os"
"strconv"
"time"
2020-08-01 18:16:13 +03:00
"github.com/cryptonote-social/csminer/crylog"
2020-08-17 02:31:54 +03:00
"github.com/cryptonote-social/csminer/minerlib"
2020-08-01 18:16:13 +03:00
"github.com/cryptonote-social/csminer/stratum/client"
2020-06-20 01:21:08 +03:00
)
2020-08-17 02:31:54 +03:00
var ()
2020-06-20 01:21:08 +03:00
const (
2020-08-21 06:13:30 +03:00
// Valid machine state changes
SCREEN_IDLE = 0
SCREEN_ACTIVE = 1
BATTERY_POWER = 2
AC_POWER = 3
2020-06-20 01:21:08 +03:00
)
2020-08-21 06:13:30 +03:00
type MachineState int
2020-08-21 06:13:30 +03:00
type MachineStater interface {
// Returns a channel that produces events when screen state & power state of the machine
// changes.
GetMachineStateChannel(saver bool) (chan MachineState, error)
2020-06-20 01:21:08 +03:00
}
type MinerConfig struct {
2020-08-21 06:13:30 +03:00
MachineStater MachineStater
Threads int
Username, RigID string
Wallet string
Agent string
Saver bool
ExcludeHrStart, ExcludeHrEnd int
UseTLS bool
AdvancedConfig string
}
func Mine(c *MinerConfig) error {
2020-08-17 02:31:54 +03:00
imResp := minerlib.InitMiner(&minerlib.InitMinerArgs{
2020-08-20 07:19:23 +03:00
Threads: c.Threads,
2020-08-17 02:31:54 +03:00
ExcludeHourStart: c.ExcludeHrStart,
ExcludeHourEnd: c.ExcludeHrEnd,
})
if imResp.Code > 2 {
crylog.Error("Bad configuration:", imResp.Message)
return errors.New("InitMiner failed: " + imResp.Message)
2020-06-20 01:21:08 +03:00
}
2020-08-17 02:31:54 +03:00
if imResp.Code == 2 {
crylog.Warn("")
crylog.Warn("WARNING: Could not allocate hugepages. This may reduce hashrate.")
crylog.Warn(" Rebooting your machine might fix this.")
2020-08-17 02:31:54 +03:00
crylog.Warn("")
2020-06-20 01:21:08 +03:00
}
2020-08-01 18:16:13 +03:00
sleepSec := 3 * time.Second // time to sleep if connection attempt fails
for {
2020-08-17 02:31:54 +03:00
plResp := minerlib.PoolLogin(&minerlib.PoolLoginArgs{
Username: c.Username,
RigID: c.RigID,
Wallet: c.Wallet,
Agent: c.Agent,
Config: c.AdvancedConfig,
UseTLS: c.UseTLS,
})
if plResp.Code < 0 {
crylog.Error("Pool server not responding:", plResp.Message)
crylog.Info("Sleeping for", sleepSec, "seconds before trying again.")
time.Sleep(sleepSec)
sleepSec += time.Second
continue
2020-08-17 02:31:54 +03:00
}
if plResp.Code == 1 {
if len(plResp.Message) > 0 {
crylog.Warn(":::::::::::::::::::::::::::::::::::::::::::::::::::::::::\n")
if plResp.MessageID == client.NO_WALLET_SPECIFIED_WARNING_CODE {
crylog.Warn("WARNING: your username is not yet associated with any")
crylog.Warn(" wallet id. You should fix this immediately.")
} else {
crylog.Warn("WARNING from pool server")
crylog.Warn(" Message:", plResp.Message)
}
crylog.Warn(" Code :", plResp.MessageID, "\n")
crylog.Warn(":::::::::::::::::::::::::::::::::::::::::::::::::::::::::")
2020-08-06 01:32:12 +03:00
}
2020-08-17 02:31:54 +03:00
break
}
2020-08-17 02:31:54 +03:00
crylog.Error("Pool refused login:", plResp.Message)
return errors.New("pool refused login")
}
2020-08-21 06:13:30 +03:00
// We assume the screen is active when the miner is started. This may
// not hold if someone is running the miner from an auto-start script?
if !c.Saver {
2020-08-17 02:31:54 +03:00
minerlib.ReportIdleScreenState(true)
2020-06-20 01:21:08 +03:00
}
2020-08-21 06:13:30 +03:00
ch, err := c.MachineStater.GetMachineStateChannel(c.Saver)
if err != nil {
minerlib.ReportIdleScreenState(true)
crylog.Error("failed to machine state monitor, screen & battery state will be ignored")
} else {
go monitorMachineState(ch)
}
2020-06-20 01:21:08 +03:00
2020-08-20 07:19:23 +03:00
go printStatsPeriodically()
2020-06-20 01:21:08 +03:00
2020-08-20 07:19:23 +03:00
printKeyboardCommands()
2020-08-17 02:31:54 +03:00
scanner := bufio.NewScanner(os.Stdin)
var manualMinerActivate bool
for scanner.Scan() {
b := scanner.Text()
switch b {
case "i":
crylog.Info("Increasing thread count.")
minerlib.IncreaseThreads()
case "d":
crylog.Info("Decreasing thread count.")
minerlib.DecreaseThreads()
case "h", "s", "p":
2020-08-20 07:19:23 +03:00
printStats(false)
2020-08-17 02:31:54 +03:00
case "q", "quit", "exit":
crylog.Info("quitting due to keyboard command")
return nil
case "?", "help":
printKeyboardCommands()
}
if len(b) == 0 {
if !manualMinerActivate {
manualMinerActivate = true
minerlib.OverrideMiningActivityState(true)
} else {
manualMinerActivate = false
minerlib.RemoveMiningActivityOverride()
2020-06-20 01:21:08 +03:00
}
}
2020-08-17 02:31:54 +03:00
}
crylog.Error("Scanning terminated")
return errors.New("didn't expect keyboard scanning to terminate")
}
2020-06-20 01:21:08 +03:00
2020-08-20 07:19:23 +03:00
func printStats(ifActive bool) {
2020-08-17 02:31:54 +03:00
s := minerlib.GetMiningState()
msg := getActivityMessage(s.MiningActivity)
2020-08-20 07:19:23 +03:00
if ifActive && s.MiningActivity < 0 {
return
}
2020-08-17 02:31:54 +03:00
crylog.Info("")
2020-08-20 07:19:23 +03:00
crylog.Info("===========================================================")
2020-08-17 02:31:54 +03:00
if s.RecentHashrate < 0 {
2020-08-17 03:19:02 +03:00
crylog.Info("Current Hashrate : --calculating--")
2020-08-17 02:31:54 +03:00
} else {
crylog.Info("Current Hashrate :", strconv.FormatFloat(s.RecentHashrate, 'f', 2, 64))
2020-08-17 02:31:54 +03:00
}
crylog.Info("Hashrate since inception :", strconv.FormatFloat(s.Hashrate, 'f', 2, 64))
crylog.Info("Threads :", s.Threads)
crylog.Info("Shares [accepted:rejected]:", s.SharesAccepted, ":", s.SharesRejected)
crylog.Info("Hashes [client:pool]:", s.ClientSideHashes, ":", s.PoolSideHashes)
2020-08-20 07:19:23 +03:00
crylog.Info("===========================================================")
2020-08-17 02:31:54 +03:00
if s.SecondsOld >= 0.0 {
2020-08-20 07:19:23 +03:00
crylog.Info("Pool username :", s.PoolUsername)
crylog.Info("Lifetime hashes :", prettyInt(s.LifetimeHashes))
crylog.Info("Paid :", strconv.FormatFloat(s.Paid, 'f', 12, 64), "$XMR")
2020-08-17 03:19:02 +03:00
if s.Owed > 0.0 {
2020-08-20 07:19:23 +03:00
crylog.Info("Owed :", strconv.FormatFloat(s.Owed, 'f', 12, 64), "$XMR")
2020-08-17 03:19:02 +03:00
}
2020-08-20 07:19:23 +03:00
crylog.Info("Time to next reward (est.) :", s.TimeToReward)
crylog.Info(" Accumulated (est.) :", strconv.FormatFloat(s.Accumulated, 'f', 12, 64), "$XMR")
crylog.Info("===========================================================")
2020-06-20 01:21:08 +03:00
}
2020-08-17 03:19:02 +03:00
crylog.Info("Mining", msg)
2020-08-20 07:19:23 +03:00
crylog.Info("===========================================================")
2020-08-17 02:31:54 +03:00
crylog.Info("")
2020-06-20 01:21:08 +03:00
}
2020-07-26 05:37:56 +03:00
func printKeyboardCommands() {
2020-08-20 07:19:23 +03:00
crylog.Info("")
2020-07-26 05:37:56 +03:00
crylog.Info("Keyboard commands:")
crylog.Info(" s: print miner stats")
2020-08-20 07:19:23 +03:00
crylog.Info(" i: increase number of threads by 1")
crylog.Info(" d: decrease number of threads by 1")
2020-07-26 05:37:56 +03:00
crylog.Info(" q: quit")
crylog.Info(" <enter>: override a paused miner")
2020-08-20 07:19:23 +03:00
crylog.Info("")
2020-07-26 05:37:56 +03:00
}
2020-07-25 20:11:10 +03:00
func prettyInt(i int64) string {
s := strconv.Itoa(int(i))
out := []byte{}
count := 0
for i := len(s) - 1; i >= 0; i-- {
if count == 3 {
out = append(out, ',')
count = 0
}
out = append(out, s[i])
count++
}
for i, j := 0, len(out)-1; i < j; i, j = i+1, j-1 {
out[i], out[j] = out[j], out[i]
}
return string(out)
}
2020-08-20 07:19:23 +03:00
func printStatsPeriodically() {
2020-08-20 22:09:43 +03:00
// Wait for pool stats before the first printout
for {
time.Sleep(time.Second)
s := minerlib.GetMiningState()
if s.SecondsOld >= 0 {
break
}
}
printStats(false)
2020-08-20 07:19:23 +03:00
for {
<-time.After(30 * time.Second)
printStats(true) // print full stats only if actively mining
}
}
2020-08-21 06:13:30 +03:00
func monitorMachineState(ch chan MachineState) {
2020-06-20 01:21:08 +03:00
for state := range ch {
switch state {
case SCREEN_IDLE:
2020-08-17 02:31:54 +03:00
minerlib.ReportIdleScreenState(true)
case SCREEN_ACTIVE:
2020-08-17 02:31:54 +03:00
minerlib.ReportIdleScreenState(false)
case BATTERY_POWER:
2020-08-17 02:31:54 +03:00
minerlib.ReportPowerState(true)
case AC_POWER:
2020-08-17 02:31:54 +03:00
minerlib.ReportPowerState(false)
}
}
}
func getActivityMessage(activityState int) string {
switch activityState {
case minerlib.MINING_PAUSED_NO_CONNECTION:
return "PAUSED: no connection."
case minerlib.MINING_PAUSED_SCREEN_ACTIVITY:
2020-08-20 07:19:23 +03:00
return "PAUSED: screen is active. <enter> to override."
2020-08-17 02:31:54 +03:00
case minerlib.MINING_PAUSED_BATTERY_POWER:
2020-08-20 07:19:23 +03:00
return "PAUSED: on battery power. <enter> to override."
2020-08-17 02:31:54 +03:00
case minerlib.MINING_PAUSED_USER_OVERRIDE:
2020-08-20 07:19:23 +03:00
return "PAUSED: keyboard override. <enter> to undo override."
2020-08-17 02:31:54 +03:00
case minerlib.MINING_PAUSED_TIME_EXCLUDED:
2020-08-20 07:19:23 +03:00
return "PAUSED: within time of day exclusion. <enter> to override."
2020-08-17 02:31:54 +03:00
case minerlib.MINING_ACTIVE:
return "ACTIVE"
case minerlib.MINING_ACTIVE_USER_OVERRIDE:
2020-08-20 07:19:23 +03:00
return "ACTIVE: keyboard override. <enter> to undo override."
2020-08-17 02:31:54 +03:00
}
crylog.Fatal("Unknown activity state:", activityState)
if activityState > 0 {
return "ACTIVE: unknown reason"
2020-06-20 01:21:08 +03:00
} else {
2020-08-17 02:31:54 +03:00
return "PAUSED: unknown reason"
2020-06-20 01:21:08 +03:00
}
}