From eabe9b521d9a750224e52b503afa445536a1f5ff Mon Sep 17 00:00:00 2001 From: Virgil Date: Sun, 5 Apr 2026 00:45:39 +0000 Subject: [PATCH] fix(proxy): harden hot-reload config access Co-Authored-By: Virgil --- core_impl.go | 6 ++++-- proxy.go | 3 ++- reload_test.go | 2 +- state_impl.go | 31 +++++++++++++++++++++++++------ 4 files changed, 32 insertions(+), 10 deletions(-) diff --git a/core_impl.go b/core_impl.go index 32cd71d..4c3e844 100644 --- a/core_impl.go +++ b/core_impl.go @@ -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 } diff --git a/proxy.go b/proxy.go index 9073bd0..83fc0cc 100644 --- a/proxy.go +++ b/proxy.go @@ -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 } diff --git a/reload_test.go b/reload_test.go index d7d6ed2..db47b18 100644 --- a/reload_test.go +++ b/reload_test.go @@ -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() { diff --git a/state_impl.go b/state_impl.go index 097989d..ed6ea92 100644 --- a/state_impl.go +++ b/state_impl.go @@ -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})