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

quest_host.gno

4.88 Kb · 162 lines
  1package memba_weighted_host
  2
  3import "gno.land/p/samcrew/memba_weighted_policy"
  4
  5// ApplicationPolicies is copied and validated by construction. No policy can
  6// subsequently be attached or changed on an existing host.
  7type ApplicationPolicies struct {
  8	Feedback FeedbackPolicy
  9	Channels ChannelsPolicy
 10	Feed     FeedPolicy
 11	Badges   BadgesPolicy
 12	Escrow   EscrowPolicy
 13	Appstore AppstorePolicy
 14	Arcade   ArcadePolicy
 15	Market   MarketPolicy
 16	Reviews  ReviewsPolicy
 17	Quest    QuestPolicy
 18}
 19
 20func NewWithApplications(seats []Seat, policies ApplicationPolicies, rlm realm) *Host {
 21	h := New(seats, rlm)
 22	if policies.Market != (MarketPolicy{}) {
 23		h = NewWithMarket(seats, policies.Market, rlm)
 24	}
 25	if policies.Reviews != (ReviewsPolicy{}) {
 26		h = attachReviews(h, policies.Reviews, rlm)
 27	}
 28	if policies.Quest != (QuestPolicy{}) {
 29		p := NewQuestPolicy(policies.Quest.DAO(), policies.Quest.Successor())
 30		if rlm.Address() != p.DAO() {
 31			panic("quest policy DAO does not own this host")
 32		}
 33		h.questPolicy = &p
 34	}
 35	if policies.Arcade != (ArcadePolicy{}) {
 36		p := NewArcadePolicy(policies.Arcade.DAO(), policies.Arcade.Successor())
 37		if rlm.Address() != p.DAO() {
 38			panic("arcade policy DAO does not own this host")
 39		}
 40		h.arcadePolicy = &p
 41	}
 42	if policies.Appstore != (AppstorePolicy{}) {
 43		p := NewAppstorePolicy(policies.Appstore.DAO(), policies.Appstore.Successor(), policies.Appstore.Treasury())
 44		if rlm.Address() != p.DAO() {
 45			panic("appstore policy DAO does not own this host")
 46		}
 47		h.appstorePolicy = &p
 48	}
 49	if policies.Escrow != (EscrowPolicy{}) {
 50		p := NewEscrowPolicy(policies.Escrow.DAO(), policies.Escrow.Successor())
 51		if rlm.Address() != p.DAO() {
 52			panic("escrow policy DAO does not own this host")
 53		}
 54		h.escrowPolicy = &p
 55	}
 56	if policies.Badges != (BadgesPolicy{}) {
 57		p := NewBadgesPolicy(policies.Badges.DAO(), policies.Badges.Successor())
 58		if rlm.Address() != p.DAO() {
 59			panic("badges policy DAO does not own this host")
 60		}
 61		h.badgesPolicy = &p
 62	}
 63	if policies.Feed != (FeedPolicy{}) {
 64		p := NewFeedPolicy(policies.Feed.DAO(), policies.Feed.Successor())
 65		if rlm.Address() != p.DAO() {
 66			panic("feed policy DAO does not own this host")
 67		}
 68		h.feedPolicy = &p
 69	}
 70	if policies.Channels != (ChannelsPolicy{}) {
 71		p := NewChannelsPolicy(policies.Channels.DAO(), policies.Channels.Successor())
 72		if rlm.Address() != p.DAO() {
 73			panic("channels policy DAO does not own this host")
 74		}
 75		h.channelsPolicy = &p
 76	}
 77	if policies.Feedback != (FeedbackPolicy{}) {
 78		p := NewFeedbackPolicy(policies.Feedback.DAO(), policies.Feedback.Successor())
 79		if rlm.Address() != p.DAO() {
 80			panic("feedback policy DAO does not own this host")
 81		}
 82		h.feedbackPolicy = &p
 83	}
 84	return h
 85}
 86
 87type QuestState struct {
 88	Owner, PendingOwner address
 89	Signer              string
 90}
 91type questProposal struct {
 92	action QuestAction
 93	before QuestState
 94}
 95
 96func questCommitment(a QuestAction, before QuestState) string {
 97	return a.Bytes() + "|before|" + questStateJSON(before)
 98}
 99func (h *Host) validateQuest(a QuestAction, before QuestState) {
100	if h.questPolicy == nil {
101		panic("quest actions are not configured")
102	}
103	a.Bytes()
104	p := h.questPolicy
105	if !before.Owner.IsValid() || (before.PendingOwner != "" && !before.PendingOwner.IsValid()) || (before.Signer != "" && !canonicalQuestKey(before.Signer)) {
106		panic("invalid quest pre-state")
107	}
108	if a.Kind() == QuestAcceptOwner {
109		if before.Owner == p.DAO() || before.PendingOwner != p.DAO() {
110			panic("DAO is not pending quest owner")
111		}
112		return
113	}
114	if before.Owner != p.DAO() {
115		panic("DAO is not quest owner")
116	}
117	if a.Kind() == QuestAbortReturn {
118		if a.Recipient() != p.Successor() || before.PendingOwner != p.Successor() {
119			panic("quest return does not match policy")
120		}
121		return
122	}
123	if before.PendingOwner != "" {
124		panic("quest authority transfer is pending")
125	}
126	switch a.Kind() {
127	case QuestSetSigner:
128		if before.Signer == a.Key() {
129			panic("quest signer change is a no-op")
130		}
131	case QuestReturnOwner:
132		if a.Recipient() != p.Successor() {
133			panic("quest successor does not match policy")
134		}
135	default:
136		panic("unsupported quest action")
137	}
138}
139func (h *Host) ProposeQuest(a QuestAction, before QuestState, rlm realm) uint64 {
140	who := assertCaller(h, rlm)
141	h.validateQuest(a, before)
142	p := &roleProposal{proposer: who, quest: &questProposal{action: a, before: before}}
143	p.ballot = h.policy.NewProposal(a.Category(), questCommitment(a, before))
144	h.addProposal(p)
145	return uint64(len(h.proposals))
146}
147func (h *Host) ConsumeQuest(id uint64, before QuestState, rlm realm) QuestAction {
148	assertCaller(h, rlm)
149	p := h.proposal(id)
150	if p.quest == nil {
151		panic("proposal is not a quest action")
152	}
153	h.validateQuest(p.quest.action, before)
154	p.ballot.Consume(questCommitment(p.quest.action, before))
155	members := []memba_weighted_policy.Member{}
156	for _, seat := range h.seats {
157		members = append(members, seat.Member)
158	}
159	h.policy.Reconfigure(members)
160	h.recordInvalidation(InvalidatedByExecution, id, QuestPath)
161	return p.quest.action
162}