Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

pkgpath.gno

3.31 Kb · 110 lines
  1package gnopm
  2
  3import "strings"
  4
  5// Parsing a gno package path far enough to know who owns it.
  6//
  7// A path is "<domain>/<kind>/<namespace>/<name...>", optionally ending in a
  8// "/vN" version element: "gno.land/p/moul/md/v1", "gno.land/r/sys/users",
  9// "gno.land/r/g1abc.../scratch/v0". The namespace is the third element and it
 10// is the whole reason this file exists: it decides whether a claim comes from
 11// the party that controls the path or from a stranger, which is the strongest
 12// signal the registry can offer about a claim it can never itself verify.
 13
 14// SplitPkgPath breaks a package path into its domain, kind ("p" or "r"),
 15// namespace and the remainder. ok is false for anything that is not shaped like
 16// a publishable gno path.
 17func SplitPkgPath(s string) (domain, kind, ns, rest string, ok bool) {
 18	if !ValidPkgPath(s) {
 19		return "", "", "", "", false
 20	}
 21	parts := strings.Split(s, "/")
 22	return parts[0], parts[1], parts[2], strings.Join(parts[3:], "/"), true
 23}
 24
 25// Namespace returns the owning namespace of a package path, or "" if the path
 26// is not one.
 27func Namespace(s string) string {
 28	_, _, ns, _, ok := SplitPkgPath(s)
 29	if !ok {
 30		return ""
 31	}
 32	return ns
 33}
 34
 35// Version returns the trailing "vN" element of a package path, or "" when the
 36// path carries none. Absent is legal and is not an error: gnoweb serves
 37// gno.land/u/<name> by calling the realm at exactly /r/<name>/home, so that one
 38// path can never carry a version.
 39func Version(s string) string {
 40	parts := strings.Split(s, "/")
 41	if len(parts) < 4 {
 42		return ""
 43	}
 44	last := parts[len(parts)-1]
 45	if len(last) < 2 || last[0] != 'v' {
 46		return ""
 47	}
 48	for i := 1; i < len(last); i++ {
 49		if last[i] < '0' || last[i] > '9' {
 50			return ""
 51		}
 52	}
 53	return last
 54}
 55
 56// ValidPkgPath reports whether s is shaped like a publishable gno package path.
 57//
 58// It does NOT say the path exists on any chain, and cannot: a realm has no way
 59// to ask whether some other path was ever deployed. That gap is the registry's
 60// central honesty problem and is documented on Registry.
 61func ValidPkgPath(s string) bool {
 62	if s == "" || len(s) > MaxPkgPathLen {
 63		return false
 64	}
 65	if strings.HasPrefix(s, "/") || strings.HasSuffix(s, "/") || strings.Contains(s, "//") {
 66		return false
 67	}
 68	parts := strings.Split(s, "/")
 69	if len(parts) < 4 {
 70		return false
 71	}
 72	// The domain, which is a host and so must carry a dot.
 73	if !strings.Contains(parts[0], ".") {
 74		return false
 75	}
 76	if parts[1] != "p" && parts[1] != "r" {
 77		return false
 78	}
 79	for _, p := range parts {
 80		if p == "" || p == "." || p == ".." {
 81			return false
 82		}
 83		for i := 0; i < len(p); i++ {
 84			if !pathByteOK(p[i]) {
 85				return false
 86			}
 87		}
 88	}
 89	return true
 90}
 91
 92// AddressNamespace reports whether ns is shaped like a gno bech32 address, the
 93// namespace every account owns without registering anything. A caller still
 94// has to check it is the claimant's own address; this only says which of the
 95// two ownership rules applies.
 96func AddressNamespace(ns string) bool {
 97	if len(ns) != 40 || !strings.HasPrefix(ns, "g1") {
 98		return false
 99	}
100	for i := 2; i < len(ns); i++ {
101		if !strings.ContainsRune(bech32Charset, rune(ns[i])) {
102			return false
103		}
104	}
105	return true
106}
107
108// bech32Charset is bech32's data alphabet: lowercase alphanumerics minus the
109// four characters it drops to avoid transcription errors (1, b, i, o).
110const bech32Charset = "023456789acdefghjklmnpqrstuvwxyz"