← Back to Realms

Realm

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

Overview

Realm Path
gno.land/r/moul/x/plan9/dev/v1
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	"gno.land/p/moul/kit/ui/v0"
46	"gno.land/p/moul/md/v0"
47	ninep "gno.land/p/moul/x/plan9/ninep/v0"
48	synfs "gno.land/p/moul/x/plan9/synfs/v0"
49
50	nsrealm "gno.land/r/moul/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/moul/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/moul/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/moul/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}
184

Raw Package Data

Raw JSON data