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

lock.gno

2.35 Kb · 79 lines
 1// untrusted-render: successor is the only stored string Render echoes, and
 2// Retire validates it with assertPkgPath before storing it.
 3//
 4// Package lock is version 0 of the "retire the predecessor" upgrade pattern
 5// (pattern B of the exploration; see ../README.md).
 6//
 7// Unlike pattern A, this version knows it can be superseded. Retire() freezes
 8// it and names its successor, and the freeze is one-way: once a successor has
 9// absorbed this version's total, re-opening it here would double-count.
10package lock
11
12import (
13	"strings"
14	"gno.land/p/nt/ownable/v0"
15	"gno.land/p/nt/ufmt/v0"
16)
17
18const owner address = "g1manfred47kzduec920z88wfr64ylksmdcedlf5" // @moul
19
20var (
21	Ownable = ownable.NewWithAddress(owner)
22
23	counter   int
24	successor string // pkgpath that replaced this one; "" while this version is live
25)
26
27// Inc adds n. It aborts once a successor has been declared.
28func Inc(cur realm, n int) {
29	if successor != "" {
30		panic("lock/v0 is retired, use " + successor)
31	}
32	counter += n
33}
34
35// Get returns this version's final (or current) total.
36func Get() int {
37	return counter
38}
39
40// Successor is the path callers should move to, or "" while this version is live.
41func Successor() string {
42	return successor
43}
44
45// Retire freezes this version and names its replacement. Owner-gated, one-way.
46func Retire(cur realm, pkgPath string) {
47	Ownable.AssertOwnedBy(cur.Previous().Address())
48	if successor != "" {
49		panic("lock/v0 is already retired, successor is " + successor)
50	}
51	assertPkgPath(pkgPath)
52	successor = pkgPath
53}
54
55func Render(_ string) string {
56	if successor == "" {
57		return ufmt.Sprintf("lock/v0: %d (live)\n", counter)
58	}
59	return ufmt.Sprintf("lock/v0: %d (retired, use %s)\n", counter, successor)
60}
61
62// assertPkgPath rejects anything that is not a gno.land realm path.
63//
64// It exists for Render, not for correctness of the upgrade: the stored path is
65// echoed into markdown, so validating it at write time is what lets Render
66// print it raw. ui.Inline would escape the dots in "gno.land" and turn the one
67// string a reader needs to copy into "gno\.land".
68func assertPkgPath(p string) {
69	if !strings.HasPrefix(p, "gno.land/r/") {
70		panic("not a realm path: " + p)
71	}
72	for _, c := range p {
73		ok := c == '/' || c == '.' || c == '_' || c == '-' ||
74			(c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')
75		if !ok {
76			panic("illegal character in realm path")
77		}
78	}
79}