Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions 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}