Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Serialize agent WebSocket writes (ping + JSON), mutex-protect spreadGateClock, and harden deploy-plan spread route hint setup under parallel test runs.
99 lines
2.4 KiB
Go
99 lines
2.4 KiB
Go
package deploy
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
|
|
"crypto-miner-agent/config"
|
|
)
|
|
|
|
var (
|
|
spreadGateClock = time.Now
|
|
spreadGateClockMu sync.Mutex
|
|
)
|
|
|
|
func spreadGateNow() time.Time {
|
|
spreadGateClockMu.Lock()
|
|
fn := spreadGateClock
|
|
spreadGateClockMu.Unlock()
|
|
return fn()
|
|
}
|
|
|
|
func setSpreadGateClock(fn func() time.Time) {
|
|
spreadGateClockMu.Lock()
|
|
spreadGateClock = fn
|
|
spreadGateClockMu.Unlock()
|
|
}
|
|
|
|
type spreadGateState struct {
|
|
mu sync.Mutex
|
|
stableSince time.Time
|
|
chainExhausted bool
|
|
lastHashrate float64
|
|
}
|
|
|
|
var spreadGate spreadGateState
|
|
|
|
// SetSpreadMiningTelemetry updates hashrate and chain state for autospread gating.
|
|
func SetSpreadMiningTelemetry(hashrate float64, chainExhausted bool) {
|
|
spreadGate.mu.Lock()
|
|
defer spreadGate.mu.Unlock()
|
|
spreadGate.chainExhausted = chainExhausted
|
|
spreadGate.lastHashrate = hashrate
|
|
}
|
|
|
|
func resetSpreadGateForTest() {
|
|
spreadGate.mu.Lock()
|
|
spreadGate.stableSince = time.Time{}
|
|
spreadGate.chainExhausted = false
|
|
spreadGate.lastHashrate = 0
|
|
spreadGate.mu.Unlock()
|
|
}
|
|
|
|
// HashrateGateEnabled reports whether server policy requires stable mining before spread.
|
|
func HashrateGateEnabled(cfg config.RuntimeConfig) bool {
|
|
return cfg.HashrateGateSpreadMin > 0 && cfg.HashrateGateHPS > 0
|
|
}
|
|
|
|
// AllowAutospread enforces earn-before-burn: stable hashrate and non-exhausted chain.
|
|
func AllowAutospread(cfg config.RuntimeConfig) (bool, string) {
|
|
spreadGate.mu.Lock()
|
|
exhausted := spreadGate.chainExhausted
|
|
spreadGate.mu.Unlock()
|
|
if exhausted {
|
|
return false, "mining chain exhausted"
|
|
}
|
|
if !HashrateGateEnabled(cfg) {
|
|
return true, ""
|
|
}
|
|
|
|
spreadGate.mu.Lock()
|
|
defer spreadGate.mu.Unlock()
|
|
now := spreadGateNow()
|
|
if spreadGate.lastHashrate < cfg.HashrateGateHPS {
|
|
spreadGate.stableSince = time.Time{}
|
|
return false, "hashrate below gate threshold"
|
|
}
|
|
if spreadGate.stableSince.IsZero() {
|
|
spreadGate.stableSince = now
|
|
}
|
|
need := time.Duration(cfg.HashrateGateSpreadMin) * time.Minute
|
|
if now.Sub(spreadGate.stableSince) < need {
|
|
return false, "hashrate stability window not met"
|
|
}
|
|
return true, ""
|
|
}
|
|
|
|
// StableMiningDurationForTest returns how long hashrate has been above threshold (tests only).
|
|
func StableMiningDurationForTest(cfg config.RuntimeConfig) time.Duration {
|
|
if !HashrateGateEnabled(cfg) {
|
|
return 0
|
|
}
|
|
spreadGate.mu.Lock()
|
|
defer spreadGate.mu.Unlock()
|
|
if spreadGate.stableSince.IsZero() || spreadGate.lastHashrate < cfg.HashrateGateHPS {
|
|
return 0
|
|
}
|
|
return spreadGateNow().Sub(spreadGate.stableSince)
|
|
}
|