Files
AetherForge/server/internal/erasure/torrent.go
AetherForge 894b7a50ae
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Add Fleet Torrent erasure extension with shard DHT and gossip.
Content-addressed shard DHT on seeder agents with subnet_primary_seeder election, cross-subnet fleet_torrent_gossip, BGP swarm magnets, C2 torrent manifest, and k-of-n peer fetch with C2 fallback.
2026-06-07 09:24:55 -07:00

102 lines
3.2 KiB
Go

package erasure
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"net/url"
"strings"
)
// TorrentShardEntry is one shard in a published torrent manifest.
type TorrentShardEntry struct {
Index int `json:"index"`
ShardHash string `json:"shard_hash"`
URL string `json:"url"`
}
// TorrentManifest is the C2 super-seeder torrent manifest for a deploy-plan token.
type TorrentManifest struct {
Token string `json:"token"`
Scheme string `json:"scheme"`
DataShards int `json:"data_shards"`
ParityShards int `json:"parity_shards"`
PayloadSHA256 string `json:"sha256"`
PayloadSize int `json:"payload_size"`
SwarmMagnet string `json:"swarm_magnet"`
ManifestURL string `json:"manifest_url"`
ShardManifestURLs []string `json:"shard_manifest_urls"`
Shards []TorrentShardEntry `json:"shards"`
}
// ShardContentHash returns the content-address hex SHA256 of one encoded shard.
func ShardContentHash(shard []byte) string {
sum := sha256.Sum256(shard)
return hex.EncodeToString(sum[:])
}
// ShardContentHashes returns content-address hashes for each shard slice.
func ShardContentHashes(shards [][]byte) []string {
out := make([]string, len(shards))
for i, sh := range shards {
out[i] = ShardContentHash(sh)
}
return out
}
// BuildTorrentManifest publishes canonical shard URLs and a swarm magnet for BGP hints.
func BuildTorrentManifest(serverURL, token, payloadSHA string, payloadSize int, p Params, shardHashes []string) (*TorrentManifest, error) {
p, err := p.Normalize()
if err != nil {
return nil, err
}
total := p.TotalShards()
if len(shardHashes) != total {
return nil, fmt.Errorf("erasure torrent: shard hash count mismatch")
}
base := strings.TrimRight(strings.TrimSpace(serverURL), "/")
if base == "" {
base = "http://127.0.0.1:8989"
}
manifestURL := fmt.Sprintf("%s/api/v1/public/erasure-torrent/%s/manifest", base, token)
shards := make([]TorrentShardEntry, total)
urls := make([]string, total)
for i := 0; i < total; i++ {
shardURL := fmt.Sprintf("%s/api/v1/public/erasure-shard/%s/%d", base, token, i)
shards[i] = TorrentShardEntry{Index: i, ShardHash: shardHashes[i], URL: shardURL}
urls[i] = shardURL
}
return &TorrentManifest{
Token: token,
Scheme: SchemeReedSolomonV1,
DataShards: p.DataShards,
ParityShards: p.ParityShards,
PayloadSHA256: strings.TrimSpace(strings.ToLower(payloadSHA)),
PayloadSize: payloadSize,
SwarmMagnet: SwarmMagnetLink(token, payloadSHA),
ManifestURL: manifestURL,
ShardManifestURLs: urls,
Shards: shards,
}, nil
}
// SwarmMagnetLink builds a magnet URI for fleet torrent swarm discovery.
func SwarmMagnetLink(token, payloadSHA string) string {
token = strings.TrimSpace(token)
payloadSHA = strings.TrimSpace(strings.ToLower(payloadSHA))
if token == "" {
return ""
}
q := url.Values{}
if payloadSHA != "" {
q.Set("xt", "urn:sha256:"+payloadSHA)
}
label := token
if len(label) > 8 {
label = label[:8]
}
q.Set("dn", "aetherforge-erasure-"+label)
q.Set("tr", "urn:aetherforge:erasure:"+token)
return "magnet:?" + q.Encode()
}