Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
212 lines
5.7 KiB
Go
212 lines
5.7 KiB
Go
package atlas
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"crypto-miner-server/internal/db"
|
|
"crypto-miner-server/internal/strategy"
|
|
)
|
|
|
|
// FailureAtlas records conditioned tier failures and derives hard subtree skips.
|
|
type FailureAtlas struct {
|
|
db *db.Database
|
|
}
|
|
|
|
func NewFailureAtlas(database *db.Database) *FailureAtlas {
|
|
return &FailureAtlas{db: database}
|
|
}
|
|
|
|
// RecordFailure increments failure counters for each active condition on a failed tier attempt.
|
|
func (a *FailureAtlas) RecordFailure(fingerprintBucket, tier string, conditions []string) error {
|
|
if a == nil || a.db == nil || strings.TrimSpace(fingerprintBucket) == "" || strings.TrimSpace(tier) == "" {
|
|
return nil
|
|
}
|
|
tier = normalizeTier(tier)
|
|
for _, cond := range conditions {
|
|
cond = strings.TrimSpace(cond)
|
|
if cond == "" {
|
|
continue
|
|
}
|
|
if err := a.db.UpsertFailureAtlasPattern(fingerprintBucket, cond, tier); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ShouldSkipSubtree reports whether atlas rules block attempting tier on this fingerprint.
|
|
func (a *FailureAtlas) ShouldSkipSubtree(fingerprintBucket, tier string, probes ProbeSnapshot, fp strategy.HostFingerprint) bool {
|
|
if a == nil || a.db == nil {
|
|
return false
|
|
}
|
|
rules, err := a.matchingRules(fingerprintBucket, fp.GOOS, ExtractConditions(fp, probes))
|
|
if err != nil {
|
|
return false
|
|
}
|
|
target := normalizeTier(tier)
|
|
for _, rule := range rules {
|
|
for _, branch := range rule.Subtree {
|
|
if normalizeTier(branch) == target {
|
|
return true
|
|
}
|
|
}
|
|
if tierInSubtree(target, rule.Tier) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// GetAtlasRules returns active atlas rules for a fingerprint bucket (UI / AI).
|
|
func (a *FailureAtlas) GetAtlasRules(fingerprintBucket, goos string) ([]AtlasRule, error) {
|
|
if a == nil || a.db == nil {
|
|
return nil, nil
|
|
}
|
|
patterns, err := a.db.ListFailureAtlasPatterns(fingerprintBucket, goos)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return rulesFromPatterns(patterns), nil
|
|
}
|
|
|
|
// ComputeSkips returns hard skip entries for the current host snapshot.
|
|
func (a *FailureAtlas) ComputeSkips(
|
|
fp strategy.HostFingerprint,
|
|
probes map[string]bool,
|
|
defenderEnabled, defenderRTP *bool,
|
|
) ([]AtlasSkip, error) {
|
|
if a == nil || a.db == nil {
|
|
return nil, nil
|
|
}
|
|
snap := ProbeSnapshotFromMaps(fp, probes, defenderEnabled, defenderRTP)
|
|
rules, err := a.matchingRules(fp.Key(), fp.GOOS, ExtractConditions(fp, snap))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
seen := make(map[string]bool)
|
|
var skips []AtlasSkip
|
|
for _, rule := range rules {
|
|
for _, branch := range rule.Subtree {
|
|
key := normalizeTier(branch) + "|" + rule.Condition
|
|
if seen[key] {
|
|
continue
|
|
}
|
|
seen[key] = true
|
|
skips = append(skips, AtlasSkip{
|
|
Tier: branch,
|
|
Condition: rule.Condition,
|
|
Reason: rule.Reason,
|
|
})
|
|
}
|
|
}
|
|
return skips, nil
|
|
}
|
|
|
|
// MergeSkipsIntoStrategy adds atlas hard skips into an adaptive strategy plan.
|
|
func MergeSkipsIntoStrategy(strat *strategy.AdaptiveStrategy, skips []AtlasSkip) {
|
|
if strat == nil || len(skips) == 0 {
|
|
return
|
|
}
|
|
have := make(map[string]bool, len(strat.SkipTiers))
|
|
for _, t := range strat.SkipTiers {
|
|
have[normalizeTier(t)] = true
|
|
}
|
|
for _, skip := range skips {
|
|
tier := normalizeTier(skip.Tier)
|
|
if tier == "" || have[tier] {
|
|
continue
|
|
}
|
|
have[tier] = true
|
|
strat.SkipTiers = append(strat.SkipTiers, tier)
|
|
label := strings.ReplaceAll(tier, "_", " ")
|
|
strat.Reasoning = append(strat.Reasoning, strategy.StrategyReason{
|
|
Fact: "Failure atlas: " + label + " blocked under " + conditionLabel(skip.Condition),
|
|
Inference: skip.Reason,
|
|
Action: "Hard skip " + label + " subtree",
|
|
})
|
|
}
|
|
order := make([]string, 0, len(strat.TierOrder))
|
|
for _, t := range strat.TierOrder {
|
|
if !have[normalizeTier(t)] {
|
|
order = append(order, t)
|
|
}
|
|
}
|
|
strat.TierOrder = order
|
|
}
|
|
|
|
func (a *FailureAtlas) matchingRules(fingerprintBucket, goos string, activeConditions []string) ([]AtlasRule, error) {
|
|
patterns, err := a.db.ListFailureAtlasPatterns(fingerprintBucket, goos)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
active := make(map[string]bool, len(activeConditions))
|
|
for _, c := range activeConditions {
|
|
active[c] = true
|
|
}
|
|
var out []AtlasRule
|
|
for _, p := range patterns {
|
|
if p.FailCount < MinFailCountForSubtree {
|
|
continue
|
|
}
|
|
if len(active) > 0 && !active[p.Condition] {
|
|
continue
|
|
}
|
|
rule := AtlasRule{
|
|
Tier: p.Tier,
|
|
Condition: p.Condition,
|
|
FailCount: p.FailCount,
|
|
FingerprintBucket: p.FingerprintBucket,
|
|
Subtree: subtreeTiers(p.Tier),
|
|
Reason: fmt.Sprintf("%d failures with %s", p.FailCount, conditionLabel(p.Condition)),
|
|
}
|
|
out = append(out, rule)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func rulesFromPatterns(patterns []db.FailureAtlasPattern) []AtlasRule {
|
|
out := make([]AtlasRule, 0, len(patterns))
|
|
for _, p := range patterns {
|
|
if p.FailCount < MinFailCountForSubtree {
|
|
continue
|
|
}
|
|
out = append(out, AtlasRule{
|
|
Tier: p.Tier,
|
|
Condition: p.Condition,
|
|
FailCount: p.FailCount,
|
|
FingerprintBucket: p.FingerprintBucket,
|
|
Subtree: subtreeTiers(p.Tier),
|
|
Reason: fmt.Sprintf("%d failures with %s", p.FailCount, conditionLabel(p.Condition)),
|
|
})
|
|
}
|
|
return out
|
|
}
|
|
|
|
func conditionLabel(cond string) string {
|
|
switch cond {
|
|
case ConditionDefenderOn:
|
|
return "Defender on"
|
|
case ConditionNoDocker:
|
|
return "no Docker"
|
|
case ConditionAVBlocksExe:
|
|
return "AV blocks exe"
|
|
case ConditionGOOSWindows:
|
|
return "Windows"
|
|
case ConditionGOOSLinux:
|
|
return "Linux"
|
|
case ConditionGOOSDarwin:
|
|
return "macOS"
|
|
case ConditionNoWSL:
|
|
return "no WSL"
|
|
case ConditionNoPowerShell:
|
|
return "no PowerShell"
|
|
case ConditionNoDotNet:
|
|
return "no dotnet"
|
|
case ConditionNoGPU:
|
|
return "no GPU"
|
|
default:
|
|
return strings.ReplaceAll(cond, "_", " ")
|
|
}
|
|
}
|