From 07ff21aa67b9d5646dc1371ad3d8ff0d2d93cb8a Mon Sep 17 00:00:00 2001 From: Virgil Date: Sat, 4 Apr 2026 11:07:41 +0000 Subject: [PATCH] fix(proxy): align job and splitter behaviour with RFC Co-Authored-By: Virgil --- job.go | 5 ++--- job_test.go | 8 ++++++-- proxy_runtime.go | 6 +++++- splitter/nicehash/mapper.go | 33 ++++++++++++++++++++++++++++++++- splitter/nicehash/splitter.go | 16 +++++++++++++--- splitter/simple/splitter.go | 7 +++++++ 6 files changed, 65 insertions(+), 10 deletions(-) diff --git a/job.go b/job.go index 88fe42f..f327c29 100644 --- a/job.go +++ b/job.go @@ -3,7 +3,6 @@ package proxy import ( "encoding/binary" "encoding/hex" - "math" "strconv" ) @@ -62,10 +61,10 @@ func (j Job) DifficultyFromTarget() uint64 { targetValue := binary.LittleEndian.Uint32(targetBytes) if targetValue == 0 { - return math.MaxUint64 + return 0 } - return uint64(math.Floor(float64(math.MaxUint32) / float64(targetValue))) + return uint64(^uint32(0) / targetValue) } func lowerHexDigit(value uint8) byte { diff --git a/job_test.go b/job_test.go index 32a7f35..90c9f47 100644 --- a/job_test.go +++ b/job_test.go @@ -62,7 +62,11 @@ func TestJob_DifficultyFromTarget_Bad(t *testing.T) { func TestJob_DifficultyFromTarget_Ugly(t *testing.T) { job := Job{Target: "00000000"} - if got := job.DifficultyFromTarget(); got == 0 { - t.Fatal("expected zero target to saturate difficulty") + if got := job.DifficultyFromTarget(); got != 0 { + t.Fatalf("expected zero target difficulty to be zero, got %d", got) + } + job = Job{Target: "ffffffff"} + if got := job.DifficultyFromTarget(); got != 1 { + t.Fatalf("expected maximum target to resolve to difficulty 1, got %d", got) } } diff --git a/proxy_runtime.go b/proxy_runtime.go index 05461a0..87a8329 100644 --- a/proxy_runtime.go +++ b/proxy_runtime.go @@ -259,7 +259,11 @@ func (p *Proxy) Upstreams() UpstreamStats { } func (p *Proxy) acceptConn(conn net.Conn, localPort uint16) { - miner := NewMiner(conn, localPort, nil) + var tlsCfg *tls.Config + if _, ok := conn.(*tls.Conn); ok { + tlsCfg = &tls.Config{} + } + miner := NewMiner(conn, localPort, tlsCfg) miner.events = p.events miner.splitter = p.splitter if p.config != nil { diff --git a/splitter/nicehash/mapper.go b/splitter/nicehash/mapper.go index cb46909..b532160 100644 --- a/splitter/nicehash/mapper.go +++ b/splitter/nicehash/mapper.go @@ -2,6 +2,7 @@ package nicehash import ( "sync" + "time" "dappco.re/go/core/proxy" "dappco.re/go/core/proxy/pool" @@ -21,6 +22,7 @@ type NonceMapper struct { events *proxy.EventBus active bool // true once pool has sent at least one job suspended int // > 0 when pool connection is in error/reconnecting + idleAt time.Time mu sync.Mutex } @@ -48,11 +50,27 @@ func NewNonceMapper(id int64, cfg *proxy.Config, strategy pool.Strategy) *NonceM } func (m *NonceMapper) Add(miner *proxy.Miner) bool { - return m.storage.Add(miner) + if !m.storage.Add(miner) { + return false + } + + m.mu.Lock() + m.idleAt = time.Time{} + m.mu.Unlock() + return true } func (m *NonceMapper) Remove(miner *proxy.Miner) { m.storage.Remove(miner) + + _, _, active := m.storage.SlotCount() + if active == 0 { + m.mu.Lock() + if m.idleAt.IsZero() { + m.idleAt = time.Now().UTC() + } + m.mu.Unlock() + } } func (m *NonceMapper) Submit(event *proxy.SubmitEvent) { @@ -98,6 +116,7 @@ func (m *NonceMapper) OnJob(job proxy.Job) { m.mu.Lock() m.active = true m.suspended = 0 + m.idleAt = time.Time{} m.mu.Unlock() m.storage.SetJob(job) } @@ -148,3 +167,15 @@ func (m *NonceMapper) OnDisconnect() { m.suspended++ m.mu.Unlock() } + +func (m *NonceMapper) IdleDuration(now time.Time) time.Duration { + m.mu.Lock() + idleAt := m.idleAt + m.mu.Unlock() + + if idleAt.IsZero() { + return 0 + } + + return now.Sub(idleAt) +} diff --git a/splitter/nicehash/splitter.go b/splitter/nicehash/splitter.go index 2459d9f..f4aa086 100644 --- a/splitter/nicehash/splitter.go +++ b/splitter/nicehash/splitter.go @@ -11,6 +11,7 @@ package nicehash import ( "sync" + "time" "dappco.re/go/core/proxy" "dappco.re/go/core/proxy/pool" @@ -67,6 +68,9 @@ func (s *NonceSplitter) OnLogin(event *proxy.LoginEvent) { mapper.events = s.events event.Miner.SetMapperID(mapper.id) event.Miner.SetNiceHashEnabled(true) + if currentJob := mapper.storage.CurrentJob(); currentJob != nil && currentJob.IsValid() { + event.Miner.ForwardJob(*currentJob, currentJob.Algo) + } return } } @@ -78,6 +82,9 @@ func (s *NonceSplitter) OnLogin(event *proxy.LoginEvent) { mapper.events = s.events event.Miner.SetMapperID(mapper.id) event.Miner.SetNiceHashEnabled(true) + if currentJob := mapper.storage.CurrentJob(); currentJob != nil && currentJob.IsValid() { + event.Miner.ForwardJob(*currentJob, currentJob.Algo) + } } } @@ -123,11 +130,14 @@ func (s *NonceSplitter) GC() { s.mu.Lock() defer s.mu.Unlock() + now := time.Now().UTC() filtered := s.mappers[:0] for _, mapper := range s.mappers { - free, dead, active := mapper.storage.SlotCount() - if active == 0 && dead == 0 && free == 256 && len(s.mappers) > 1 { - mapper.strategy.Disconnect() + _, _, active := mapper.storage.SlotCount() + if active == 0 && mapper.IdleDuration(now) >= 60*time.Second { + if mapper.strategy != nil { + mapper.strategy.Disconnect() + } continue } filtered = append(filtered, mapper) diff --git a/splitter/simple/splitter.go b/splitter/simple/splitter.go index 4e0c925..571a916 100644 --- a/splitter/simple/splitter.go +++ b/splitter/simple/splitter.go @@ -85,6 +85,13 @@ func (s *SimpleSplitter) OnLogin(event *proxy.LoginEvent) { mapper.idleAt = time.Time{} event.Miner.SetRouteID(mapper.id) s.active[event.Miner.ID()] = mapper + + mapper.mu.Lock() + currentJob := mapper.job + mapper.mu.Unlock() + if currentJob.IsValid() { + event.Miner.ForwardJob(currentJob, currentJob.Algo) + } } func (s *SimpleSplitter) OnSubmit(event *proxy.SubmitEvent) {