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

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

dev.gno

6.80 Kb · 181 lines
  1// Package dev is the chain as a Plan 9 device tree.
  2//
  3// In Plan 9 a device is not storage, it is code behind a name: reading
  4// /dev/time runs a function. Everything a gno realm normally reaches through
  5// an import of chain/runtime is published here as a file instead, so it can be
  6// read, listed, bound and unioned like anything else:
  7//
  8//	cat /dev/sysname     the chain id
  9//	cat /dev/height      the block height
 10//	cat /dev/session     what a delegated key is allowed to touch
 11//
 12// The tree is posted to gno.land/r/nym-moultest123/x/plan9/ns's /srv at deploy time, so
 13// any account can bind it into their own namespace, and nobody has to import
 14// this realm to use it. That is the cross-realm mount the whole experiment
 15// turns on: an interface value published by one realm, stored by another, and
 16// called later from a read-only Render.
 17//
 18// It is READ-ONLY, like every synthetic tree: the files have no Write, so
 19// grafting this into a stranger's namespace cannot be turned into a write
 20// against this realm.
 21//
 22// /dev/session is the one to look at. A gno.land account session is a
 23// delegated key scoped to a list of path prefixes, which is Plan 9's "the
 24// namespace IS the capability" rediscovered thirty-four years later. Printing
 25// it as a file makes that visible.
 26//
 27// NOTICE. Plan 9 from Bell Labs is the work of the Computing Science Research
 28// Center at Bell Labs; the name and the marks are theirs, and the copyright is
 29// held by the Plan 9 Foundation (https://p9f.org). This realm is not
 30// affiliated with, endorsed by, or sponsored by them, and contains no Plan 9
 31// code: it borrows the vocabulary so that the design reads without a glossary,
 32// and it is an homage, asking what that ecosystem's spirit looks like on a
 33// chain. Full attribution: NOTICE.md at the root of moul/gno-contracts.
 34package dev
 35
 36import (
 37	"chain/runtime"
 38	"chain/runtime/unsafe"
 39	"crypto/sha256"
 40	"encoding/hex"
 41	"strconv"
 42	"strings"
 43	"time"
 44
 45	ninep "gno.land/p/nym-moultest123/x/plan9/ninep/v0"
 46	synfs "gno.land/p/nym-moultest123/x/plan9/synfs/v0"
 47
 48	nsrealm "gno.land/r/nym-moultest123/x/plan9/ns/v0"
 49)
 50
 51var tree *synfs.Tree
 52
 53// docs describes each device, for Render and for /dev/drivers.
 54var docs = [][2]string{
 55	{"caller", "pkgpath of the realm that crossed into this read"},
 56	{"domain", "the chain domain"},
 57	{"drivers", "this table"},
 58	{"height", "current block height"},
 59	{"null", "always empty; the bit bucket"},
 60	{"random", "sha256 of chain id and height, hex; block-deterministic, NOT unpredictable"},
 61	{"session", "the calling key's session scope, one AllowPath per line"},
 62	{"sysname", "the chain id"},
 63	{"time", "block time, RFC3339"},
 64	{"user", "the origin caller's address"},
 65	{"zero", "endless NUL bytes; reads exactly what you ask for"},
 66}
 67
 68func init(cur realm) {
 69	tree = build()
 70	nsrealm.Post(cross(cur), "dev", tree.Root())
 71}
 72
 73func build() *synfs.Tree {
 74	t := synfs.New("dev", "sys", func() int64 { return runtime.ChainHeight() })
 75	r := t.Root()
 76
 77	r.Add("sysname", func() string { return runtime.ChainID() })
 78	r.Add("domain", func() string { return runtime.ChainDomain() })
 79	r.Add("height", func() string { return strconv.FormatInt(runtime.ChainHeight(), 10) })
 80	r.Add("time", func() string { return time.Now().Format(time.RFC3339) })
 81	r.Add("user", func() string { return unsafe.OriginCaller().String() })
 82	r.Add("caller", func() string { return unsafe.PreviousRealm().PkgPath() })
 83	r.Add("null", func() string { return "" })
 84	r.Add("random", func() string {
 85		sum := sha256.Sum256([]byte(runtime.ChainID() + ":" +
 86			strconv.FormatInt(runtime.ChainHeight(), 10)))
 87		return hex.EncodeToString(sum[:])
 88	})
 89	r.Add("session", session)
 90	r.Add("drivers", drivers)
 91
 92	// /dev/zero has no end, so it serves the read window itself rather than
 93	// materialising a value. An unbounded read returns nothing, which is what
 94	// stops `cat /dev/zero` from being a denial of service.
 95	r.AddRange("zero", 0444, func(off, count int64) (string, error) {
 96		if count <= 0 {
 97			return "", nil
 98		}
 99		if count > 4096 {
100			count = 4096
101		}
102		return strings.Repeat("\x00", int(count)), nil
103	})
104	return t
105}
106
107// session prints the calling key's authority. A plain key has none, which is
108// itself worth saying out loud.
109func session() string {
110	pubKeyAddr, expiresAt, allowPaths, isSession := runtime.GetSessionInfo()
111	if !isSession {
112		var b strings.Builder
113		b.WriteString("session no\n")
114		b.WriteString("scope full\n")
115		return b.String()
116	}
117	var b strings.Builder
118	b.WriteString("session yes\n")
119	b.WriteString("key " + pubKeyAddr.String() + "\n")
120	b.WriteString("expires " + strconv.FormatInt(expiresAt, 10) + "\n")
121	for _, p := range allowPaths {
122		b.WriteString("allow " + p + "\n")
123	}
124	return b.String()
125}
126
127func drivers() string {
128	var b strings.Builder
129	for _, d := range docs {
130		b.WriteString(d[0] + "\t" + d[1] + "\n")
131	}
132	return b.String()
133}
134
135// Root returns the device tree, for a realm that would rather import it than
136// bind it. Reading it is safe from anywhere; it has no mutating method.
137func Root() ninep.File { return tree.Root() }
138
139// Render lists the devices, or reads one.
140//
141//	Render("")            the table of devices
142//	Render("cat/height")  one device's contents
143func Render(path string) string {
144	parts := strings.Split(strings.Trim(path, "/"), "/")
145	if len(parts) >= 2 && parts[0] == "cat" {
146		name := parts[1]
147		f, err := tree.Root().Walk(name)
148		if err != nil {
149			return "# /dev/" + name + "\n\n```\n" + err.Error() + "\n```\n"
150		}
151		data, err := ninep.ReadAll(f)
152		if err != nil {
153			return "# /dev/" + name + "\n\n```\n" + err.Error() + "\n```\n"
154		}
155		return "# /dev/" + name + "\n\n```\n" + data + "\n```\n\n[all devices](:)\n"
156	}
157	return renderIndex()
158}
159
160func renderIndex() string {
161	var b strings.Builder
162	b.WriteString("# /dev\n\n")
163	b.WriteString("The chain as a Plan 9 device tree. Each file is a function: reading it ")
164	b.WriteString("runs code, so `/dev/height` is never stale.\n\n")
165	b.WriteString("Posted to [`r/moul/x/plan9/ns`](/r/nym-moultest123/x/plan9/ns/v0)'s `/srv` at deploy ")
166	b.WriteString("time, so no realm has to import this one to use it:\n\n")
167	b.WriteString("```\nbind /srv/dev /dev\n```\n\n")
168	b.WriteString("| device | contents |\n|---|---|\n")
169	for _, d := range docs {
170		b.WriteString("| [`" + d[0] + "`](:cat/" + d[0] + ") | " + d[1] + " |\n")
171	}
172	b.WriteString("\nDesign and analysis: ")
173	b.WriteString("[moul/gno-contracts#136](https://github.com/moul/gno-contracts/issues/136).\n")
174	b.WriteString("\n_Not affiliated with Plan 9. Plan 9 from Bell Labs is the ")
175	b.WriteString("work of the Computing Science Research Center at Bell Labs; the name ")
176	b.WriteString("and the marks are theirs, and the copyright is held by the ")
177	b.WriteString("[Plan 9 Foundation](https://p9f.org). This realm borrows the ")
178	b.WriteString("vocabulary and none of the code: it is an homage, asking what that ")
179	b.WriteString("ecosystem's spirit looks like on a chain._\n")
180	return b.String()
181}