move stats into own package

This commit is contained in:
cryptonote-social 2020-08-12 16:47:00 -07:00
parent 14dec3b86f
commit 64e33fbf4e
2 changed files with 230 additions and 27 deletions

View File

@ -3,12 +3,14 @@ package minerlib
import (
"github.com/cryptonote-social/csminer/blockchain"
"github.com/cryptonote-social/csminer/crylog"
"github.com/cryptonote-social/csminer/minerlib/stats"
"github.com/cryptonote-social/csminer/rx"
"github.com/cryptonote-social/csminer/stratum/client"
"bytes"
"encoding/hex"
"runtime"
"strconv"
"sync"
"sync/atomic"
"time"
@ -35,15 +37,6 @@ const (
)
var (
// miner client stats
sharesAccepted int64
sharesRejected int64
poolSideHashes int64
clientSideHashes, recentHashes int64
startTime time.Time // when the miner started up
lastStatsResetTime time.Time
lastStatsUpdateTime time.Time
// miner config
configMutex sync.Mutex
plArgs *PoolLoginArgs
@ -54,16 +47,6 @@ var (
cl client.Client
)
func tallyHashes(hashes int64) {
atomic.AddInt64(&clientSideHashes, hashes)
atomic.AddInt64(&recentHashes, hashes)
}
func resetRecentStats() {
atomic.StoreInt64(&recentHashes, 0)
lastStatsResetTime = time.Now()
}
type PoolLoginArgs struct {
// username: a properly formatted pool username.
Username string
@ -193,7 +176,7 @@ func InitMiner(args *InitMinerArgs) *InitMinerResponse {
} else {
r.Code = 1
}
startTime = time.Now()
stats.Init()
threads = args.Threads
go func() {
MiningLoop(awaitLogin())
@ -257,7 +240,6 @@ func MiningLoop(jobChan <-chan *client.MultiClientJob) {
var wg sync.WaitGroup // used to wait for stopped worker threads to finish
var stopper uint32 // atomic int used to signal rxlib worker threads to stop mining
resetRecentStats()
//wasJustMining := false
for {
@ -267,6 +249,7 @@ func MiningLoop(jobChan <-chan *client.MultiClientJob) {
if job == nil {
crylog.Warn("stratum client died")
jobChan = reconnectClient()
stats.ResetRecent()
continue
}
}
@ -275,6 +258,7 @@ func MiningLoop(jobChan <-chan *client.MultiClientJob) {
// Stop existing mining, if any, and wait for mining threads to finish.
atomic.StoreUint32(&stopper, 1)
wg.Wait()
printStats(true /*isMining*/)
// Check if we need to reinitialize rx dataset
newSeed, err := hex.DecodeString(job.SeedHash)
@ -286,7 +270,7 @@ func MiningLoop(jobChan <-chan *client.MultiClientJob) {
crylog.Info("New seed:", job.SeedHash)
rx.SeedRX(newSeed, runtime.GOMAXPROCS(0))
lastSeed = newSeed
resetRecentStats()
stats.ResetRecent()
}
atomic.StoreUint32(&stopper, 0)
@ -299,6 +283,19 @@ func MiningLoop(jobChan <-chan *client.MultiClientJob) {
defer crylog.Info("Mining loop terminated")
}
func printStats(isMining bool) {
s := stats.GetSnapshot(isMining)
crylog.Info("=====================================")
crylog.Info("Shares [accepted:rejected]:", s.SharesAccepted, ":", s.SharesRejected)
crylog.Info("Hashes [client:pool]:", s.ClientSideHashes, ":", s.PoolSideHashes)
if s.RecentHashrate > 0.0 {
crylog.Info("Hashes/sec [inception:recent]:",
strconv.FormatFloat(s.Hashrate, 'f', 2, 64), ":",
strconv.FormatFloat(s.RecentHashrate, 'f', 2, 64))
}
crylog.Info("=====================================")
}
func goMine(wg *sync.WaitGroup, stopper *uint32, job client.MultiClientJob, thread int) {
defer wg.Done()
input, err := hex.DecodeString(job.Blob)
@ -314,10 +311,10 @@ func goMine(wg *sync.WaitGroup, stopper *uint32, job client.MultiClientJob, thre
for {
res := rx.HashUntil(input, uint64(diffTarget), thread, hash, nonce, stopper)
if res <= 0 {
tallyHashes(-res)
stats.TallyHashes(-res)
break
}
tallyHashes(res)
stats.TallyHashes(res)
crylog.Info("Share found by thread", thread, "w/ target:", blockchain.HashDifficulty(hash))
fnonce := hex.EncodeToString(nonce)
// If the client is alive, submit the share in a separate thread so we can resume hashing
@ -335,12 +332,11 @@ func goMine(wg *sync.WaitGroup, stopper *uint32, job client.MultiClientJob, thre
return
}
if len(resp.Error) > 0 {
atomic.AddInt64(&sharesRejected, 1)
stats.ShareRejected()
crylog.Warn("Submit work server error:", jobid, resp.Error)
return
}
atomic.AddInt64(&sharesAccepted, 1)
atomic.AddInt64(&poolSideHashes, diffTarget)
stats.ShareAccepted(diffTarget)
}(fnonce, job.JobID)
}
}

207
minerlib/stats/stats.go Normal file
View File

@ -0,0 +1,207 @@
package stats
import (
"encoding/json"
"io/ioutil"
"net/http"
"strconv"
"strings"
"sync"
"time"
)
var (
mutex sync.RWMutex
// client side stats
startTime time.Time // when the miner started up
lastResetTime time.Time
lastUpdateTime time.Time
sharesAccepted int64
sharesRejected int64
poolSideHashes int64
clientSideHashes, recentHashes int64
// pool stats
lastPoolUserQuery string
lastPoolUpdateTime time.Time
ppropProgress float64
hashrate1, hashrate24 int64
lifetimeHashes int64
paid, owed, accumulated float64
timeToReward string
)
func Init() {
mutex.Lock()
defer mutex.Unlock()
now := time.Now()
startTime = now
lastResetTime = now
lastUpdateTime = now
}
func TallyHashes(hashes int64) {
mutex.Lock()
defer mutex.Unlock()
lastUpdateTime = time.Now()
clientSideHashes += hashes
recentHashes += hashes
}
func ShareAccepted(diffTarget int64) {
mutex.Lock()
defer mutex.Unlock()
sharesAccepted++
poolSideHashes += diffTarget
}
func ShareRejected() {
mutex.Lock()
defer mutex.Unlock()
sharesRejected++
}
// Call every time an event happens that may induce a big change in hashrate,
// e.g. reseeding, adding/removing threads, restablishing a connection.
func ResetRecent() {
mutex.Lock()
defer mutex.Unlock()
recentHashes = 0
now := time.Now()
lastUpdateTime = now
lastResetTime = now
}
type Snapshot struct {
SharesAccepted, SharesRejected int64
ClientSideHashes, PoolSideHashes int64
Hashrate, RecentHashrate float64
}
func GetSnapshot(isMining bool) *Snapshot {
mutex.Lock()
defer mutex.Unlock()
r := &Snapshot{}
r.SharesAccepted = sharesAccepted
r.SharesRejected = sharesRejected
r.ClientSideHashes = clientSideHashes
r.PoolSideHashes = poolSideHashes
var elapsedOverall float64
if isMining {
// if we're actively mining then hash count is only accurate
// as of the last update time
elapsedOverall = lastUpdateTime.Sub(startTime).Seconds()
} else {
elapsedOverall = time.Now().Sub(startTime).Seconds()
}
// Recent stats are only accurate up to the last update time
elapsedRecent := lastUpdateTime.Sub(lastResetTime).Seconds()
if elapsedRecent > 0.0 && elapsedOverall > 0.0 {
r.Hashrate = float64(clientSideHashes) / elapsedOverall
r.RecentHashrate = float64(recentHashes) / elapsedRecent
}
return r
}
func RefreshPoolStats(username string) error {
c := &http.Client{
Timeout: 15 * time.Second,
}
uri := "https://cryptonote.social/json/WorkerStats"
sbody := "{\"Coin\": \"xmr\", \"Worker\": \"" + username + "\"}\n"
body := strings.NewReader(sbody)
resp, err := c.Post(uri, "", body)
if err != nil {
return err
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
s := &struct {
Code int
CycleProgress float64
Hashrate1 int64
Hashrate24 int64
LifetimeHashes int64
LifetimeBestHash int64
Donate float64
AmountPaid float64
AmountOwed float64
}{}
err = json.Unmarshal(b, &s)
if err != nil {
return err
}
// Now get pool stats
uri = "https://cryptonote.social/json/PoolStats"
sbody = "{\"Coin\": \"xmr\"}\n"
body = strings.NewReader(sbody)
resp2, err := http.DefaultClient.Post(uri, "", body)
if err != nil {
return err
}
defer resp2.Body.Close()
b, err = ioutil.ReadAll(resp2.Body)
if err != nil {
return err
}
ps := &struct {
Code int
NextBlockReward float64
Margin float64
PPROPProgress float64
PPROPHashrate int64
NetworkDifficulty int64
SmoothedDifficulty int64 // Network difficulty averaged over the past hour
}{}
err = json.Unmarshal(b, &ps)
if err != nil {
return err
}
diff := float64(ps.SmoothedDifficulty)
if diff == 0.0 {
diff = float64(ps.NetworkDifficulty)
}
hr := float64(ps.PPROPHashrate)
var ttreward string
if hr > 0.0 {
ttr := (diff*(1.0+ps.Margin) - (ps.PPROPProgress * diff)) / hr / 3600.0 / 24.0
if ttr > 0.0 {
if ttr < 1.0 {
ttr *= 24.0
if ttr < 1.0 {
ttr *= 60.0
ttreward = strconv.FormatFloat(ttr, 'f', 2, 64) + " min"
} else {
ttreward = strconv.FormatFloat(ttr, 'f', 2, 64) + " hrs"
}
} else {
ttreward = strconv.FormatFloat(ttr, 'f', 2, 64) + " days"
}
} else if ttr < 0.0 {
ttreward = "overdue"
}
}
mutex.Lock()
lastPoolUserQuery = username
lastPoolUpdateTime = time.Now()
ppropProgress = s.CycleProgress / (1.0 + ps.Margin)
hashrate1 = s.Hashrate1
hashrate24 = s.Hashrate24
lifetimeHashes = s.LifetimeHashes
paid = s.AmountPaid
owed = s.AmountOwed
timeToReward = ttreward
mutex.Unlock()
return nil
}