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

root.gno

2.44 Kb · 81 lines
 1// untrusted-render: live is the only stored string Render echoes, and SetLive
 2// validates it with assertPkgPath before storing it.
 3//
 4// Package root is the data realm of the "state realm + swappable logic" upgrade
 5// pattern (pattern C of the exploration; see ../../README.md).
 6//
 7// root holds the state and nothing else worth changing. It grants write access
 8// to exactly one logic realm at a time, identified by package path off the
 9// crossing frame rather than passed in as an argument, so a logic realm cannot
10// claim to be a path it does not occupy. Upgrading is a single SetLive call:
11// no migration, no downtime, and the data never moves.
12package root
13
14import (
15	"strings"
16	"gno.land/p/nt/ownable/v0"
17	"gno.land/p/nt/ufmt/v0"
18)
19
20const owner address = "g1manfred47kzduec920z88wfr64ylksmdcedlf5" // @moul
21
22var (
23	Ownable = ownable.NewWithAddress(owner)
24
25	counter int
26	live    = "gno.land/r/moul/x/upgrade/store/logic/v0"
27)
28
29// Inc adds n to the stored counter. Only the live logic realm may call it.
30func Inc(cur realm, n int) int {
31	assertCallerIsLive(cur)
32	counter += n
33	return counter
34}
35
36// Get is open to everyone: the data is public, only writes are gated.
37func Get() int {
38	return counter
39}
40
41// Live is the package path currently allowed to write.
42func Live() string {
43	return live
44}
45
46// SetLive hands write access to another logic realm. This is the upgrade.
47func SetLive(cur realm, pkgPath string) {
48	Ownable.AssertOwnedBy(cur.Previous().Address())
49	assertPkgPath(pkgPath)
50	live = pkgPath
51}
52
53func assertCallerIsLive(cur realm) {
54	caller := cur.Previous().PkgPath()
55	if caller != live {
56		panic("unauthorized: " + caller + " is not the live logic realm (" + live + ")")
57	}
58}
59
60func Render(_ string) string {
61	return ufmt.Sprintf("store/root/v0: %d (live logic: %s)\n", counter, live)
62}
63
64// assertPkgPath rejects anything that is not a gno.land realm path.
65//
66// It exists for Render, not for correctness of the upgrade: the stored path is
67// echoed into markdown, so validating it at write time is what lets Render
68// print it raw. ui.Inline would escape the dots in "gno.land" and turn the one
69// string a reader needs to copy into "gno\.land".
70func assertPkgPath(p string) {
71	if !strings.HasPrefix(p, "gno.land/r/") {
72		panic("not a realm path: " + p)
73	}
74	for _, c := range p {
75		ok := c == '/' || c == '.' || c == '_' || c == '-' ||
76			(c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')
77		if !ok {
78			panic("illegal character in realm path")
79		}
80	}
81}