fix(proxy): align job and splitter behaviour with RFC

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-04 11:07:41 +00:00
parent 36fb1232d5
commit 07ff21aa67
6 changed files with 65 additions and 10 deletions

5
job.go
View file

@ -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 {

View file

@ -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)
}
}

View file

@ -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 {

View file

@ -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)
}

View file

@ -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)

View file

@ -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) {