Initial commit: AetherForge Linux (forge-mesh) v0.1.0-dev
Some checks failed
Test / test (push) Has been cancelled
Some checks failed
Test / test (push) Has been cancelled
This commit is contained in:
93
internal/fleet/earn.go
Normal file
93
internal/fleet/earn.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package fleet
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// EarnBeforeBurnConfig gates sibling autospread until local mining proves viable.
|
||||
type EarnBeforeBurnConfig struct {
|
||||
MinHashrate float64 `json:"min_hashrate"`
|
||||
MinDuration time.Duration `json:"min_duration"`
|
||||
FirstTierOnly bool `json:"first_tier_only"`
|
||||
}
|
||||
|
||||
// DefaultEarnConfig returns conservative earn-before-burn defaults.
|
||||
func DefaultEarnConfig() EarnBeforeBurnConfig {
|
||||
return EarnBeforeBurnConfig{
|
||||
MinHashrate: 100.0,
|
||||
MinDuration: 5 * time.Minute,
|
||||
FirstTierOnly: true,
|
||||
}
|
||||
}
|
||||
|
||||
// EarnGate tracks local hashrate proof before sibling spread.
|
||||
type EarnGate struct {
|
||||
Store *Store
|
||||
Config EarnBeforeBurnConfig
|
||||
}
|
||||
|
||||
// SpreadDecision indicates whether sibling spread is allowed.
|
||||
type SpreadDecision struct {
|
||||
Allowed bool `json:"allowed"`
|
||||
Reason string `json:"reason"`
|
||||
LocalHashrate float64 `json:"local_hashrate"`
|
||||
Siblings []string `json:"siblings,omitempty"`
|
||||
}
|
||||
|
||||
// CanSpreadToSiblings checks hashrate gate before autospread to phenotype siblings.
|
||||
func (g *EarnGate) CanSpreadToSiblings(ctx context.Context, hostID string) (*SpreadDecision, error) {
|
||||
dec := &SpreadDecision{}
|
||||
|
||||
hr, err := g.Store.HostHashrate(ctx, hostID)
|
||||
if err != nil {
|
||||
dec.Reason = "host not found"
|
||||
return dec, err
|
||||
}
|
||||
dec.LocalHashrate = hr
|
||||
|
||||
if hr < g.Config.MinHashrate {
|
||||
dec.Reason = fmt.Sprintf("hashrate %.2f below threshold %.2f", hr, g.Config.MinHashrate)
|
||||
return dec, nil
|
||||
}
|
||||
|
||||
if g.Config.FirstTierOnly {
|
||||
attempts, err := g.Store.ListLOTL(ctx, hostID, 50)
|
||||
if err != nil {
|
||||
return dec, err
|
||||
}
|
||||
hasSuccess := false
|
||||
for _, a := range attempts {
|
||||
if a.Phase == "deploy" && a.Status == "success" {
|
||||
hasSuccess = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasSuccess {
|
||||
dec.Reason = "first tier deploy has not succeeded yet"
|
||||
return dec, nil
|
||||
}
|
||||
}
|
||||
|
||||
var phenotype string
|
||||
err = g.Store.DB().QueryRowContext(ctx, `SELECT COALESCE(phenotype,'') FROM hosts WHERE id = ?`, hostID).Scan(&phenotype)
|
||||
if err != nil {
|
||||
dec.Reason = "phenotype unknown"
|
||||
return dec, err
|
||||
}
|
||||
|
||||
siblings, err := g.Store.SiblingHosts(ctx, hostID, phenotype)
|
||||
if err != nil {
|
||||
return dec, err
|
||||
}
|
||||
|
||||
dec.Allowed = len(siblings) > 0
|
||||
dec.Siblings = siblings
|
||||
if !dec.Allowed {
|
||||
dec.Reason = "no siblings in phenotype group"
|
||||
} else {
|
||||
dec.Reason = "earn-before-burn gate passed"
|
||||
}
|
||||
return dec, nil
|
||||
}
|
||||
Reference in New Issue
Block a user