204 lines
4.8 KiB
Go
204 lines
4.8 KiB
Go
package nicehash
|
|
|
|
import (
|
|
"sync"
|
|
|
|
"dappco.re/go/core/proxy"
|
|
)
|
|
|
|
// NonceStorage is the 256-slot fixed-byte allocation table for one NonceMapper.
|
|
//
|
|
// Slot encoding:
|
|
//
|
|
// 0 = free
|
|
// +minerID = active miner
|
|
// -minerID = disconnected miner (dead slot, cleared on next SetJob)
|
|
//
|
|
// storage := nicehash.NewNonceStorage()
|
|
type NonceStorage struct {
|
|
slots [256]int64 // slot state per above encoding
|
|
miners map[int64]*proxy.Miner // minerID → Miner pointer for active miners
|
|
job proxy.Job // current job from pool
|
|
prevJob proxy.Job // previous job (for stale submit validation)
|
|
cursor int // search starts here (round-robin allocation)
|
|
expired uint64 // stale job ID hits for the previous job
|
|
mu sync.Mutex
|
|
}
|
|
|
|
// NewNonceStorage allocates the fixed-size miner slot table.
|
|
//
|
|
// storage := nicehash.NewNonceStorage()
|
|
func NewNonceStorage() *NonceStorage {
|
|
return &NonceStorage{
|
|
miners: make(map[int64]*proxy.Miner),
|
|
}
|
|
}
|
|
|
|
// Add finds the next free slot starting from cursor (wrapping), sets slot[index] = minerID,
|
|
// and sets the miner fixed byte.
|
|
//
|
|
// ok := storage.Add(miner)
|
|
func (s *NonceStorage) Add(miner *proxy.Miner) bool {
|
|
if miner == nil {
|
|
return false
|
|
}
|
|
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
for offset := 0; offset < len(s.slots); offset++ {
|
|
index := (s.cursor + offset) % len(s.slots)
|
|
if s.slots[index] != 0 {
|
|
continue
|
|
}
|
|
|
|
s.slots[index] = miner.ID()
|
|
s.miners[miner.ID()] = miner
|
|
miner.SetFixedByte(uint8(index))
|
|
s.cursor = (index + 1) % len(s.slots)
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// Remove marks slot[miner.FixedByte] as a dead slot until the next SetJob call.
|
|
//
|
|
// storage.Remove(miner)
|
|
func (s *NonceStorage) Remove(miner *proxy.Miner) {
|
|
if miner == nil {
|
|
return
|
|
}
|
|
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
index := int(miner.FixedByte())
|
|
if index >= 0 && index < len(s.slots) && s.slots[index] == miner.ID() {
|
|
s.slots[index] = -miner.ID()
|
|
}
|
|
delete(s.miners, miner.ID())
|
|
}
|
|
|
|
// SetJob replaces the current job, clears dead slots, and fans the job out to active miners.
|
|
//
|
|
// storage.SetJob(job)
|
|
func (s *NonceStorage) SetJob(job proxy.Job) {
|
|
s.mu.Lock()
|
|
if s.job.IsValid() && s.job.ClientID != "" && s.job.ClientID == job.ClientID {
|
|
s.prevJob = s.job
|
|
} else {
|
|
s.prevJob = proxy.Job{}
|
|
}
|
|
s.job = job
|
|
|
|
miners := make([]*proxy.Miner, 0, len(s.miners))
|
|
for index, minerID := range s.slots {
|
|
if minerID < 0 {
|
|
s.slots[index] = 0
|
|
continue
|
|
}
|
|
if minerID > 0 {
|
|
if miner := s.miners[minerID]; miner != nil {
|
|
miners = append(miners, miner)
|
|
}
|
|
}
|
|
}
|
|
s.mu.Unlock()
|
|
|
|
for _, miner := range miners {
|
|
miner.ForwardJob(job, job.Algo)
|
|
}
|
|
}
|
|
|
|
// IsValidJobID returns true if id matches the current or previous job ID.
|
|
//
|
|
// if !storage.IsValidJobID(submitJobID) { reject }
|
|
func (s *NonceStorage) IsValidJobID(id string) bool {
|
|
valid, _ := s.JobStatus(id)
|
|
return valid
|
|
}
|
|
|
|
// JobForID returns a copy of the current or previous job for the given ID.
|
|
//
|
|
// job, valid, expired := storage.JobForID(submitJobID)
|
|
func (s *NonceStorage) JobForID(id string) (job proxy.Job, valid bool, expired bool) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
if id == "" {
|
|
return proxy.Job{}, false, false
|
|
}
|
|
if id == s.job.JobID {
|
|
return s.job, true, false
|
|
}
|
|
if s.prevJob.IsValid() && s.prevJob.ClientID != "" && id == s.prevJob.JobID {
|
|
s.expired++
|
|
return s.prevJob, true, true
|
|
}
|
|
return proxy.Job{}, false, false
|
|
}
|
|
|
|
// JobStatus returns whether the job ID is current or stale-but-still-acceptable.
|
|
//
|
|
// valid, expired := storage.JobStatus(submitJobID)
|
|
func (s *NonceStorage) JobStatus(id string) (valid bool, expired bool) {
|
|
_, valid, expired = s.JobForID(id)
|
|
return valid, expired
|
|
}
|
|
|
|
// SlotCount returns free, dead, and active slot counts for monitoring output.
|
|
//
|
|
// free, dead, active := storage.SlotCount()
|
|
func (s *NonceStorage) SlotCount() (free int, dead int, active int) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
for _, slot := range s.slots {
|
|
switch {
|
|
case slot == 0:
|
|
free++
|
|
case slot < 0:
|
|
dead++
|
|
default:
|
|
active++
|
|
}
|
|
}
|
|
|
|
return free, dead, active
|
|
}
|
|
|
|
// ExpiredCount returns the number of times the previous job ID has been accepted as stale.
|
|
//
|
|
// count := storage.ExpiredCount()
|
|
func (s *NonceStorage) ExpiredCount() uint64 {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
return s.expired
|
|
}
|
|
|
|
// Miners returns a snapshot of the active miner map.
|
|
func (s *NonceStorage) Miners() map[int64]*proxy.Miner {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
miners := make(map[int64]*proxy.Miner, len(s.miners))
|
|
for minerID, miner := range s.miners {
|
|
miners[minerID] = miner
|
|
}
|
|
return miners
|
|
}
|
|
|
|
// CurrentJob returns a copy of the latest assigned job, if any.
|
|
func (s *NonceStorage) CurrentJob() *proxy.Job {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
if !s.job.IsValid() {
|
|
return nil
|
|
}
|
|
|
|
jobCopy := s.job
|
|
return &jobCopy
|
|
}
|