2026-04-04 11:16:28 +01:00
|
|
|
// Package proxy is a CryptoNote stratum mining proxy library.
|
|
|
|
|
//
|
|
|
|
|
// It accepts miner connections over TCP (optionally TLS), splits the 32-bit nonce
|
|
|
|
|
// space across up to 256 simultaneous miners per upstream pool connection (NiceHash
|
|
|
|
|
// mode), and presents a small monitoring API.
|
|
|
|
|
//
|
|
|
|
|
// Full specification: docs/RFC.md
|
|
|
|
|
//
|
|
|
|
|
// p, result := proxy.New(cfg)
|
|
|
|
|
// if result.OK { p.Start() }
|
|
|
|
|
package proxy
|
|
|
|
|
|
|
|
|
|
import (
|
2026-04-04 10:29:02 +00:00
|
|
|
"net/http"
|
2026-04-04 11:16:28 +01:00
|
|
|
"sync"
|
2026-04-04 18:49:03 +00:00
|
|
|
"sync/atomic"
|
2026-04-04 11:16:28 +01:00
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
2026-04-04 23:48:33 +00:00
|
|
|
// p, result := proxy.New(cfg)
|
2026-04-04 11:16:28 +01:00
|
|
|
//
|
2026-04-04 23:48:33 +00:00
|
|
|
// if result.OK {
|
|
|
|
|
// p.Start()
|
|
|
|
|
// }
|
2026-04-04 11:16:28 +01:00
|
|
|
type Proxy struct {
|
2026-04-04 19:20:29 +00:00
|
|
|
config *Config
|
|
|
|
|
splitter Splitter
|
|
|
|
|
stats *Stats
|
|
|
|
|
workers *Workers
|
|
|
|
|
events *EventBus
|
|
|
|
|
servers []*Server
|
|
|
|
|
ticker *time.Ticker
|
|
|
|
|
watcher *ConfigWatcher
|
|
|
|
|
done chan struct{}
|
|
|
|
|
stopOnce sync.Once
|
|
|
|
|
minersMu sync.RWMutex
|
|
|
|
|
miners map[int64]*Miner
|
|
|
|
|
customDiff *CustomDiff
|
|
|
|
|
customDiffBuckets *CustomDiffBuckets
|
|
|
|
|
rateLimit *RateLimiter
|
|
|
|
|
httpServer *http.Server
|
|
|
|
|
accessLog *accessLogSink
|
2026-04-04 20:09:13 +00:00
|
|
|
shareLog *shareLogSink
|
2026-04-04 19:20:29 +00:00
|
|
|
submitCount atomic.Int64
|
2026-04-04 11:16:28 +01:00
|
|
|
}
|
|
|
|
|
|
2026-04-04 23:48:33 +00:00
|
|
|
// type stubSplitter struct{}
|
|
|
|
|
//
|
|
|
|
|
// func (stubSplitter) Connect() {}
|
2026-04-04 11:16:28 +01:00
|
|
|
type Splitter interface {
|
|
|
|
|
// Connect establishes the first pool upstream connection.
|
|
|
|
|
Connect()
|
|
|
|
|
// OnLogin routes a newly authenticated miner to an upstream slot.
|
|
|
|
|
OnLogin(event *LoginEvent)
|
|
|
|
|
// OnSubmit routes a share submission to the correct upstream.
|
|
|
|
|
OnSubmit(event *SubmitEvent)
|
|
|
|
|
// OnClose releases the upstream slot for a disconnecting miner.
|
|
|
|
|
OnClose(event *CloseEvent)
|
|
|
|
|
// Tick is called every second for keepalive and GC housekeeping.
|
|
|
|
|
Tick(ticks uint64)
|
|
|
|
|
// GC runs every 60 ticks to reclaim disconnected upstream slots.
|
|
|
|
|
GC()
|
|
|
|
|
// Upstreams returns current upstream pool connection counts.
|
|
|
|
|
Upstreams() UpstreamStats
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-04 23:48:33 +00:00
|
|
|
// UpstreamStats{Active: 1, Sleep: 0, Error: 0, Total: 1}
|
2026-04-04 11:16:28 +01:00
|
|
|
type UpstreamStats struct {
|
|
|
|
|
Active uint64 // connections currently receiving jobs
|
|
|
|
|
Sleep uint64 // idle connections (simple mode reuse pool)
|
|
|
|
|
Error uint64 // connections in error/reconnecting state
|
|
|
|
|
Total uint64 // Active + Sleep + Error
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-04 23:48:33 +00:00
|
|
|
// LoginEvent{Miner: miner}
|
2026-04-04 11:16:28 +01:00
|
|
|
type LoginEvent struct {
|
|
|
|
|
Miner *Miner
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-04 23:48:33 +00:00
|
|
|
// SubmitEvent{Miner: miner, JobID: "job-1", Nonce: "deadbeef", Result: "HASH", RequestID: 2}
|
2026-04-04 11:16:28 +01:00
|
|
|
type SubmitEvent struct {
|
|
|
|
|
Miner *Miner
|
|
|
|
|
JobID string
|
|
|
|
|
Nonce string
|
|
|
|
|
Result string
|
|
|
|
|
Algo string
|
|
|
|
|
RequestID int64
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-04 23:48:33 +00:00
|
|
|
// CloseEvent{Miner: miner}
|
2026-04-04 11:16:28 +01:00
|
|
|
type CloseEvent struct {
|
|
|
|
|
Miner *Miner
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-04 23:48:33 +00:00
|
|
|
// NewConfigWatcher("config.json", func(cfg *Config) { p.Reload(cfg) })
|
2026-04-04 11:16:28 +01:00
|
|
|
type ConfigWatcher struct {
|
|
|
|
|
path string
|
|
|
|
|
onChange func(*Config)
|
|
|
|
|
lastMod time.Time
|
|
|
|
|
done chan struct{}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-04 23:48:33 +00:00
|
|
|
// limiter := NewRateLimiter(RateLimit{MaxConnectionsPerMinute: 30, BanDurationSeconds: 300})
|
|
|
|
|
// limiter.Allow("1.2.3.4:3333")
|
2026-04-04 11:16:28 +01:00
|
|
|
type RateLimiter struct {
|
2026-04-04 22:06:18 +00:00
|
|
|
config RateLimit
|
2026-04-04 11:16:28 +01:00
|
|
|
buckets map[string]*tokenBucket
|
|
|
|
|
banned map[string]time.Time
|
|
|
|
|
mu sync.Mutex
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-04 23:48:33 +00:00
|
|
|
// tokenBucket{tokens: 30, lastRefill: time.Now()}
|
2026-04-04 11:16:28 +01:00
|
|
|
type tokenBucket struct {
|
|
|
|
|
tokens int
|
|
|
|
|
lastRefill time.Time
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-04 23:48:33 +00:00
|
|
|
// resolver := NewCustomDiff(50000)
|
|
|
|
|
// resolver.Apply(&Miner{user: "WALLET+75000"})
|
2026-04-04 11:16:28 +01:00
|
|
|
type CustomDiff struct {
|
|
|
|
|
globalDiff uint64
|
|
|
|
|
}
|