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

role.gno

4.34 Kb · 139 lines
  1// Package role is a role held by one address and passed in two steps
  2// (offer, accept), which its holder can cancel or renounce. A Role is pinned
  3// to the realm that made it (its home): every method that acts takes that
  4// realm's live cur and refuses any other value, so a *Role that leaked out of
  5// its realm can do nothing anywhere else.
  6package role
  7
  8import (
  9	"chain"
 10	"chain/runtime/unsafe"
 11)
 12
 13// Canonical reports whether a is a valid address in its one canonical
 14// spelling, lower case. The chain also takes the upper-case bech32 of an
 15// account, but realms compare and key addresses as strings, so every
 16// address argument that is stored, compared or paid to goes through this.
 17func Canonical(a address) bool {
 18	return !Upper(a) && a.IsValid()
 19}
 20
 21// Upper reports whether a is spelled in upper case. Bech32 is all upper or
 22// all lower case (mixed case is not IsValid), and an address starts with its
 23// letter prefix "g", so the first byte tells: a scan of the 40 bytes costs
 24// far more gas, strings.ToLower more still.
 25func Upper(a address) bool {
 26	s := a.String()
 27	return len(s) > 0 && s[0] >= 'A' && s[0] <= 'Z'
 28}
 29
 30// Role is kept by its home realm in an unexported variable.
 31type Role struct {
 32	home    string  // the realm that made it, the only one it answers
 33	name    string  // for events and messages ("owner", "guardian")
 34	holder  address // "" once renounced
 35	pending address // offered, "" if none
 36}
 37
 38// New makes a role held by holder and pinned to the calling realm. Call it
 39// from the realm's init.
 40func New(name string, holder address) *Role {
 41	if !Canonical(holder) {
 42		panic("role: not an address")
 43	}
 44	return &Role{home: unsafe.CurrentRealm().PkgPath(), name: name, holder: holder}
 45}
 46
 47// live checks that rlm is the home realm's runtime-current cur: a stale,
 48// stored, Previous() or foreign realm value is refused (Class 2). The _ int
 49// keeps the method non-crossing: v1.5.0 refuses crossing methods in /p/.
 50func (r *Role) live(_ int, rlm realm) {
 51	if !rlm.IsCurrent() || rlm.PkgPath() != r.home {
 52		panic("role: not the home realm's live cur")
 53	}
 54}
 55
 56// Holder is who holds the role ("" once renounced).
 57func (r *Role) Holder() address { return r.holder }
 58
 59// Pending is who was offered the role ("" if nobody).
 60func (r *Role) Pending() address {
 61	if r == nil { // no holder since a release renounced the role
 62		return ""
 63	}
 64	return r.pending
 65}
 66
 67// Is reports whether the immediate caller of the home realm holds the role.
 68func (r *Role) Is(_ int, rlm realm) bool {
 69	r.live(0, rlm)
 70	return r.holder != "" && rlm.Previous().Address() == r.holder
 71}
 72
 73// Must panics unless the immediate caller holds the role.
 74func (r *Role) Must(_ int, rlm realm) {
 75	if !r.Is(0, rlm) {
 76		panic("role: " + r.name + " only")
 77	}
 78}
 79
 80// Offer offers the role to to. Offering it to the holder cancels an offer.
 81func (r *Role) Offer(_ int, rlm realm, to address) {
 82	r.Must(0, rlm)
 83	if !Canonical(to) {
 84		panic("role: not an address")
 85	}
 86	if to == r.holder {
 87		r.pending = ""
 88		chain.Emit("RoleOfferCancelled", "role", r.name)
 89		return
 90	}
 91	r.pending = to
 92	chain.Emit("RoleOffered", "role", r.name, "to", to.String())
 93}
 94
 95// Accept takes the role offered to the caller.
 96func (r *Role) Accept(_ int, rlm realm) {
 97	r.live(0, rlm)
 98	c := rlm.Previous().Address()
 99	if r.holder == "" || r.pending == "" || c != r.pending {
100		panic("role: nothing offered to you")
101	}
102	from := r.holder
103	r.holder, r.pending = c, ""
104	chain.Emit("RoleChanged", "role", r.name, "from", from.String(), "to", c.String())
105}
106
107// Renounce gives the role up for good.
108func (r *Role) Renounce(_ int, rlm realm) {
109	r.Must(0, rlm)
110	r.holder, r.pending = "", ""
111	chain.Emit("RoleRenounced", "role", r.name)
112}
113
114// ---- the release handover: a realm's role and its mirror in data ----
115
116// Mirror is the holder: the mirror h when it exists (ok), as the previous
117// release may have changed it after r was made, else r's ("" once renounced).
118func Mirror(r *Role, h string, ok bool) address {
119	if ok {
120		return address(h)
121	}
122	if r == nil {
123		return ""
124	}
125	return r.holder
126}
127
128// Adopt is r once it agrees with the mirror h: unchanged without a mirror
129// (!ok) or when r already holds it, nil when the mirror says renounced,
130// else a new role named name for h, pinned to the calling realm (New).
131func Adopt(r *Role, name, h string, ok bool) *Role {
132	if !ok || r != nil && h == r.holder.String() {
133		return r
134	}
135	if h == "" {
136		return nil
137	}
138	return New(name, address(h))
139}