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.99 Kb · 183 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	"gno.land/p/nym-moultest123/kit/ui/v0"
 46	"gno.land/p/nym-moultest123/md/v0"
 47	ninep "gno.land/p/nym-moultest123/x/plan9/ninep/v0"
 48	synfs "gno.land/p/nym-moultest123/x/plan9/synfs/v0"
 49
 50	nsrealm "gno.land/r/nym-moultest123/x/plan9/ns/v1"
 51)
 52
 53var tree *synfs.Tree
 54
 55// docs describes each device, for Render and for /dev/drivers.
 56var docs = [][2]string{
 57	{"caller", "pkgpath of the realm that crossed into this read"},
 58	{"domain", "the chain domain"},
 59	{"drivers", "this table"},
 60	{"height", "current block height"},
 61	{"null", "always empty; the bit bucket"},
 62	{"random", "sha256 of chain id and height, hex; block-deterministic, NOT unpredictable"},
 63	{"session", "the calling key's session scope, one AllowPath per line"},
 64	{"sysname", "the chain id"},
 65	{"time", "block time, RFC3339"},
 66	{"user", "the origin caller's address"},
 67	{"zero", "endless NUL bytes; reads exactly what you ask for"},
 68}
 69
 70func init(cur realm) {
 71	tree = build()
 72	nsrealm.Post(cross(cur), "dev", tree.Root())
 73}
 74
 75func build() *synfs.Tree {
 76	t := synfs.New("dev", "sys", func() int64 { return runtime.ChainHeight() })
 77	r := t.Root()
 78
 79	r.Add("sysname", func() string { return runtime.ChainID() })
 80	r.Add("domain", func() string { return runtime.ChainDomain() })
 81	r.Add("height", func() string { return strconv.FormatInt(runtime.ChainHeight(), 10) })
 82	r.Add("time", func() string { return time.Now().Format(time.RFC3339) })
 83	r.Add("user", func() string { return unsafe.OriginCaller().String() })
 84	r.Add("caller", func() string { return unsafe.PreviousRealm().PkgPath() })
 85	r.Add("null", func() string { return "" })
 86	r.Add("random", func() string {
 87		sum := sha256.Sum256([]byte(runtime.ChainID() + ":" +
 88			strconv.FormatInt(runtime.ChainHeight(), 10)))
 89		return hex.EncodeToString(sum[:])
 90	})
 91	r.Add("session", session)
 92	r.Add("drivers", drivers)
 93
 94	// /dev/zero has no end, so it serves the read window itself rather than
 95	// materialising a value. An unbounded read returns nothing, which is what
 96	// stops `cat /dev/zero` from being a denial of service.
 97	r.AddRange("zero", 0444, func(off, count int64) (string, error) {
 98		if count <= 0 {
 99			return "", nil
100		}
101		if count > 4096 {
102			count = 4096
103		}
104		return strings.Repeat("\x00", int(count)), nil
105	})
106	return t
107}
108
109// session prints the calling key's authority. A plain key has none, which is
110// itself worth saying out loud.
111func session() string {
112	pubKeyAddr, expiresAt, allowPaths, isSession := runtime.GetSessionInfo()
113	if !isSession {
114		var b strings.Builder
115		b.WriteString("session no\n")
116		b.WriteString("scope full\n")
117		return b.String()
118	}
119	var b strings.Builder
120	b.WriteString("session yes\n")
121	b.WriteString("key " + pubKeyAddr.String() + "\n")
122	b.WriteString("expires " + strconv.FormatInt(expiresAt, 10) + "\n")
123	for _, p := range allowPaths {
124		b.WriteString("allow " + p + "\n")
125	}
126	return b.String()
127}
128
129func drivers() string {
130	var b strings.Builder
131	for _, d := range docs {
132		b.WriteString(d[0] + "\t" + d[1] + "\n")
133	}
134	return b.String()
135}
136
137// Root returns the device tree, for a realm that would rather import it than
138// bind it. Reading it is safe from anywhere; it has no mutating method.
139func Root() ninep.File { return tree.Root() }
140
141// Render lists the devices, or reads one.
142//
143//	Render("")            the table of devices
144//	Render("cat/height")  one device's contents
145func Render(path string) string {
146	parts := strings.Split(strings.Trim(path, "/"), "/")
147	if len(parts) >= 2 && parts[0] == "cat" {
148		name := parts[1]
149		f, err := tree.Root().Walk(name)
150		if err != nil {
151			return "# /dev/" + ui.Inline(name) + "\n\n" + md.CodeBlock(err.Error())
152		}
153		data, err := ninep.ReadAll(f)
154		if err != nil {
155			return "# /dev/" + ui.Inline(name) + "\n\n" + md.CodeBlock(err.Error())
156		}
157		return "# /dev/" + ui.Inline(name) + "\n\n" + md.CodeBlock(strings.TrimSuffix(data, "\n")) + "\n[all devices](/r/nym-moultest123/x/plan9/dev/v1)\n"
158	}
159	return renderIndex()
160}
161
162func renderIndex() string {
163	var b strings.Builder
164	b.WriteString("# /dev\n\n")
165	b.WriteString("The chain as a Plan 9 device tree. Each file is a function: reading it ")
166	b.WriteString("runs code, so `/dev/height` is never stale.\n\n")
167	b.WriteString("Posted to [`r/moul/x/plan9/ns`](/r/nym-moultest123/x/plan9/ns/v1)'s `/srv` at deploy ")
168	b.WriteString("time, so no realm has to import this one to use it:\n\n")
169	b.WriteString("```\nbind /srv/dev /dev\n```\n\n")
170	b.WriteString("| device | contents |\n|---|---|\n")
171	for _, d := range docs {
172		b.WriteString("| [`" + d[0] + "`](/r/nym-moultest123/x/plan9/dev/v1:cat/" + d[0] + ") | " + d[1] + " |\n")
173	}
174	b.WriteString("\nDesign and analysis: ")
175	b.WriteString("[moul/gno-contracts#136](https://github.com/moul/gno-contracts/issues/136).\n")
176	b.WriteString("\n_Not affiliated with Plan 9. Plan 9 from Bell Labs is the ")
177	b.WriteString("work of the Computing Science Research Center at Bell Labs; the name ")
178	b.WriteString("and the marks are theirs, and the copyright is held by the ")
179	b.WriteString("[Plan 9 Foundation](https://p9f.org). This realm borrows the ")
180	b.WriteString("vocabulary and none of the code: it is an homage, asking what that ")
181	b.WriteString("ecosystem's spirit looks like on a chain._\n")
182	return b.String()
183}