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

feed_host.gno

3.42 Kb · 113 lines
  1package memba_weighted_host
  2
  3import "gno.land/p/samcrew/memba_weighted_policy"
  4
  5// Feed moderators are explicit grants. Ownership itself can moderate, so the
  6// outgoing owner must not retain a separate moderator grant after handoff.
  7type FeedState struct {
  8	Owner, PendingOwner                                                address
  9	Paused                                                             bool
 10	ModeratorCount                                                     int
 11	OwnerModerator, DAOModerator, SuccessorModerator, SubjectModerator bool
 12}
 13type feedProposal struct {
 14	action FeedAction
 15	before FeedState
 16}
 17
 18func feedCommitment(a FeedAction, s FeedState) string {
 19	return a.Bytes() + "|before|" + feedStateJSON(s)
 20}
 21func (h *Host) validateFeedAuthority(s FeedState) {
 22	if h.feedPolicy == nil {
 23		panic("feed actions are not configured")
 24	}
 25	if !s.Owner.IsValid() || (s.PendingOwner != "" && !s.PendingOwner.IsValid()) {
 26		panic("invalid feed authority pre-state")
 27	}
 28	if s.ModeratorCount < 0 {
 29		panic("invalid feed moderator count")
 30	}
 31}
 32func (h *Host) validateFeed(a FeedAction, s FeedState) {
 33	h.validateFeedAuthority(s)
 34	a.Bytes()
 35	p := h.feedPolicy
 36	if a.Subject() == "" && s.SubjectModerator {
 37		panic("unexpected feed subject pre-state")
 38	}
 39	if a.Kind() == FeedAcceptOwner {
 40		if s.Owner == p.DAO() || s.PendingOwner != p.DAO() || s.OwnerModerator {
 41			panic("DAO is not pending feed owner or previous owner retains moderation")
 42		}
 43		return
 44	}
 45	if s.Owner != p.DAO() {
 46		panic("DAO is not feed owner")
 47	}
 48	if a.Kind() == FeedAbortReturn {
 49		if a.Recipient() != p.Successor() || s.PendingOwner != p.Successor() {
 50			panic("feed return does not match policy")
 51		}
 52		return
 53	}
 54	if s.PendingOwner != "" {
 55		panic("feed authority transfer is pending")
 56	}
 57	switch a.Kind() {
 58	case FeedReturnOwner:
 59		if a.Recipient() != p.Successor() || s.DAOModerator {
 60			panic("feed successor mismatch or DAO retains moderation")
 61		}
 62	case FeedAddModerator:
 63		p.AddModerator(a.Subject())
 64		if s.SubjectModerator {
 65			panic("feed moderator grant is a no-op")
 66		}
 67	case FeedRemoveModerator:
 68		if !s.SubjectModerator {
 69			panic("feed moderator removal is a no-op")
 70		}
 71	case FeedUnpause:
 72		if !s.Paused {
 73			panic("feed unpause is a no-op")
 74		}
 75	default:
 76		panic("unsupported feed action")
 77	}
 78}
 79func (h *Host) ProposeFeed(a FeedAction, s FeedState, rlm realm) uint64 {
 80	who := assertCaller(h, rlm)
 81	h.validateFeed(a, s)
 82	p := &roleProposal{proposer: who, feed: &feedProposal{action: a, before: s}}
 83	p.ballot = h.policy.NewProposal(a.Category(), feedCommitment(a, s))
 84	h.addProposal(p)
 85	return uint64(len(h.proposals))
 86}
 87func (h *Host) invalidateFeedPeers(cause string, id uint64) {
 88	members := []memba_weighted_policy.Member{}
 89	for _, seat := range h.seats {
 90		members = append(members, seat.Member)
 91	}
 92	h.policy.Reconfigure(members)
 93	h.recordInvalidation(cause, id, FeedPath)
 94}
 95func (h *Host) ConsumeFeed(id uint64, s FeedState, rlm realm) FeedAction {
 96	assertCaller(h, rlm)
 97	p := h.proposal(id)
 98	if p.feed == nil {
 99		panic("proposal is not a feed action")
100	}
101	h.validateFeed(p.feed.action, s)
102	p.ballot.Consume(feedCommitment(p.feed.action, s))
103	h.invalidateFeedPeers(InvalidatedByExecution, id)
104	return p.feed.action
105}
106func (h *Host) PrepareFeedEmergencyPause(s FeedState, rlm realm) {
107	assertCaller(h, rlm)
108	h.validateFeedAuthority(s)
109	if s.Owner != h.feedPolicy.DAO() || s.Paused || s.SubjectModerator {
110		panic("feed emergency pause unavailable")
111	}
112	h.invalidateFeedPeers(InvalidatedByPause, 0)
113}