Upgrade dashboard, builder, and agent resource controls.
Dark UI with hashrate graphs, inline setting help, percent-based threads/RAM, configurable process name and display modes, and LAN-aware run.bat startup banner.
This commit is contained in:
@@ -8,34 +8,41 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"crypto-miner-agent/config"
|
||||
"crypto-miner-agent/job"
|
||||
"crypto-miner-agent/stats"
|
||||
)
|
||||
|
||||
type ShareHandler func(jobID, nonce, hash string)
|
||||
|
||||
type Pool struct {
|
||||
threads int
|
||||
engine *Engine
|
||||
handler ShareHandler
|
||||
threads int
|
||||
cfg config.RuntimeConfig
|
||||
reporter *stats.Reporter
|
||||
engine *Engine
|
||||
handler ShareHandler
|
||||
|
||||
mu sync.RWMutex
|
||||
currentJob *job.Job
|
||||
stopCh chan struct{}
|
||||
wg sync.WaitGroup
|
||||
paused atomic.Bool
|
||||
|
||||
hashesTotal atomic.Uint64
|
||||
sharesFound atomic.Uint64
|
||||
}
|
||||
|
||||
func NewPool(threads int, handler ShareHandler) *Pool {
|
||||
func NewPool(threads int, cfg config.RuntimeConfig, reporter *stats.Reporter, handler ShareHandler) *Pool {
|
||||
if threads <= 0 {
|
||||
threads = 1
|
||||
}
|
||||
return &Pool{
|
||||
threads: threads,
|
||||
engine: NewEngine(),
|
||||
handler: handler,
|
||||
stopCh: make(chan struct{}),
|
||||
threads: threads,
|
||||
cfg: cfg,
|
||||
reporter: reporter,
|
||||
engine: NewEngine(),
|
||||
handler: handler,
|
||||
stopCh: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +67,7 @@ func (p *Pool) Start() {
|
||||
p.wg.Add(1)
|
||||
go p.worker(i)
|
||||
}
|
||||
go p.resourceGuard()
|
||||
}
|
||||
|
||||
func (p *Pool) Stop() {
|
||||
@@ -75,6 +83,34 @@ func (p *Pool) ResetHashCounter() {
|
||||
p.hashesTotal.Store(0)
|
||||
}
|
||||
|
||||
func (p *Pool) resourceGuard() {
|
||||
ticker := time.NewTicker(5 * time.Second)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-p.stopCh:
|
||||
return
|
||||
case <-ticker.C:
|
||||
p.paused.Store(!p.resourcesOK())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Pool) resourcesOK() bool {
|
||||
freeMB := p.reporter.FreeMemoryMB()
|
||||
if freeMB > 0 && freeMB < uint64(p.cfg.MinFreeRAM) {
|
||||
return false
|
||||
}
|
||||
totalMB := p.reporter.TotalMemoryMB()
|
||||
if totalMB > 0 && p.cfg.MaxMemoryPct > 0 {
|
||||
usedPct := float64(totalMB-freeMB) / float64(totalMB) * 100
|
||||
if usedPct > float64(p.cfg.MaxMemoryPct) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *Pool) worker(id int) {
|
||||
defer p.wg.Done()
|
||||
|
||||
@@ -87,6 +123,11 @@ func (p *Pool) worker(id int) {
|
||||
default:
|
||||
}
|
||||
|
||||
if p.paused.Load() {
|
||||
time.Sleep(2 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
p.mu.RLock()
|
||||
job := p.currentJob
|
||||
p.mu.RUnlock()
|
||||
@@ -101,6 +142,9 @@ func (p *Pool) worker(id int) {
|
||||
return
|
||||
default:
|
||||
}
|
||||
if p.paused.Load() {
|
||||
break
|
||||
}
|
||||
|
||||
hashHex, _, err := p.engine.HashAtNonce(nonce)
|
||||
if err != nil {
|
||||
@@ -126,7 +170,17 @@ func (p *Pool) worker(id int) {
|
||||
|
||||
func uint32ToHex(n uint32) string {
|
||||
b := []byte{byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24)}
|
||||
return hex.EncodeToString(b)
|
||||
return hexEncode(b)
|
||||
}
|
||||
|
||||
func hexEncode(b []byte) string {
|
||||
const hexdigits = "0123456789abcdef"
|
||||
out := make([]byte, len(b)*2)
|
||||
for i, v := range b {
|
||||
out[i*2] = hexdigits[v>>4]
|
||||
out[i*2+1] = hexdigits[v&0x0f]
|
||||
}
|
||||
return string(out)
|
||||
}
|
||||
|
||||
func difficultyToTargetHex(difficulty int64) string {
|
||||
|
||||
28
agent/miner/target_test.go
Normal file
28
agent/miner/target_test.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package miner
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHashMeetsTargetEqual(t *testing.T) {
|
||||
target := "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||
hash := "0000000000000000000000000000000000000000000000000000000000000001"
|
||||
if !hashMeetsTarget(hash, target) {
|
||||
t.Fatal("lower hash should meet high target")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHashMeetsTargetReject(t *testing.T) {
|
||||
target := "0000000000000000000000000000000000000000000000000000000000000001"
|
||||
hash := "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||
if hashMeetsTarget(hash, target) {
|
||||
t.Fatal("high hash should not meet low target")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDifficultyToTargetHex(t *testing.T) {
|
||||
out := difficultyToTargetHex(1000)
|
||||
if len(out) != 64 {
|
||||
t.Fatalf("expected 64 hex chars, got %d", len(out))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user