new meta-core api, now redis is not exposed as an "API"
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
// Package cid centralizes CID-format helpers: ranking by utility for
|
||||
// external presentation, normalizing prefixed CID tokens, and the rules for
|
||||
// picking which CID is the canonical externally-visible identifier of a
|
||||
// file.
|
||||
//
|
||||
// A "CID token" in this codebase is the prefixed form "<algorithm>:<value>",
|
||||
// e.g. "midhash256:bafkr4ih...", "sha256:bafkrei...", "ipfs:bafy...". The
|
||||
// prefix makes the type recoverable without parsing the multicodec, which is
|
||||
// what the reverse index (cid:<token> → uuid), the per-root cids set, and
|
||||
// the canonical_cid field all rely on.
|
||||
package cid
|
||||
|
||||
import "strings"
|
||||
|
||||
// Rank returns a score for a CID token. Higher = more preferred as the
|
||||
// public-facing "canonical" identifier for a file. Unknown algorithms rank
|
||||
// below all known ones (0) so a plugin that invents a new digest type
|
||||
// can't accidentally outrank an established one until it's registered here.
|
||||
//
|
||||
// Ordering rationale (utility-weighted, NOT cryptographic-strength):
|
||||
// - IPFS CIDs win because the network around them — gateways, swarm,
|
||||
// content-addressing tooling — has the broadest reach. A cryptographically
|
||||
// equivalent sha256 in a non-CID form has less downstream value.
|
||||
// - sha256 / sha3 follow: standard, widely recognized, no platform lock-in.
|
||||
// - BitTorrent infohashes (btih) outrank midhash256 not for strength
|
||||
// (v1 is sha1, weaker than midhash256's sha256 inner) but for swarm
|
||||
// interop — a btih makes a file discoverable on existing torrent
|
||||
// infrastructure that knows nothing about MetaMesh.
|
||||
// - midhash256 is the floor. Internally meaningful, externally invisible.
|
||||
func Rank(token string) int {
|
||||
switch AlgorithmOf(token) {
|
||||
case "ipfs":
|
||||
return 40
|
||||
case "sha256", "sha3", "sha3-256", "sha3-384", "sha3-512":
|
||||
return 30
|
||||
case "btih", "btih-v2":
|
||||
return 20
|
||||
case "midhash256":
|
||||
return 10
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// AlgorithmOf returns the algorithm prefix of a CID token, or "" if the
|
||||
// token is malformed (no colon, or empty prefix).
|
||||
func AlgorithmOf(token string) string {
|
||||
colon := strings.IndexByte(token, ':')
|
||||
if colon <= 0 {
|
||||
return ""
|
||||
}
|
||||
return token[:colon]
|
||||
}
|
||||
|
||||
// Token assembles a CID token from its algorithm and raw value. Mirror of
|
||||
// AlgorithmOf — callers writing to the reverse index, cids set, or
|
||||
// canonical_cid field route through this so the format stays consistent.
|
||||
func Token(algorithm, raw string) string {
|
||||
if algorithm == "" || raw == "" {
|
||||
return ""
|
||||
}
|
||||
return algorithm + ":" + raw
|
||||
}
|
||||
|
||||
// ValueOf returns the raw value portion of a CID token (everything after
|
||||
// the first colon). Returns "" for malformed tokens.
|
||||
func ValueOf(token string) string {
|
||||
colon := strings.IndexByte(token, ':')
|
||||
if colon <= 0 || colon == len(token)-1 {
|
||||
return ""
|
||||
}
|
||||
return token[colon+1:]
|
||||
}
|
||||
|
||||
// Better picks the higher-ranked of two tokens. On ties, returns the
|
||||
// lexicographically smaller one — gives the reconciler a deterministic
|
||||
// answer so an unchanged canonical_cid doesn't churn when peers add the
|
||||
// same digest under a different ordering.
|
||||
func Better(a, b string) string {
|
||||
ra, rb := Rank(a), Rank(b)
|
||||
if ra > rb {
|
||||
return a
|
||||
}
|
||||
if rb > ra {
|
||||
return b
|
||||
}
|
||||
if a <= b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package cid
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestRank_Ordering(t *testing.T) {
|
||||
cases := []struct {
|
||||
token string
|
||||
want int
|
||||
}{
|
||||
{"ipfs:bafy", 40},
|
||||
{"sha256:bafk", 30},
|
||||
{"sha3-256:abc", 30},
|
||||
{"btih:def", 20},
|
||||
{"btih-v2:def", 20},
|
||||
{"midhash256:bafk", 10},
|
||||
{"unknown:xyz", 0},
|
||||
{"", 0},
|
||||
{"bare-no-colon", 0},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
if got := Rank(tc.token); got != tc.want {
|
||||
t.Errorf("Rank(%q) = %d, want %d", tc.token, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRank_StrictOrdering(t *testing.T) {
|
||||
// The exact numbers don't matter, but the inequality chain does — this
|
||||
// is what the canonical_cid reconciler relies on.
|
||||
ipfs := Rank("ipfs:x")
|
||||
sha := Rank("sha256:x")
|
||||
btih := Rank("btih:x")
|
||||
mid := Rank("midhash256:x")
|
||||
unknown := Rank("unknown:x")
|
||||
|
||||
if !(ipfs > sha && sha > btih && btih > mid && mid > unknown) {
|
||||
t.Fatalf("rank ordering broken: ipfs=%d sha=%d btih=%d mid=%d unknown=%d",
|
||||
ipfs, sha, btih, mid, unknown)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAlgorithmOf(t *testing.T) {
|
||||
cases := map[string]string{
|
||||
"ipfs:bafy": "ipfs",
|
||||
"sha256:bafk": "sha256",
|
||||
"midhash256:abc": "midhash256",
|
||||
"": "",
|
||||
"no-colon": "",
|
||||
":leading-colon": "", // empty algo not allowed
|
||||
}
|
||||
for in, want := range cases {
|
||||
if got := AlgorithmOf(in); got != want {
|
||||
t.Errorf("AlgorithmOf(%q) = %q, want %q", in, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestToken(t *testing.T) {
|
||||
if got := Token("sha256", "abc"); got != "sha256:abc" {
|
||||
t.Errorf("Token(sha256, abc) = %q", got)
|
||||
}
|
||||
if got := Token("", "abc"); got != "" {
|
||||
t.Errorf("Token with empty algo should return empty, got %q", got)
|
||||
}
|
||||
if got := Token("sha256", ""); got != "" {
|
||||
t.Errorf("Token with empty value should return empty, got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBetter(t *testing.T) {
|
||||
// IPFS beats sha256
|
||||
if got := Better("ipfs:a", "sha256:b"); got != "ipfs:a" {
|
||||
t.Errorf("Better(ipfs, sha256) = %q, want ipfs:a", got)
|
||||
}
|
||||
if got := Better("sha256:b", "ipfs:a"); got != "ipfs:a" {
|
||||
t.Errorf("Better(sha256, ipfs) = %q, want ipfs:a (order shouldn't matter)", got)
|
||||
}
|
||||
// Tie → lexicographically smaller wins, deterministic
|
||||
if got := Better("ipfs:b", "ipfs:a"); got != "ipfs:a" {
|
||||
t.Errorf("Better(ipfs:b, ipfs:a) = %q, want ipfs:a", got)
|
||||
}
|
||||
if got := Better("ipfs:a", "ipfs:b"); got != "ipfs:a" {
|
||||
t.Errorf("Better(ipfs:a, ipfs:b) = %q, want ipfs:a", got)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user