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

appstore_host.gno

5.00 Kb · 170 lines
  1package memba_weighted_host
  2
  3import "gno.land/p/samcrew/memba_weighted_policy"
  4
  5type AppstoreListing struct {
  6	Exists                      bool
  7	ContentHash, Status, Reason string
  8	Credit                      bool
  9	Flags                       int
 10	ClearKeys                   string
 11}
 12type AppstoreState struct {
 13	Owner, PendingOwner, Treasury                                              address
 14	Fee                                                                        int64
 15	Paused, Sealed, OwnerCurator, DAOCurator, SuccessorCurator, SubjectCurator bool
 16	Listing                                                                    AppstoreListing
 17}
 18type appstoreProposal struct {
 19	action AppstoreAction
 20	before AppstoreState
 21}
 22
 23func appstoreCommitment(a AppstoreAction, s AppstoreState) string {
 24	return a.Bytes() + "|before|" + appstoreStateJSON(s)
 25}
 26func (h *Host) validateAppstoreAuthority(s AppstoreState) {
 27	if h.appstorePolicy == nil {
 28		panic("appstore actions are not configured")
 29	}
 30	if !s.Owner.IsValid() || !s.Treasury.IsValid() || (s.PendingOwner != "" && !s.PendingOwner.IsValid()) || s.Fee < 0 || s.Fee > AppstoreMaxFee {
 31		panic("invalid appstore pre-state")
 32	}
 33}
 34func (h *Host) validateAppstore(a AppstoreAction, s AppstoreState) {
 35	h.validateAppstoreAuthority(s)
 36	a.Bytes()
 37	p := h.appstorePolicy
 38	if !a.IsListing() && s.Listing != (AppstoreListing{}) {
 39		panic("unexpected appstore listing state")
 40	}
 41	if a.kind != AppstoreAddCurator && a.kind != AppstoreRemoveCurator && s.SubjectCurator {
 42		panic("unexpected appstore curator state")
 43	}
 44	if a.kind == AppstoreAccept {
 45		if s.Owner == p.DAO() || s.PendingOwner != p.DAO() {
 46			panic("DAO is not pending appstore owner")
 47		}
 48		return
 49	}
 50	if s.Owner != p.DAO() {
 51		panic("DAO is not appstore owner")
 52	}
 53	if a.kind == AppstoreAbortReturn {
 54		if a.recipient != p.Successor() || s.PendingOwner != p.Successor() {
 55			panic("appstore return does not match policy")
 56		}
 57		return
 58	}
 59	if s.PendingOwner != "" {
 60		panic("appstore authority transfer is pending")
 61	}
 62	if a.IsListing() {
 63		l := s.Listing
 64		if !s.DAOCurator {
 65			panic("DAO is not appstore curator")
 66		}
 67		if !l.Exists || l.ContentHash == "" || l.Flags < 0 {
 68			panic("invalid appstore listing")
 69		}
 70		switch a.kind {
 71		case AppstoreApprove:
 72			if l.Status != "pending" && l.Status != "rejected" {
 73				panic("appstore approval requires pending or rejected listing")
 74			}
 75		case AppstoreReject:
 76			if l.Status != "pending" {
 77				panic("appstore rejection requires pending listing")
 78			}
 79		case AppstoreDelist:
 80			if l.Status == "delisted" {
 81				panic("appstore delist is a no-op")
 82			}
 83		case AppstoreRestore:
 84			if l.Status != "delisted" {
 85				panic("appstore restore requires delisted listing")
 86			}
 87		case AppstoreClearFlags:
 88			if l.Flags <= 0 || l.ClearKeys == "" {
 89				panic("appstore has no removable flags")
 90			}
 91		}
 92		return
 93	}
 94	switch a.kind {
 95	case AppstoreReturn:
 96		if a.recipient != p.Successor() {
 97			panic("appstore successor does not match policy")
 98		}
 99	case AppstoreTreasury:
100		if a.recipient != p.Treasury() {
101			panic("appstore treasury does not match policy")
102		}
103		if s.Treasury == a.recipient {
104			panic("appstore treasury is a no-op")
105		}
106	case AppstoreFee:
107		if s.Fee == a.fee {
108			panic("appstore fee is a no-op")
109		}
110	case AppstoreAddCurator:
111		if s.SubjectCurator {
112			panic("appstore curator grant is a no-op")
113		}
114	case AppstoreRemoveCurator:
115		if !s.SubjectCurator {
116			panic("appstore curator removal is a no-op")
117		}
118	case AppstoreUnpause:
119		if !s.Paused {
120			panic("appstore unpause is a no-op")
121		}
122	case AppstoreSeal:
123		if s.Sealed {
124			panic("appstore import already sealed")
125		}
126	default:
127		panic("unsupported appstore action")
128	}
129}
130func (h *Host) ProposeAppstore(a AppstoreAction, s AppstoreState, rlm realm) uint64 {
131	who := assertCaller(h, rlm)
132	h.validateAppstore(a, s)
133	p := &roleProposal{proposer: who, appstore: &appstoreProposal{action: a, before: s}}
134	p.ballot = h.policy.NewProposal(a.Category(), appstoreCommitment(a, s))
135	h.addProposal(p)
136	return uint64(len(h.proposals))
137}
138func (h *Host) invalidateAppstorePeers(cause string, id uint64) {
139	members := []memba_weighted_policy.Member{}
140	for _, seat := range h.seats {
141		members = append(members, seat.Member)
142	}
143	h.policy.Reconfigure(members)
144	h.recordInvalidation(cause, id, AppstorePath)
145}
146func (h *Host) ConsumeAppstore(id uint64, s AppstoreState, rlm realm) AppstoreAction {
147	assertCaller(h, rlm)
148	p := h.proposal(id)
149	if p.appstore == nil {
150		panic("proposal is not an appstore action")
151	}
152	h.validateAppstore(p.appstore.action, s)
153	p.ballot.Consume(appstoreCommitment(p.appstore.action, s))
154	h.invalidateAppstorePeers(InvalidatedByExecution, id)
155	return p.appstore.action
156}
157func (h *Host) PrepareAppstoreEmergencyPause(s AppstoreState, rlm realm) {
158	assertCaller(h, rlm)
159	h.validateAppstoreAuthority(s)
160	if s.Owner != h.appstorePolicy.DAO() {
161		panic("DAO is not appstore owner")
162	}
163	if s.Paused {
164		panic("appstore is already paused")
165	}
166	if s.Listing != (AppstoreListing{}) || s.SubjectCurator {
167		panic("unexpected appstore pause state")
168	}
169	h.invalidateAppstorePeers(InvalidatedByPause, 0)
170}