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

staker_delegation_snapshot.gno

5.60 Kb · 186 lines
  1package staker
  2
  3import (
  4	"chain"
  5	"errors"
  6
  7	"gno.land/p/gnoswap/utils"
  8	"gno.land/r/gnoswap/access"
  9	"gno.land/r/gnoswap/halt"
 10)
 11
 12// SetUnDelegationLockupPeriodByAdmin sets the undelegation lockup period.
 13// This administrative function configures the time period that undelegated tokens
 14// must wait before they can be collected by users.
 15//
 16// The lockup period serves as a security mechanism to:
 17// - Prevent rapid delegation/undelegation cycles
 18// - Provide time for governance decisions to take effect
 19// - Maintain system stability during volatile periods
 20//
 21// Parameters:
 22//   - period: lockup period in seconds (must be non-negative)
 23//
 24// Panics:
 25//   - if caller is not admin
 26//   - if period is negative
 27//
 28// Note: This change affects all future undelegation operations
 29func (gs *govStakerV1) SetUnDelegationLockupPeriodByAdmin(_ int, rlm realm, period int64) {
 30	if !rlm.IsCurrent() {
 31		panic(errors.New(errSpoofedRealm))
 32	}
 33
 34	halt.AssertIsNotHaltedGovStaker()
 35
 36	prev := rlm.Previous()
 37	caller := prev.Address()
 38	access.AssertIsAdmin(caller)
 39
 40	if period < 0 {
 41		panic("period must be greater than 0")
 42	}
 43
 44	gs.setUnDelegationLockupPeriod(0, rlm, period)
 45
 46	chain.Emit(
 47		"SetUnDelegationLockupPeriod",
 48		"prevAddr", prev.Address().String(),
 49		"prevRealm", prev.PkgPath(),
 50		"period", utils.FormatInt(period),
 51	)
 52}
 53
 54// CleanStakerDelegationSnapshotByAdmin cleans old delegation history records.
 55// This administrative function removes delegation history records older than the specified threshold
 56// to prevent unlimited growth of historical data and optimize storage usage.
 57//
 58// The cleanup process:
 59// 1. Validates the snapshot time is within allowed range
 60// 2. Checks that no active proposals need data older than the cleanup threshold
 61// 3. Filters delegation history to keep only records after cutoff time
 62// 4. Updates the delegation history with filtered records
 63//
 64// Parameters:
 65//   - snapshotTime: cutoff timestamp (records older than this will be removed)
 66//   - target: the user address whose delegation history should be cleaned
 67//
 68// Panics:
 69//   - if caller is not admin
 70//   - if snapshotTime is invalid (negative or too recent)
 71//   - if active proposals have snapshotTime older than the cleanup threshold
 72func (gs *govStakerV1) CleanStakerDelegationSnapshotByAdmin(_ int, rlm realm, snapshotTime int64, target address) {
 73	if !rlm.IsCurrent() {
 74		panic(errors.New(errSpoofedRealm))
 75	}
 76
 77	halt.AssertIsNotHaltedGovStaker()
 78
 79	prev := rlm.Previous()
 80	caller := prev.Address()
 81	access.AssertIsAdmin(caller)
 82
 83	assertIsValidSnapshotTime(snapshotTime)
 84	assertIsAvailableCleanupSnapshotTime(snapshotTime)
 85
 86	// Clean total delegation history
 87	gs.cleanTotalDelegationHistory(0, rlm, snapshotTime)
 88
 89	// Clean user delegation history
 90	gs.cleanUserDelegationHistoryForAddress(0, rlm, target, snapshotTime)
 91
 92	chain.Emit(
 93		"CleanStakerDelegationSnapshot",
 94		"prevAddr", prev.Address().String(),
 95		"prevRealm", prev.PkgPath(),
 96		"snapshotTime", utils.FormatInt(snapshotTime),
 97		"target", target.String(),
 98	)
 99}
100
101// cleanTotalDelegationHistory removes total delegation history entries older than cutoff time.
102// Keeps the most recent entry before cutoff to preserve state continuity.
103func (gs *govStakerV1) cleanTotalDelegationHistory(_ int, rlm realm, cutoffTimestamp int64) {
104	history := gs.store.GetTotalDelegationHistory()
105
106	// First, find the most recent entry before cutoff to preserve state
107	var lastValue any
108
109	hasLastValue := false
110
111	history.ReverseIterate(0, cutoffTimestamp, func(timestamp int64, value any) bool {
112		lastValue = value
113		hasLastValue = true
114
115		return true // stop after first (most recent)
116	})
117
118	// If there was a value before cutoff, set it at cutoff time to preserve continuity
119	if hasLastValue && !history.Has(cutoffTimestamp) {
120		history.Set(cutoffTimestamp, lastValue)
121	}
122
123	// Collect keys to remove (cannot modify tree during iteration)
124	var keysToRemove []int64
125	history.Iterate(0, cutoffTimestamp, func(timestamp int64, _ any) bool {
126		keysToRemove = append(keysToRemove, timestamp)
127		return false // continue
128	})
129	for _, key := range keysToRemove {
130		history.Remove(key)
131	}
132
133	if err := gs.store.SetTotalDelegationHistory(0, rlm, history); err != nil {
134		panic(err)
135	}
136}
137
138// cleanUserDelegationHistoryForAddress removes user delegation history entries
139// older than cutoff time for a single target address.
140// Keeps the most recent entry strictly before cutoff
141// in place so range-based snapshot lookups continue to resolve correctly.
142func (gs *govStakerV1) cleanUserDelegationHistoryForAddress(_ int, rlm realm, target address, cutoffTimestamp int64) {
143	if cutoffTimestamp <= 0 {
144		return
145	}
146
147	history := gs.store.GetUserDelegationHistory()
148
149	addrStr := target.String()
150	iterationStartKey, _ := userHistoryKeyRange(addrStr)
151	iterationEndKey := makeUserHistoryKey(addrStr, cutoffTimestamp)
152
153	// First, find the most recent entry before cutoff to preserve state
154	var lastValue any
155
156	hasLastValue := false
157
158	history.ReverseIterate(iterationStartKey, iterationEndKey, func(key string, value any) bool {
159		lastValue = value
160		hasLastValue = true
161
162		return true // stop after first (most recent)
163	})
164
165	if hasLastValue && !history.Has(iterationEndKey) {
166		history.Set(iterationEndKey, lastValue)
167	}
168
169	// Collect all keys strictly before preserveKey within this address prefix.
170	// Iterate's end is exclusive, so [lo, preserveKey) skips the preserved entry.
171	var keysToRemove []string
172
173	history.Iterate(iterationStartKey, iterationEndKey, func(key string, _ any) bool {
174		keysToRemove = append(keysToRemove, key)
175
176		return false
177	})
178
179	for _, key := range keysToRemove {
180		history.Remove(key)
181	}
182
183	if err := gs.store.SetUserDelegationHistory(0, rlm, history); err != nil {
184		panic(err)
185	}
186}