← Back to Realms

Realm

gno.land/r/moul/x/plan9/dev/v0

Overview

Realm Path
gno.land/r/moul/x/plan9/dev/v0
Exported Functions
2
State Entries
2
Source Files
4
Total Package Entries
9

Exported Functions

2 exported functions

State

2 state entries

Source Code

FILES
dev.gno
go
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/moul/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/moul/x/plan9/ninep/v0"
46	synfs "gno.land/p/moul/x/plan9/synfs/v0"
47
48	nsrealm "gno.land/r/moul/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/moul/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}
182

Raw Package Data

Raw JSON data