Search Apps Documentation Source Content File Folder Download Copy Actions Download

staker_delegation_snapshot.gno

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