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

arcade_host.gno

3.39 Kb · 116 lines
  1package memba_weighted_host
  2
  3import "gno.land/p/samcrew/memba_weighted_policy"
  4
  5// Explicit allowlist bits are separate from the owner's implicit attester right.
  6type ArcadeState struct {
  7	Owner, PendingOwner                                            address
  8	Paused, OwnerListed, DAOListed, SuccessorListed, SubjectListed bool
  9}
 10type arcadeProposal struct {
 11	action ArcadeAction
 12	before ArcadeState
 13}
 14
 15func arcadeCommitment(a ArcadeAction, s ArcadeState) string {
 16	return a.Bytes() + "|before|" + arcadeStateJSON(s)
 17}
 18func (h *Host) validateArcadeAuthority(s ArcadeState) {
 19	if h.arcadePolicy == nil {
 20		panic("arcade actions are not configured")
 21	}
 22	if !s.Owner.IsValid() || (s.PendingOwner != "" && !s.PendingOwner.IsValid()) {
 23		panic("invalid arcade authority pre-state")
 24	}
 25}
 26func (h *Host) validateArcade(a ArcadeAction, s ArcadeState) {
 27	h.validateArcadeAuthority(s)
 28	a.Bytes()
 29	p := h.arcadePolicy
 30	if a.Attester() == "" && s.SubjectListed {
 31		panic("unexpected arcade subject pre-state")
 32	}
 33	if a.Kind() == ArcadeAcceptOwner {
 34		if s.Owner == p.DAO() || s.PendingOwner != p.DAO() {
 35			panic("DAO is not pending arcade owner")
 36		}
 37		return
 38	}
 39	if s.Owner != p.DAO() {
 40		panic("DAO is not arcade owner")
 41	}
 42	if a.Kind() == ArcadeAbortReturn {
 43		if a.Recipient() != p.Successor() || s.PendingOwner != p.Successor() {
 44			panic("arcade return does not match policy")
 45		}
 46		return
 47	}
 48	if s.PendingOwner != "" {
 49		panic("arcade authority transfer is pending")
 50	}
 51	switch a.Kind() {
 52	case ArcadeReturnOwner:
 53		if a.Recipient() != p.Successor() {
 54			panic("arcade successor does not match policy")
 55		}
 56	case ArcadeAddAttester:
 57		p.AddAttester(a.Attester())
 58		if s.SubjectListed {
 59			panic("arcade attester grant is a no-op")
 60		}
 61	case ArcadeRemoveAttester:
 62		if !s.SubjectListed {
 63			panic("arcade attester removal is a no-op")
 64		}
 65	case ArcadeUnpause:
 66		if !s.Paused {
 67			panic("arcade unpause is a no-op")
 68		}
 69	default:
 70		panic("unsupported arcade action")
 71	}
 72}
 73func (h *Host) ProposeArcade(a ArcadeAction, s ArcadeState, rlm realm) uint64 {
 74	who := assertCaller(h, rlm)
 75	h.validateArcade(a, s)
 76	p := &roleProposal{proposer: who, arcade: &arcadeProposal{action: a, before: s}}
 77	p.ballot = h.policy.NewProposal(a.Category(), arcadeCommitment(a, s))
 78	h.addProposal(p)
 79	return uint64(len(h.proposals))
 80}
 81func (h *Host) invalidateArcadePeers(cause string, id uint64) {
 82	members := []memba_weighted_policy.Member{}
 83	for _, seat := range h.seats {
 84		members = append(members, seat.Member)
 85	}
 86	h.policy.Reconfigure(members)
 87	h.recordInvalidation(cause, id, ArcadePath)
 88}
 89func (h *Host) ConsumeArcade(id uint64, s ArcadeState, rlm realm) ArcadeAction {
 90	assertCaller(h, rlm)
 91	p := h.proposal(id)
 92	if p.arcade == nil {
 93		panic("proposal is not an arcade action")
 94	}
 95	h.validateArcade(p.arcade.action, s)
 96	p.ballot.Consume(arcadeCommitment(p.arcade.action, s))
 97	h.invalidateArcadePeers(InvalidatedByExecution, id)
 98	return p.arcade.action
 99}
100
101// A fixed pause-only capability, not a proposal category or a bool-controlled
102// escape. An already-paused call cannot invalidate pending unpause approvals.
103func (h *Host) PrepareArcadeEmergencyPause(s ArcadeState, rlm realm) {
104	assertCaller(h, rlm)
105	h.validateArcadeAuthority(s)
106	if s.Owner != h.arcadePolicy.DAO() {
107		panic("DAO is not arcade owner")
108	}
109	if s.Paused {
110		panic("arcade is already paused")
111	}
112	if s.SubjectListed {
113		panic("unexpected arcade subject pre-state")
114	}
115	h.invalidateArcadePeers(InvalidatedByPause, 0)
116}