docs(proxy): align API comments with AX
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
9e44fb6ea3
commit
fd6bc01b87
7 changed files with 50 additions and 103 deletions
|
|
@ -16,16 +16,13 @@ import (
|
|||
"dappco.re/go/proxy"
|
||||
)
|
||||
|
||||
// Router matches the standard http.ServeMux registration shape.
|
||||
// http.NewServeMux()
|
||||
type Router interface {
|
||||
HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request))
|
||||
}
|
||||
|
||||
// RegisterRoutes wires the monitoring endpoints onto the supplied router.
|
||||
//
|
||||
// mux := http.NewServeMux()
|
||||
// proxyapi.RegisterRoutes(mux, p)
|
||||
// // GET /1/summary, /1/workers, and /1/miners are now live.
|
||||
// mux := http.NewServeMux()
|
||||
// RegisterRoutes(mux, p)
|
||||
func RegisterRoutes(router Router, p *proxy.Proxy) {
|
||||
if router == nil || p == nil {
|
||||
return
|
||||
|
|
|
|||
36
api_rows.go
36
api_rows.go
|
|
@ -1,18 +1,12 @@
|
|||
package proxy
|
||||
|
||||
// WorkerRow is one row in the /1/workers table.
|
||||
//
|
||||
// WorkerRow{"rig-alpha", "10.0.0.1", 1, 10, 0, 0, 100000, 1712232000, 1.0, 1.0, 1.0, 1.0, 1.0}
|
||||
// WorkerRow{"rig-alpha", "10.0.0.1", 1, 10, 0, 0, 100000, 1712232000, 1.0, 1.0, 1.0, 1.0, 1.0}
|
||||
type WorkerRow [13]any
|
||||
|
||||
// MinerRow is one row in the /1/miners table.
|
||||
//
|
||||
// MinerRow{1, "10.0.0.1:49152", 4096, 512, 2, 100000, "WALLET", "********", "rig-alpha", "XMRig/6.21.0"}
|
||||
// MinerRow{1, "10.0.0.1:49152", 4096, 512, 2, 100000, "WALLET", "********", "rig-alpha", "XMRig/6.21.0"}
|
||||
type MinerRow [10]any
|
||||
|
||||
// SummaryDocument is the RFC-shaped /1/summary response body.
|
||||
//
|
||||
// doc := p.SummaryDocument()
|
||||
// p.SummaryDocument()
|
||||
type SummaryDocument struct {
|
||||
Version string `json:"version"`
|
||||
Mode string `json:"mode"`
|
||||
|
|
@ -24,24 +18,18 @@ type SummaryDocument struct {
|
|||
CustomDiffStats map[uint64]CustomDiffBucketStats `json:"custom_diff_stats,omitempty"`
|
||||
}
|
||||
|
||||
// HashrateDocument carries the per-window hashrate array.
|
||||
//
|
||||
// HashrateDocument{Total: [6]float64{12345.67, 11900.00, 12100.00, 11800.00, 12000.00, 12200.00}}
|
||||
// HashrateDocument{Total: [6]float64{12345.67, 11900.00, 12100.00, 11800.00, 12000.00, 12200.00}}
|
||||
type HashrateDocument struct {
|
||||
Total [6]float64 `json:"total"`
|
||||
}
|
||||
|
||||
// MinersCountDocument carries current and peak miner counts.
|
||||
//
|
||||
// MinersCountDocument{Now: 142, Max: 200}
|
||||
// MinersCountDocument{Now: 142, Max: 200}
|
||||
type MinersCountDocument struct {
|
||||
Now uint64 `json:"now"`
|
||||
Max uint64 `json:"max"`
|
||||
}
|
||||
|
||||
// UpstreamDocument carries pool connection state counts.
|
||||
//
|
||||
// UpstreamDocument{Active: 1, Sleep: 0, Error: 0, Total: 1, Ratio: 142.0}
|
||||
// UpstreamDocument{Active: 1, Sleep: 0, Error: 0, Total: 1, Ratio: 142.0}
|
||||
type UpstreamDocument struct {
|
||||
Active uint64 `json:"active"`
|
||||
Sleep uint64 `json:"sleep"`
|
||||
|
|
@ -50,9 +38,7 @@ type UpstreamDocument struct {
|
|||
Ratio float64 `json:"ratio"`
|
||||
}
|
||||
|
||||
// ResultsDocument carries share acceptance statistics.
|
||||
//
|
||||
// ResultsDocument{Accepted: 4821, Rejected: 3, Invalid: 0, Expired: 12}
|
||||
// ResultsDocument{Accepted: 4821, Rejected: 3, Invalid: 0, Expired: 12}
|
||||
type ResultsDocument struct {
|
||||
Accepted uint64 `json:"accepted"`
|
||||
Rejected uint64 `json:"rejected"`
|
||||
|
|
@ -64,17 +50,13 @@ type ResultsDocument struct {
|
|||
Best [10]uint64 `json:"best"`
|
||||
}
|
||||
|
||||
// WorkersDocument is the RFC-shaped /1/workers response body.
|
||||
//
|
||||
// doc := p.WorkersDocument()
|
||||
// p.WorkersDocument()
|
||||
type WorkersDocument struct {
|
||||
Mode string `json:"mode"`
|
||||
Workers []WorkerRow `json:"workers"`
|
||||
}
|
||||
|
||||
// MinersDocument is the RFC-shaped /1/miners response body.
|
||||
//
|
||||
// doc := p.MinersDocument()
|
||||
// p.MinersDocument()
|
||||
type MinersDocument struct {
|
||||
Format []string `json:"format"`
|
||||
Miners []MinerRow `json:"miners"`
|
||||
|
|
|
|||
29
config.go
29
config.go
|
|
@ -1,11 +1,6 @@
|
|||
package proxy
|
||||
|
||||
// Config is the top-level proxy configuration, loaded from JSON and hot-reloaded on change.
|
||||
//
|
||||
// cfg, result := proxy.LoadConfig("/etc/proxy.json")
|
||||
// if !result.OK {
|
||||
// return result.Error
|
||||
// }
|
||||
// LoadConfig("/etc/proxy.json")
|
||||
type Config struct {
|
||||
Mode string `json:"mode"` // "nicehash" or "simple"
|
||||
Bind []BindAddr `json:"bind"` // listen addresses
|
||||
|
|
@ -27,18 +22,14 @@ type Config struct {
|
|||
configPath string
|
||||
}
|
||||
|
||||
// BindAddr is one TCP listen endpoint.
|
||||
//
|
||||
// proxy.BindAddr{Host: "0.0.0.0", Port: 3333, TLS: false}
|
||||
// BindAddr{Host: "0.0.0.0", Port: 3333, TLS: false}
|
||||
type BindAddr struct {
|
||||
Host string `json:"host"`
|
||||
Port uint16 `json:"port"`
|
||||
TLS bool `json:"tls"`
|
||||
}
|
||||
|
||||
// PoolConfig is one upstream pool entry.
|
||||
//
|
||||
// proxy.PoolConfig{URL: "pool.lthn.io:3333", User: "WALLET", Pass: "x", Enabled: true}
|
||||
// PoolConfig{URL: "pool.lthn.io:3333", User: "WALLET", Pass: "x", Enabled: true}
|
||||
type PoolConfig struct {
|
||||
URL string `json:"url"`
|
||||
User string `json:"user"`
|
||||
|
|
@ -51,9 +42,7 @@ type PoolConfig struct {
|
|||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
// TLSConfig controls inbound TLS on bind addresses that have TLS: true.
|
||||
//
|
||||
// proxy.TLSConfig{Enabled: true, CertFile: "/etc/proxy/cert.pem", KeyFile: "/etc/proxy/key.pem"}
|
||||
// TLSConfig{Enabled: true, CertFile: "/etc/proxy/cert.pem", KeyFile: "/etc/proxy/key.pem"}
|
||||
type TLSConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
CertFile string `json:"cert"`
|
||||
|
|
@ -62,9 +51,7 @@ type TLSConfig struct {
|
|||
Protocols string `json:"protocols"` // TLS version string; "" = default
|
||||
}
|
||||
|
||||
// HTTPConfig controls the monitoring API server.
|
||||
//
|
||||
// proxy.HTTPConfig{Enabled: true, Host: "127.0.0.1", Port: 8080, Restricted: true}
|
||||
// HTTPConfig{Enabled: true, Host: "127.0.0.1", Port: 8080, Restricted: true}
|
||||
type HTTPConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
Host string `json:"host"`
|
||||
|
|
@ -73,15 +60,13 @@ type HTTPConfig struct {
|
|||
Restricted bool `json:"restricted"` // true = read-only GET only
|
||||
}
|
||||
|
||||
// RateLimit controls per-IP connection rate limiting using a token bucket.
|
||||
//
|
||||
// proxy.RateLimit{MaxConnectionsPerMinute: 30, BanDurationSeconds: 300}
|
||||
// RateLimit{MaxConnectionsPerMinute: 30, BanDurationSeconds: 300}
|
||||
type RateLimit struct {
|
||||
MaxConnectionsPerMinute int `json:"max-connections-per-minute"` // 0 = disabled
|
||||
BanDurationSeconds int `json:"ban-duration"` // 0 = no ban
|
||||
}
|
||||
|
||||
// WorkersMode controls which login field becomes the worker name.
|
||||
// WorkersByRigID
|
||||
type WorkersMode string
|
||||
|
||||
const (
|
||||
|
|
|
|||
|
|
@ -12,12 +12,8 @@ import (
|
|||
"dappco.re/go/proxy"
|
||||
)
|
||||
|
||||
// StratumClient is one outbound stratum TCP (optionally TLS) connection to a pool.
|
||||
// The proxy presents itself to the pool as a standard stratum miner using the
|
||||
// wallet address and password from PoolConfig.
|
||||
//
|
||||
// client := pool.NewStratumClient(poolCfg, listener)
|
||||
// client.Connect()
|
||||
// client := NewStratumClient(poolCfg, listener)
|
||||
// client.Connect()
|
||||
type StratumClient struct {
|
||||
config proxy.PoolConfig
|
||||
listener StratumListener
|
||||
|
|
@ -32,7 +28,9 @@ type StratumClient struct {
|
|||
sendMu sync.Mutex
|
||||
}
|
||||
|
||||
// StratumListener receives events from the pool connection.
|
||||
// type listener struct{}
|
||||
//
|
||||
// func (listener) OnJob(job proxy.Job) {}
|
||||
type StratumListener interface {
|
||||
// OnJob is called when the pool pushes a new job notification or the login reply contains a job.
|
||||
OnJob(job proxy.Job)
|
||||
|
|
|
|||
40
proxy.go
40
proxy.go
|
|
@ -17,11 +17,11 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
// Proxy is the top-level orchestrator. It owns the server, splitter, stats, workers,
|
||||
// event bus, tick goroutine, and optional HTTP API.
|
||||
// p, result := proxy.New(cfg)
|
||||
//
|
||||
// p, result := proxy.New(cfg)
|
||||
// if result.OK { p.Start() }
|
||||
// if result.OK {
|
||||
// p.Start()
|
||||
// }
|
||||
type Proxy struct {
|
||||
config *Config
|
||||
splitter Splitter
|
||||
|
|
@ -44,7 +44,9 @@ type Proxy struct {
|
|||
submitCount atomic.Int64
|
||||
}
|
||||
|
||||
// Splitter is the interface both NonceSplitter and SimpleSplitter satisfy.
|
||||
// type stubSplitter struct{}
|
||||
//
|
||||
// func (stubSplitter) Connect() {}
|
||||
type Splitter interface {
|
||||
// Connect establishes the first pool upstream connection.
|
||||
Connect()
|
||||
|
|
@ -62,7 +64,7 @@ type Splitter interface {
|
|||
Upstreams() UpstreamStats
|
||||
}
|
||||
|
||||
// UpstreamStats carries pool connection state counts for monitoring.
|
||||
// UpstreamStats{Active: 1, Sleep: 0, Error: 0, Total: 1}
|
||||
type UpstreamStats struct {
|
||||
Active uint64 // connections currently receiving jobs
|
||||
Sleep uint64 // idle connections (simple mode reuse pool)
|
||||
|
|
@ -70,12 +72,12 @@ type UpstreamStats struct {
|
|||
Total uint64 // Active + Sleep + Error
|
||||
}
|
||||
|
||||
// LoginEvent is dispatched when a miner completes the login handshake.
|
||||
// LoginEvent{Miner: miner}
|
||||
type LoginEvent struct {
|
||||
Miner *Miner
|
||||
}
|
||||
|
||||
// SubmitEvent is dispatched when a miner submits a share.
|
||||
// SubmitEvent{Miner: miner, JobID: "job-1", Nonce: "deadbeef", Result: "HASH", RequestID: 2}
|
||||
type SubmitEvent struct {
|
||||
Miner *Miner
|
||||
JobID string
|
||||
|
|
@ -85,15 +87,12 @@ type SubmitEvent struct {
|
|||
RequestID int64
|
||||
}
|
||||
|
||||
// CloseEvent is dispatched when a miner TCP connection closes.
|
||||
// CloseEvent{Miner: miner}
|
||||
type CloseEvent struct {
|
||||
Miner *Miner
|
||||
}
|
||||
|
||||
// ConfigWatcher polls a config file for mtime changes and calls onChange on modification.
|
||||
//
|
||||
// w := proxy.NewConfigWatcher("config.json", func(cfg *proxy.Config) { p.Reload(cfg) })
|
||||
// w.Start()
|
||||
// NewConfigWatcher("config.json", func(cfg *Config) { p.Reload(cfg) })
|
||||
type ConfigWatcher struct {
|
||||
path string
|
||||
onChange func(*Config)
|
||||
|
|
@ -101,10 +100,8 @@ type ConfigWatcher struct {
|
|||
done chan struct{}
|
||||
}
|
||||
|
||||
// RateLimiter implements per-IP token bucket connection rate limiting.
|
||||
//
|
||||
// rl := proxy.NewRateLimiter(proxy.RateLimit{MaxConnectionsPerMinute: 30, BanDurationSeconds: 300})
|
||||
// if rl.Allow("1.2.3.4:3333") { proceed() }
|
||||
// limiter := NewRateLimiter(RateLimit{MaxConnectionsPerMinute: 30, BanDurationSeconds: 300})
|
||||
// limiter.Allow("1.2.3.4:3333")
|
||||
type RateLimiter struct {
|
||||
config RateLimit
|
||||
buckets map[string]*tokenBucket
|
||||
|
|
@ -112,17 +109,14 @@ type RateLimiter struct {
|
|||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// tokenBucket is a simple token bucket for one IP.
|
||||
// tokenBucket{tokens: 30, lastRefill: time.Now()}
|
||||
type tokenBucket struct {
|
||||
tokens int
|
||||
lastRefill time.Time
|
||||
}
|
||||
|
||||
// CustomDiff resolves and applies per-miner difficulty overrides at login time.
|
||||
// Resolution order: user-suffix (+N) > Config.CustomDiff > pool difficulty.
|
||||
//
|
||||
// cd := proxy.NewCustomDiff(cfg.CustomDiff)
|
||||
// bus.Subscribe(proxy.EventLogin, cd.OnLogin)
|
||||
// resolver := NewCustomDiff(50000)
|
||||
// resolver.Apply(&Miner{user: "WALLET+75000"})
|
||||
type CustomDiff struct {
|
||||
globalDiff uint64
|
||||
}
|
||||
|
|
|
|||
17
stats.go
17
stats.go
|
|
@ -6,12 +6,9 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
// Stats tracks global proxy metrics. Hot-path counters are atomic. Hashrate windows
|
||||
// use a ring buffer per window size, advanced by Tick().
|
||||
//
|
||||
// stats := proxy.NewStats()
|
||||
// bus.Subscribe(proxy.EventAccept, stats.OnAccept)
|
||||
// bus.Subscribe(proxy.EventReject, stats.OnReject)
|
||||
// stats := NewStats()
|
||||
// bus.Subscribe(EventAccept, stats.OnAccept)
|
||||
// bus.Subscribe(EventReject, stats.OnReject)
|
||||
type Stats struct {
|
||||
accepted atomic.Uint64
|
||||
rejected atomic.Uint64
|
||||
|
|
@ -28,7 +25,7 @@ type Stats struct {
|
|||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// Hashrate window sizes in seconds. Index maps to Stats.windows and SummaryResponse.Hashrate.
|
||||
// HashrateWindow60s
|
||||
const (
|
||||
HashrateWindow60s = 0 // 1 minute
|
||||
HashrateWindow600s = 1 // 10 minutes
|
||||
|
|
@ -38,16 +35,14 @@ const (
|
|||
HashrateWindowAll = 5 // all-time (single accumulator, no window)
|
||||
)
|
||||
|
||||
// tickWindow is a fixed-capacity ring buffer of per-second difficulty sums.
|
||||
// newTickWindow(60)
|
||||
type tickWindow struct {
|
||||
buckets []uint64
|
||||
pos int
|
||||
size int // window size in seconds = len(buckets)
|
||||
}
|
||||
|
||||
// StatsSummary is the serialisable snapshot returned by Summary().
|
||||
//
|
||||
// summary := stats.Summary()
|
||||
// NewStats().Summary()
|
||||
type StatsSummary struct {
|
||||
Accepted uint64 `json:"accepted"`
|
||||
Rejected uint64 `json:"rejected"`
|
||||
|
|
|
|||
10
worker.go
10
worker.go
|
|
@ -5,10 +5,8 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
// Workers maintains per-worker aggregate stats. Workers are identified by name,
|
||||
// derived from the miner's login fields per WorkersMode.
|
||||
//
|
||||
// workers := proxy.NewWorkers(proxy.WorkersByRigID, bus)
|
||||
// workers := NewWorkers(WorkersByRigID, bus)
|
||||
// records := workers.List()
|
||||
type Workers struct {
|
||||
mode WorkersMode
|
||||
entries []WorkerRecord // ordered by first-seen (stable)
|
||||
|
|
@ -18,9 +16,7 @@ type Workers struct {
|
|||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// WorkerRecord is the per-identity aggregate.
|
||||
//
|
||||
// hr60 := record.Hashrate(60)
|
||||
// record.Hashrate(60)
|
||||
type WorkerRecord struct {
|
||||
Name string
|
||||
LastIP string
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue