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

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}