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

vault.gno

3.82 Kb · 147 lines
  1package vault
  2
  3import (
  4	"chain"
  5	"strconv"
  6	"strings"
  7
  8	"gno.land/p/moul/kit/ui/v0"
  9	"gno.land/p/nt/avl/v0"
 10)
 11
 12// selfPath is this realm's own page. A root-relative link resolves against
 13// the domain, not the realm, so every link back into it names it in full.
 14const selfPath = "/r/moul/x/daily/vault/v1"
 15
 16// vaults maps address string -> *avl.Tree (key string -> value string).
 17var vaults avl.Tree
 18
 19// The bounds on what a caller may store. Anyone may open a vault, the root
 20// page lists every vault, and a vault's page lists every entry.
 21const (
 22	MaxVaults   = 1000
 23	MaxEntries  = 100 // per vault
 24	MaxKeyLen   = 64
 25	MaxValueLen = 512
 26)
 27
 28// vaultFor returns the caller's namespace tree, creating it if needed.
 29func vaultFor(owner address) *avl.Tree {
 30	k := owner.String()
 31	if v := vaults.Get(k); v != nil {
 32		return v.(*avl.Tree)
 33	}
 34	if vaults.Size() >= MaxVaults {
 35		panic("vault is full at " + strconv.Itoa(MaxVaults) + " vaults")
 36	}
 37	t := &avl.Tree{}
 38	vaults.Set(k, t)
 39	return t
 40}
 41
 42// Set stores value under key in the caller's own namespace.
 43func Set(cur realm, key string, value string) {
 44	who := caller(cur)
 45	if key == "" {
 46		panic("key must not be empty")
 47	}
 48	if len(key) > MaxKeyLen || len(value) > MaxValueLen {
 49		panic("key or value too long")
 50	}
 51	t := vaultFor(who)
 52	if !t.Has(key) && t.Size() >= MaxEntries {
 53		panic("a vault holds at most " + strconv.Itoa(MaxEntries) + " entries")
 54	}
 55	t.Set(key, value)
 56	chain.Emit("VaultSet", "owner", who.String(), "key", key)
 57}
 58
 59// Delete removes key from the caller's own namespace.
 60func Delete(cur realm, key string) {
 61	who := caller(cur)
 62	k := who.String()
 63	v := vaults.Get(k)
 64	if v == nil {
 65		panic("no vault for caller")
 66	}
 67	t := v.(*avl.Tree)
 68	if _, removed := t.Remove(key); !removed {
 69		panic("key not found")
 70	}
 71	if t.Size() == 0 {
 72		vaults.Remove(k)
 73	}
 74	chain.Emit("VaultDelete", "owner", who.String(), "key", key)
 75}
 76
 77// Render shows all vaults (root) or a single address's entries ("/<address>").
 78func Render(path string) string {
 79	p := strings.TrimPrefix(path, "/")
 80	if p == "" {
 81		return renderRoot()
 82	}
 83	return renderAddress(p)
 84}
 85
 86func renderRoot() string {
 87	var b strings.Builder
 88	b.WriteString("# Key-Value Vault\n\n")
 89	b.WriteString("A per-address key-value store. Each caller owns their own namespace; only the owner can mutate their entries.\n\n")
 90	if vaults.Size() == 0 {
 91		b.WriteString("_No vaults yet. Call `Set(cur, key, value)` to create one._\n")
 92		return b.String()
 93	}
 94	b.WriteString("## Vaults\n\n")
 95	b.WriteString("| Address | Entries |\n")
 96	b.WriteString("|---|---|\n")
 97	vaults.Iterate("", "", func(k string, v interface{}) bool {
 98		t := v.(*avl.Tree)
 99		b.WriteString("| [")
100		b.WriteString(k)
101		b.WriteString("](" + selfPath + ":")
102		// link back into this realm's render for the address subpage
103		b.WriteString(k)
104		b.WriteString(") | ")
105		b.WriteString(strconv.Itoa(t.Size()))
106		b.WriteString(" |\n")
107		return false
108	})
109	b.WriteString("\nView an address's entries at path `/<address>`.\n")
110	return b.String()
111}
112
113func renderAddress(addr string) string {
114	var b strings.Builder
115	b.WriteString("# Vault: ")
116	b.WriteString(ui.Inline(addr))
117	b.WriteString("\n\n")
118	v := vaults.Get(addr)
119	if v == nil {
120		b.WriteString("_No vault for this address._\n")
121		return b.String()
122	}
123	t := v.(*avl.Tree)
124	b.WriteString("| Key | Value |\n")
125	b.WriteString("|---|---|\n")
126	t.Iterate("", "", func(k string, val interface{}) bool {
127		b.WriteString("| ")
128		b.WriteString(ui.Cell(k))
129		b.WriteString(" | ")
130		b.WriteString(ui.Cell(val.(string)))
131		b.WriteString(" |\n")
132		return false
133	})
134	b.WriteString("\nTotal entries: ")
135	b.WriteString(strconv.Itoa(t.Size()))
136	b.WriteString("\n")
137	return b.String()
138}
139
140// caller is the address that crossed into this realm, after checking that the
141// realm token is this frame's own.
142func caller(cur realm) address {
143	if !cur.IsCurrent() {
144		panic("vault: spoofed realm")
145	}
146	return cur.Previous().Address()
147}