fix(reload): apply custom diff updates to active miners
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
711c4259f7
commit
6f0747abc2
4 changed files with 242 additions and 44 deletions
|
|
@ -296,7 +296,10 @@ func (cd *CustomDiff) OnLogin(e Event) {
|
||||||
if e.Miner.customDiffResolved {
|
if e.Miner.customDiffResolved {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
e.Miner.user, e.Miner.customDiff = resolveLoginCustomDiff(e.Miner.user, cd.globalDiff.Load())
|
resolved := resolveLoginCustomDiff(e.Miner.user, cd.globalDiff.Load())
|
||||||
|
e.Miner.user = resolved.user
|
||||||
|
e.Miner.customDiff = resolved.diff
|
||||||
|
e.Miner.customDiffFromLogin = resolved.fromLogin
|
||||||
e.Miner.customDiffResolved = true
|
e.Miner.customDiffResolved = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
1
miner.go
1
miner.go
|
|
@ -44,6 +44,7 @@ type Miner struct {
|
||||||
routeID int64 // SimpleMapper ID in simple mode; -1 = unassigned
|
routeID int64 // SimpleMapper ID in simple mode; -1 = unassigned
|
||||||
customDiff uint64 // 0 = use pool diff; non-zero = cap diff to this value
|
customDiff uint64 // 0 = use pool diff; non-zero = cap diff to this value
|
||||||
customDiffResolved bool
|
customDiffResolved bool
|
||||||
|
customDiffFromLogin bool
|
||||||
accessPassword string
|
accessPassword string
|
||||||
globalDiff uint64
|
globalDiff uint64
|
||||||
diff uint64 // last difficulty sent to this miner from the pool
|
diff uint64 // last difficulty sent to this miner from the pool
|
||||||
|
|
|
||||||
163
reload_test.go
163
reload_test.go
|
|
@ -1,6 +1,13 @@
|
||||||
package proxy
|
package proxy
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/json"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
type reloadableSplitter struct {
|
type reloadableSplitter struct {
|
||||||
reloads int
|
reloads int
|
||||||
|
|
@ -105,6 +112,160 @@ func TestProxy_Reload_WorkersMode_Good(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestProxy_Reload_CustomDiff_Good(t *testing.T) {
|
||||||
|
minerConn, clientConn := net.Pipe()
|
||||||
|
defer minerConn.Close()
|
||||||
|
defer clientConn.Close()
|
||||||
|
|
||||||
|
miner := NewMiner(minerConn, 3333, nil)
|
||||||
|
miner.state = MinerStateReady
|
||||||
|
miner.globalDiff = 1000
|
||||||
|
miner.customDiff = 1000
|
||||||
|
miner.currentJob = Job{
|
||||||
|
Blob: strings.Repeat("0", 160),
|
||||||
|
JobID: "job-1",
|
||||||
|
Target: "01000000",
|
||||||
|
Algo: "cn/r",
|
||||||
|
}
|
||||||
|
|
||||||
|
p := &Proxy{
|
||||||
|
config: &Config{
|
||||||
|
Mode: "nicehash",
|
||||||
|
Workers: WorkersByRigID,
|
||||||
|
Bind: []BindAddr{{Host: "127.0.0.1", Port: 3333}},
|
||||||
|
Pools: []PoolConfig{{URL: "pool.example:3333", Enabled: true}},
|
||||||
|
CustomDiff: 1000,
|
||||||
|
},
|
||||||
|
customDiff: NewCustomDiff(1000),
|
||||||
|
miners: map[int64]*Miner{miner.ID(): miner},
|
||||||
|
}
|
||||||
|
|
||||||
|
done := make(chan map[string]any, 1)
|
||||||
|
go func() {
|
||||||
|
line, err := bufio.NewReader(clientConn).ReadBytes('\n')
|
||||||
|
if err != nil {
|
||||||
|
done <- nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var payload map[string]any
|
||||||
|
if err := json.Unmarshal(line, &payload); err != nil {
|
||||||
|
done <- nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
done <- payload
|
||||||
|
}()
|
||||||
|
|
||||||
|
p.Reload(&Config{
|
||||||
|
Mode: "nicehash",
|
||||||
|
Workers: WorkersByRigID,
|
||||||
|
Bind: []BindAddr{{Host: "127.0.0.1", Port: 3333}},
|
||||||
|
Pools: []PoolConfig{{URL: "pool.example:3333", Enabled: true}},
|
||||||
|
CustomDiff: 5000,
|
||||||
|
})
|
||||||
|
|
||||||
|
select {
|
||||||
|
case payload := <-done:
|
||||||
|
if payload == nil {
|
||||||
|
t.Fatal("expected reload to resend the current job with the new custom diff")
|
||||||
|
}
|
||||||
|
params, ok := payload["params"].(map[string]any)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("expected job params payload, got %#v", payload["params"])
|
||||||
|
}
|
||||||
|
target, _ := params["target"].(string)
|
||||||
|
if got := (Job{Target: target}).DifficultyFromTarget(); got == 0 || got > 5000 {
|
||||||
|
t.Fatalf("expected resent job difficulty at or below 5000, got %d", got)
|
||||||
|
}
|
||||||
|
case <-time.After(time.Second):
|
||||||
|
t.Fatal("timed out waiting for reload job refresh")
|
||||||
|
}
|
||||||
|
|
||||||
|
if miner.customDiff != 5000 {
|
||||||
|
t.Fatalf("expected active miner custom diff to reload, got %d", miner.customDiff)
|
||||||
|
}
|
||||||
|
if miner.globalDiff != 5000 {
|
||||||
|
t.Fatalf("expected active miner global diff to reload, got %d", miner.globalDiff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProxy_Reload_CustomDiff_Bad(t *testing.T) {
|
||||||
|
miner := &Miner{
|
||||||
|
id: 9,
|
||||||
|
state: MinerStateReady,
|
||||||
|
globalDiff: 1000,
|
||||||
|
customDiff: 7000,
|
||||||
|
customDiffFromLogin: true,
|
||||||
|
currentJob: Job{
|
||||||
|
Blob: strings.Repeat("0", 160),
|
||||||
|
JobID: "job-1",
|
||||||
|
Target: "01000000",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
p := &Proxy{
|
||||||
|
config: &Config{
|
||||||
|
Mode: "nicehash",
|
||||||
|
Workers: WorkersByRigID,
|
||||||
|
Bind: []BindAddr{{Host: "127.0.0.1", Port: 3333}},
|
||||||
|
Pools: []PoolConfig{{URL: "pool.example:3333", Enabled: true}},
|
||||||
|
CustomDiff: 1000,
|
||||||
|
},
|
||||||
|
customDiff: NewCustomDiff(1000),
|
||||||
|
miners: map[int64]*Miner{miner.ID(): miner},
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Reload(&Config{
|
||||||
|
Mode: "nicehash",
|
||||||
|
Workers: WorkersByRigID,
|
||||||
|
Bind: []BindAddr{{Host: "127.0.0.1", Port: 3333}},
|
||||||
|
Pools: []PoolConfig{{URL: "pool.example:3333", Enabled: true}},
|
||||||
|
CustomDiff: 5000,
|
||||||
|
})
|
||||||
|
|
||||||
|
if miner.customDiff != 7000 {
|
||||||
|
t.Fatalf("expected login suffix custom diff to be preserved, got %d", miner.customDiff)
|
||||||
|
}
|
||||||
|
if miner.globalDiff != 5000 {
|
||||||
|
t.Fatalf("expected miner global diff to update for future logins, got %d", miner.globalDiff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProxy_Reload_CustomDiff_Ugly(t *testing.T) {
|
||||||
|
miner := &Miner{
|
||||||
|
id: 11,
|
||||||
|
state: MinerStateWaitLogin,
|
||||||
|
globalDiff: 1000,
|
||||||
|
customDiff: 1000,
|
||||||
|
}
|
||||||
|
|
||||||
|
p := &Proxy{
|
||||||
|
config: &Config{
|
||||||
|
Mode: "nicehash",
|
||||||
|
Workers: WorkersByRigID,
|
||||||
|
Bind: []BindAddr{{Host: "127.0.0.1", Port: 3333}},
|
||||||
|
Pools: []PoolConfig{{URL: "pool.example:3333", Enabled: true}},
|
||||||
|
CustomDiff: 1000,
|
||||||
|
},
|
||||||
|
customDiff: NewCustomDiff(1000),
|
||||||
|
miners: map[int64]*Miner{miner.ID(): miner},
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Reload(&Config{
|
||||||
|
Mode: "nicehash",
|
||||||
|
Workers: WorkersByRigID,
|
||||||
|
Bind: []BindAddr{{Host: "127.0.0.1", Port: 3333}},
|
||||||
|
Pools: []PoolConfig{{URL: "pool.example:3333", Enabled: true}},
|
||||||
|
CustomDiff: 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
if miner.customDiff != 0 {
|
||||||
|
t.Fatalf("expected reload to clear the global custom diff for unauthenticated miners, got %d", miner.customDiff)
|
||||||
|
}
|
||||||
|
if miner.globalDiff != 0 {
|
||||||
|
t.Fatalf("expected miner global diff to be cleared, got %d", miner.globalDiff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestProxy_Reload_UpdatesServers(t *testing.T) {
|
func TestProxy_Reload_UpdatesServers(t *testing.T) {
|
||||||
originalLimiter := NewRateLimiter(RateLimit{MaxConnectionsPerMinute: 1})
|
originalLimiter := NewRateLimiter(RateLimit{MaxConnectionsPerMinute: 1})
|
||||||
p := &Proxy{
|
p := &Proxy{
|
||||||
|
|
|
||||||
|
|
@ -375,6 +375,7 @@ func (p *Proxy) Reload(config *Config) {
|
||||||
if p.customDiff != nil {
|
if p.customDiff != nil {
|
||||||
p.customDiff.globalDiff.Store(config.CustomDiff)
|
p.customDiff.globalDiff.Store(config.CustomDiff)
|
||||||
}
|
}
|
||||||
|
p.reloadCustomDiff(config.CustomDiff)
|
||||||
if p.customDiffBuckets != nil {
|
if p.customDiffBuckets != nil {
|
||||||
p.customDiffBuckets.SetEnabled(config.CustomDiffStats)
|
p.customDiffBuckets.SetEnabled(config.CustomDiffStats)
|
||||||
}
|
}
|
||||||
|
|
@ -398,6 +399,29 @@ func (p *Proxy) Reload(config *Config) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Proxy) reloadCustomDiff(globalDiff uint64) {
|
||||||
|
if p == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, miner := range p.activeMiners() {
|
||||||
|
if miner == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
miner.globalDiff = globalDiff
|
||||||
|
if miner.customDiffFromLogin {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
miner.customDiff = globalDiff
|
||||||
|
switch miner.state {
|
||||||
|
case MinerStateWaitReady, MinerStateReady:
|
||||||
|
job := miner.CurrentJob()
|
||||||
|
if job.IsValid() {
|
||||||
|
miner.ForwardJob(job, job.Algo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Proxy) reloadWatcher(enabled bool) {
|
func (p *Proxy) reloadWatcher(enabled bool) {
|
||||||
if p == nil || p.config == nil || p.config.configPath == "" {
|
if p == nil || p.config == nil || p.config.configPath == "" {
|
||||||
return
|
return
|
||||||
|
|
@ -1010,7 +1034,10 @@ func (m *Miner) handleLogin(request stratumRequest) {
|
||||||
m.ReplyWithError(requestID(request.ID), "Invalid password")
|
m.ReplyWithError(requestID(request.ID), "Invalid password")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
m.user, m.customDiff = resolveLoginCustomDiff(params.Login, m.globalDiff)
|
resolved := resolveLoginCustomDiff(params.Login, m.globalDiff)
|
||||||
|
m.user = resolved.user
|
||||||
|
m.customDiff = resolved.diff
|
||||||
|
m.customDiffFromLogin = resolved.fromLogin
|
||||||
m.customDiffResolved = true
|
m.customDiffResolved = true
|
||||||
m.password = params.Pass
|
m.password = params.Pass
|
||||||
m.agent = params.Agent
|
m.agent = params.Agent
|
||||||
|
|
@ -1044,21 +1071,27 @@ func (m *Miner) handleLogin(request stratumRequest) {
|
||||||
m.replyLoginSuccess(requestID(request.ID))
|
m.replyLoginSuccess(requestID(request.ID))
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveLoginCustomDiff(login string, globalDiff uint64) (string, uint64) {
|
type resolvedCustomDiff struct {
|
||||||
|
user string
|
||||||
|
diff uint64
|
||||||
|
fromLogin bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveLoginCustomDiff(login string, globalDiff uint64) resolvedCustomDiff {
|
||||||
plus := strings.LastIndex(login, "+")
|
plus := strings.LastIndex(login, "+")
|
||||||
if plus >= 0 && plus < len(login)-1 {
|
if plus >= 0 && plus < len(login)-1 {
|
||||||
suffix := login[plus+1:]
|
suffix := login[plus+1:]
|
||||||
if isDecimalDigits(suffix) {
|
if isDecimalDigits(suffix) {
|
||||||
if parsed, err := strconv.ParseUint(suffix, 10, 64); err == nil {
|
if parsed, err := strconv.ParseUint(suffix, 10, 64); err == nil {
|
||||||
return login[:plus], parsed
|
return resolvedCustomDiff{user: login[:plus], diff: parsed, fromLogin: true}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return login, 0
|
return resolvedCustomDiff{user: login}
|
||||||
}
|
}
|
||||||
if globalDiff > 0 {
|
if globalDiff > 0 {
|
||||||
return login, globalDiff
|
return resolvedCustomDiff{user: login, diff: globalDiff}
|
||||||
}
|
}
|
||||||
return login, 0
|
return resolvedCustomDiff{user: login}
|
||||||
}
|
}
|
||||||
|
|
||||||
func isDecimalDigits(value string) bool {
|
func isDecimalDigits(value string) bool {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue