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

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

registry.gno

4.73 Kb · 162 lines
  1package bolo
  2
  3import (
  4	"chain"
  5	"chain/runtime"
  6	"strings"
  7
  8	"gno.land/p/nt/avl/v0"
  9)
 10
 11const (
 12	registryPath  = "gno.land/r/g12yg9nh4ncma44emgm8msxe8aavzywt0p95tanv/bolo"
 13	versionPrefix = registryPath + "/v"
 14
 15	// maxVersions keeps Versions() to one bounded read (INV-9).
 16	maxVersions = 50
 17
 18	// maxPathLen is the longest package path the chain deploys: a longer one could
 19	// never hold a version.
 20	maxPathLen = 256
 21)
 22
 23// The registry's state (API §3.7), all unexported (C6).
 24var (
 25	admin        address = adminAddr // a realm once a DAO accepts the role (ADR D11)
 26	pendingAdmin address             // the proposed admin until it accepts; "" for none
 27
 28	versions []string // every version added, oldest first
 29	added    avl.Tree // path → true, for lookups
 30	current  string   // "" until the first SetCurrent
 31)
 32
 33// The registry's events (API §6).
 34const (
 35	evVersionAdded  = "VersionAdded"
 36	evCurrentSet    = "CurrentSet"
 37	evAdminProposed = "AdminProposed"
 38	evAdminChanged  = "AdminChanged"
 39)
 40
 41// Current returns the version new campaigns should use: a pkgpath, or "" before the
 42// first SetCurrent.
 43func Current() string { return current }
 44
 45// Versions returns every version ever added, oldest first, as a copy.
 46func Versions() []string { return append([]string(nil), versions...) }
 47
 48// GetAdmin returns the admin and the proposed admin, "" when there is none.
 49func GetAdmin() (address, address) { return admin, pendingAdmin }
 50
 51// AddVersion registers a version. None is ever removed.
 52func AddVersion(cur realm, path string) {
 53	assertCurrent(cur)
 54	if cur.Previous().IsUserCall() {
 55		runtime.AssertOriginCall()
 56	} else {
 57		assertCodeCaller(cur)
 58	}
 59	assertSigner(cur, admin)
 60	checkNewVersion(path)
 61	added.Set(path, true)
 62	versions = append(versions, path)
 63	chain.Emit(evVersionAdded, "path", path)
 64}
 65
 66// SetCurrent points new campaigns, and their donors' money, at a version already
 67// added.
 68func SetCurrent(cur realm, path string) {
 69	assertCurrent(cur)
 70	if cur.Previous().IsUserCall() {
 71		runtime.AssertOriginCall()
 72	} else {
 73		assertCodeCaller(cur)
 74	}
 75	assertSigner(cur, admin)
 76	if !added.Has(path) {
 77		panic(ErrSetting)
 78	}
 79	current = path
 80	chain.Emit(evCurrentSet, "path", path)
 81}
 82
 83// ProposeAdmin proposes next as the registry's admin, who takes over by accepting.
 84// A second proposal replaces the first.
 85func ProposeAdmin(cur realm, next address) {
 86	assertCurrent(cur)
 87	if cur.Previous().IsUserCall() {
 88		runtime.AssertOriginCall()
 89	} else {
 90		assertCodeCaller(cur)
 91	}
 92	assertSigner(cur, admin)
 93	checkCandidate(next)
 94	pendingAdmin = next
 95	chain.Emit(evAdminProposed, "next", next.String())
 96}
 97
 98// AcceptAdmin makes the proposed admin the admin, and ends the proposal.
 99func AcceptAdmin(cur realm) {
100	assertCurrent(cur)
101	if cur.Previous().IsUserCall() {
102		runtime.AssertOriginCall()
103	} else {
104		assertCodeCaller(cur)
105	}
106	assertSigner(cur, pendingAdmin)
107	previous := admin
108	admin, pendingAdmin = pendingAdmin, ""
109	chain.Emit(evAdminChanged, "admin", admin.String(), "previous", previous.String())
110}
111
112// Render is Bolo's front page on gnoweb, the twin of the web app's Home (UI spec §3,
113// §4.13): the name, the tagline, a link to the current version, then the versions,
114// oldest first, the current one marked. It ignores path: nothing a visitor types
115// reaches the page (API §3.7).
116func Render(path string) string {
117	if len(versions) == 0 {
118		return "# Bolo\n\nNo version is registered yet.\n"
119	}
120	var b strings.Builder
121	b.WriteString("# Bolo\n\nGive together. See where it goes.\n\n")
122	if current != "" {
123		b.WriteString("**[Open Bolo](" + strings.TrimPrefix(current, "gno.land") + ")**, where new campaigns start.\n\n")
124	}
125	b.WriteString("## Versions\n\nEach campaign runs to its end on the version it started on: money never moves between versions.\n\n")
126	for _, v := range versions {
127		b.WriteString("- [" + v + "](" + strings.TrimPrefix(v, "gno.land") + ")")
128		if v == current {
129			b.WriteString(" — current")
130		}
131		b.WriteString("\n")
132	}
133	return b.String()
134}
135
136// checkNewVersion refuses, with ErrSetting, a path that isn't a version path, one
137// already added, and one past the cap (API §3.7).
138func checkNewVersion(path string) {
139	if !isVersionPath(path) || added.Has(path) || len(versions) >= maxVersions {
140		panic(ErrSetting)
141	}
142}
143
144// isVersionPath reports whether path names a Bolo version (API §3.7), parsed by
145// hand: exactly versionPrefix, then 0 or a number without a leading zero, and
146// nothing else, in at most maxPathLen bytes. v012 would be another package than
147// v12, so it is refused.
148func isVersionPath(path string) bool {
149	if len(path) > maxPathLen || !strings.HasPrefix(path, versionPrefix) {
150		return false
151	}
152	n := path[len(versionPrefix):]
153	if n == "" || (n[0] == '0' && len(n) > 1) {
154		return false
155	}
156	for i := 0; i < len(n); i++ {
157		if n[i] < '0' || n[i] > '9' {
158			return false
159		}
160	}
161	return true
162}