fix(proxy): harden hot-reload config access

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-05 00:45:39 +00:00
parent a11d5b0969
commit eabe9b521d
4 changed files with 32 additions and 10 deletions

View file

@ -224,7 +224,9 @@ func EffectiveShareDifficulty(job Job, miner *Miner) uint64 {
// resolver := proxy.NewCustomDiff(50000)
// resolver.OnLogin(proxy.Event{Miner: miner})
func NewCustomDiff(globalDiff uint64) *CustomDiff {
return &CustomDiff{globalDiff: globalDiff}
cd := &CustomDiff{}
cd.globalDiff.Store(globalDiff)
return cd
}
// OnLogin normalises the login user once during handshake.
@ -237,7 +239,7 @@ func (cd *CustomDiff) OnLogin(e Event) {
if e.Miner.customDiffResolved {
return
}
e.Miner.user, e.Miner.customDiff = parseLoginUser(e.Miner.user, cd.globalDiff)
e.Miner.user, e.Miner.customDiff = parseLoginUser(e.Miner.user, cd.globalDiff.Load())
e.Miner.customDiffResolved = true
}

View file

@ -23,6 +23,7 @@ import (
// if result.OK { p.Start() }
type Proxy struct {
config *Config
configMu sync.RWMutex
splitter Splitter
stats *Stats
workers *Workers
@ -135,5 +136,5 @@ type tokenBucket struct {
// resolver := proxy.NewCustomDiff(50000)
// resolver.Apply(&Miner{user: "WALLET+75000"})
type CustomDiff struct {
globalDiff uint64
globalDiff atomic.Uint64
}

View file

@ -62,7 +62,7 @@ func TestProxy_Reload_Good(t *testing.T) {
if got := p.config.Pools[0].URL; got != "pool-b.example:4444" {
t.Fatalf("expected pools to reload, got %q", got)
}
if got := p.customDiff.globalDiff; got != 50000 {
if got := p.customDiff.globalDiff.Load(); got != 50000 {
t.Fatalf("expected custom diff to reload, got %d", got)
}
if !p.rateLimit.IsActive() {

View file

@ -97,6 +97,8 @@ func (p *Proxy) Mode() string {
if p == nil || p.config == nil {
return ""
}
p.configMu.RLock()
defer p.configMu.RUnlock()
return p.config.Mode
}
@ -105,6 +107,8 @@ func (p *Proxy) WorkersMode() WorkersMode {
if p == nil || p.config == nil {
return WorkersDisabled
}
p.configMu.RLock()
defer p.configMu.RUnlock()
return p.config.Workers
}
@ -325,8 +329,10 @@ func (p *Proxy) Reload(config *Config) {
if result := config.Validate(); !result.OK {
return
}
p.configMu.Lock()
poolsChanged := p.config == nil || !reflect.DeepEqual(p.config.Pools, config.Pools)
workersChanged := p.config == nil || p.config.Workers != config.Workers
nextWorkersMode := config.Workers
if p.config == nil {
p.config = config
} else {
@ -338,11 +344,12 @@ func (p *Proxy) Reload(config *Config) {
p.config.Mode = preservedMode
p.config.configPath = preservedConfigPath
}
p.configMu.Unlock()
if workersChanged && p.workers != nil {
p.workers.ResetMode(p.config.Workers, p.activeMiners())
p.workers.ResetMode(nextWorkersMode, p.activeMiners())
}
if p.customDiff != nil {
p.customDiff.globalDiff = config.CustomDiff
p.customDiff.globalDiff.Store(config.CustomDiff)
}
if p.customDiffBuckets != nil {
p.customDiffBuckets.SetEnabled(config.CustomDiffStats)
@ -406,14 +413,26 @@ func (p *Proxy) acceptMiner(conn net.Conn, localPort uint16) {
_ = conn.Close()
return
}
p.configMu.RLock()
accessPassword := ""
algoExtension := false
customDiff := uint64(0)
mode := ""
if p.config != nil {
accessPassword = p.config.AccessPassword
algoExtension = p.config.AlgoExtension
customDiff = p.config.CustomDiff
mode = p.config.Mode
}
p.configMu.RUnlock()
if p.stats != nil {
p.stats.connections.Add(1)
}
miner := NewMiner(conn, localPort, nil)
miner.accessPassword = p.config.AccessPassword
miner.algoEnabled = p.config.AlgoExtension
miner.globalDiff = p.config.CustomDiff
miner.extNH = strings.EqualFold(p.config.Mode, "nicehash")
miner.accessPassword = accessPassword
miner.algoEnabled = algoExtension
miner.globalDiff = customDiff
miner.extNH = strings.EqualFold(mode, "nicehash")
miner.onLogin = func(m *Miner) {
if p.events != nil {
p.events.Dispatch(Event{Type: EventLogin, Miner: m})