owner.gno
3.28 Kb · 98 lines
1package gnosplit
2
3import (
4 "chain"
5 "strings"
6)
7
8// The realm's owner, the deployer, holds one power: naming the release that
9// takes over (SetSuccessor), once. It cannot touch a group. The role passes
10// by Transfer then Accept; Renounce drops it for good.
11var (
12 owner address // "" once renounced
13 pending address // the address a Transfer offered the role to
14 successor string // the pkgpath of the next release, "" until named
15)
16
17// setUp is init's owner half. Only `gno test` (chain "dev") runs with no
18// signer, and an owner "" holds no power.
19func setUp(deployer address, chainID string) {
20 if deployer == "" && chainID != "dev" {
21 panic("gnosplit: no owner: deploy gnosplit from an account")
22 }
23 owner = deployer
24}
25
26// Owner is the realm's owner, "" once renounced.
27func Owner() address { return owner }
28
29// Successor is the pkgpath of the release that takes groups over, "" if none.
30func Successor() string { return successor }
31
32func isOwner(a address) bool { return owner != "" && a == owner }
33
34// Transfer offers the role to another address, which takes it with Accept.
35func Transfer(cur realm, to address) {
36 if !isOwner(cur.Previous().Address()) {
37 panic("gnosplit: only the owner can transfer the role")
38 }
39 if !to.IsValid() {
40 panic("gnosplit: not an address: " + to.String())
41 }
42 pending = to
43 chain.Emit(EventOwnerOffered, "to", to.String())
44}
45
46// Accept takes the role a Transfer offered the caller.
47func Accept(cur realm) {
48 caller := cur.Previous().Address()
49 if owner == "" || pending == "" || caller != pending {
50 panic("gnosplit: no transfer of the role is offered to you")
51 }
52 owner, pending = caller, ""
53 chain.Emit(EventOwnerChanged, "owner", owner.String())
54}
55
56// Renounce gives the role up for good.
57func Renounce(cur realm) {
58 if !isOwner(cur.Previous().Address()) {
59 panic("gnosplit: only the owner can renounce the role")
60 }
61 owner, pending = "", ""
62 chain.Emit(EventOwnerChanged, "owner", "")
63}
64
65// SetSuccessor names the release that takes groups over, once: a realm under
66// this one's namespace, like gno.land/r/<ns>/gnosplit/v2. From then on that
67// realm can move a group with MoveOut; nothing moves by itself.
68func SetSuccessor(cur realm, pkgpath string) {
69 if !isOwner(cur.Previous().Address()) {
70 panic("gnosplit: only the owner can name a successor")
71 }
72 if successor != "" {
73 panic("gnosplit: the successor is already " + successor)
74 }
75 ns := self[:strings.LastIndex(self, "/")+1] // "gno.land/r/<ns>/gnosplit/"
76 if !strings.HasPrefix(pkgpath, ns) || pkgpath == self || len(pkgpath) > 100 || strings.ContainsAny(pkgpath, " :?#") {
77 panic("gnosplit: a successor is a sibling release under " + ns)
78 }
79 successor = pkgpath
80 chain.Emit(EventSuccessorSet, "pkgpath", pkgpath)
81}
82
83// MoveOut marks a group as taken over and returns it, as Export writes it.
84// Only the successor realm can call it, which decides when a group moves
85// (when one of its editors asks). The group stays readable here, frozen.
86func MoveOut(cur realm, group string) string {
87 prev := cur.Previous()
88 if successor == "" || !prev.IsCode() || prev.PkgPath() != successor {
89 panic("gnosplit: only the successor can move a group")
90 }
91 g := mustGroup(group)
92 if g.Moved {
93 panic("gnosplit: group " + group + " already moved")
94 }
95 g.Moved = true
96 chain.Emit(EventGroupMoved, "group", itoa(g.ID), "to", successor)
97 return Export(group)
98}