Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
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.
102 lines
3.2 KiB
Go
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()
|
|
}
|