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

instance.gno

6.38 Kb · 207 lines
  1package governance
  2
  3import (
  4	"gno.land/p/gnoswap/utils/v1"
  5	bptree "gno.land/p/nt/bptree/v0"
  6	ufmt "gno.land/p/nt/ufmt/v0"
  7	"gno.land/r/gnoswap/gov/governance"
  8)
  9
 10type governanceV1 struct {
 11	store          governance.IGovernanceStore
 12	stakerAccessor governance.GovStakerAccessor
 13}
 14
 15// NewGovernanceV1 creates a governance v1 implementation backed by the supplied
 16// state store and staker voting-weight accessor.
 17//
 18// Parameters:
 19//   - governanceStore: persistence adapter for configuration, proposals, counters, and voting information
 20//   - stakerAccessor: accessor that supplies delegation snapshots and total xGNS supply for governance decisions
 21//
 22// Returns:
 23//   - governance.IGovernance: governance manager and getter implementation using the supplied dependencies
 24func NewGovernanceV1(
 25	governanceStore governance.IGovernanceStore,
 26	stakerAccessor governance.GovStakerAccessor,
 27) governance.IGovernance {
 28	return &governanceV1{
 29		store:          governanceStore,
 30		stakerAccessor: stakerAccessor,
 31	}
 32}
 33
 34// Config version methods
 35func (g *governanceV1) getCurrentConfigVersion() int64 {
 36	return g.store.GetConfigCounter().Get()
 37}
 38
 39// nextConfigVersion increments and persists the config counter. It mutates the
 40// underlying KV store, so it requires a realm value that resolves to the
 41// governance proxy realm (the only writer-permitted address).
 42func (g *governanceV1) nextConfigVersion(_ int, rlm realm) int64 {
 43	counter := g.store.GetConfigCounter()
 44	next := counter.Next()
 45	if err := g.store.SetConfigCounter(0, rlm, counter); err != nil {
 46		panic(err)
 47	}
 48	return next
 49}
 50
 51// Proposal ID methods
 52func (g *governanceV1) getCurrentProposalID() int64 {
 53	return g.store.GetProposalCounter().Get()
 54}
 55
 56// nextProposalID increments and persists the proposal counter. Same realm
 57// rules as nextConfigVersion.
 58func (g *governanceV1) nextProposalID(_ int, rlm realm) int64 {
 59	counter := g.store.GetProposalCounter()
 60	next := counter.Next()
 61	if err := g.store.SetProposalCounter(0, rlm, counter); err != nil {
 62		panic(err)
 63	}
 64	return next
 65}
 66
 67// Config methods
 68func (g *governanceV1) getConfig(version int64) (governance.Config, bool) {
 69	return g.store.GetConfig(version)
 70}
 71
 72func (g *governanceV1) setConfig(_ int, rlm realm, version int64, config governance.Config) error {
 73	return g.store.SetConfig(0, rlm, version, config)
 74}
 75
 76func (g *governanceV1) getCurrentConfig() (governance.Config, bool) {
 77	return g.getConfig(g.getCurrentConfigVersion())
 78}
 79
 80// Proposal methods
 81func (g *governanceV1) getProposal(id int64) (*governance.Proposal, bool) {
 82	proposal, exists := g.store.GetProposal(id)
 83	if !exists {
 84		return nil, false
 85	}
 86
 87	return proposal, true
 88}
 89
 90func activeProposalSnapshotKey(proposal *governance.Proposal) string {
 91	return utils.EncodeUint64(uint64(proposal.SnapshotTime())) + utils.EncodeUint64(uint64(proposal.ID()))
 92}
 93
 94func (g *governanceV1) addProposal(_ int, rlm realm, proposal *governance.Proposal) bool {
 95	// Set the proposal (ID already set in proposal)
 96	err := g.store.SetProposal(0, rlm, proposal.ID(), proposal)
 97	if err != nil {
 98		return false
 99	}
100
101	// Add to the active snapshot index.
102	snapshotIndex := g.store.GetActiveProposalsBySnapshot()
103	snapshotIndex.Set(activeProposalSnapshotKey(proposal), proposal.ID())
104	if err := g.store.SetActiveProposalsBySnapshot(0, rlm, snapshotIndex); err != nil {
105		return false
106	}
107
108	// Add to user proposals
109	err = g.store.AddUserProposal(0, rlm, proposal.Proposer().String(), proposal.ID())
110	if err != nil {
111		return false
112	}
113
114	return true
115}
116
117// removeActiveProposal removes a proposal from the active snapshot index.
118func (g *governanceV1) removeActiveProposal(_ int, rlm realm, proposal *governance.Proposal) error {
119	snapshotIndex := g.store.GetActiveProposalsBySnapshot()
120	key := activeProposalSnapshotKey(proposal)
121	if !snapshotIndex.Has(key) {
122		return makeErrorWithDetails(errDataNotFound, ufmt.Sprintf("active proposal index entry not found, proposalID: %d", proposal.ID()))
123	}
124	snapshotIndex.Remove(key)
125
126	return g.store.SetActiveProposalsBySnapshot(0, rlm, snapshotIndex)
127}
128
129// User proposals methods
130func (g *governanceV1) getUserProposals(user string) []*governance.Proposal {
131	proposalIDs, exists := g.store.GetUserProposalIDs(user)
132	if !exists {
133		return []*governance.Proposal{}
134	}
135
136	proposals := make([]*governance.Proposal, 0)
137
138	for _, id := range proposalIDs {
139		proposal, exists := g.store.GetProposal(id)
140		if !exists {
141			continue
142		}
143
144		proposals = append(proposals, proposal)
145	}
146
147	return proposals
148}
149
150// hasActiveProposal assumes removeInactiveUserProposals has already pruned stale proposals.
151func (g *governanceV1) hasActiveProposal(proposerAddress address) bool {
152	proposals := g.getUserProposals(proposerAddress.String())
153
154	return len(proposals) > 0
155}
156
157// Remove inactive user proposals
158// This function is used to remove inactive proposals from the user proposals list.
159// It is used to clean up user's active proposal list when creating a new proposal.
160func (g *governanceV1) removeInactiveUserProposals(_ int, rlm realm, proposerAddress address, current int64) error {
161	proposals := g.getUserProposals(proposerAddress.String())
162
163	for _, proposal := range proposals {
164		proposalResolver := NewProposalResolver(proposal)
165
166		if !proposalResolver.IsActive(current) {
167			err := g.store.RemoveUserProposal(0, rlm, proposerAddress.String(), proposal.ID())
168			if err != nil {
169				return err
170			}
171		}
172	}
173
174	return nil
175}
176
177// Proposal voting info methods
178func (g *governanceV1) getProposalUserVotingInfos(proposalID int64) (*bptree.BPTree, bool) {
179	return g.store.GetProposalVotingInfos(proposalID)
180}
181
182func (g *governanceV1) updateProposalUserVotes(_ int, rlm realm, proposal *governance.Proposal, userVotingInfos *bptree.BPTree) error {
183	return g.store.SetProposalVotingInfos(0, rlm, proposal.ID(), userVotingInfos)
184}
185
186// Helper methods for API
187func (g *governanceV1) mustGetProposal(id int64) *governance.Proposal {
188	proposal, exists := g.getProposal(id)
189	if !exists {
190		panic(makeErrorWithDetails(errProposalNotFound, ufmt.Sprintf("proposal(%d) not found", id)))
191	}
192	return proposal
193}
194
195func (g *governanceV1) getProposalUserVotingInfo(proposalID int64, addr address) (governance.VotingInfo, bool) {
196	votingInfosTree, exists := g.getProposalUserVotingInfos(proposalID)
197	if !exists {
198		return governance.DefaultVotingInfo(), false
199	}
200
201	votingInfoRaw := votingInfosTree.Get(addr.String())
202	if votingInfoRaw == nil {
203		return governance.DefaultVotingInfo(), false
204	}
205	votingInfo, ok := votingInfoRaw.(governance.VotingInfo)
206	return votingInfo, ok
207}