forked from cosmos/cosmos-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
keeper.go
421 lines (365 loc) · 11.2 KB
/
keeper.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
package stake
import (
"bytes"
"encoding/json"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/bank"
abci "github.com/tendermint/abci/types"
)
// keeper of the staking store
type Keeper struct {
storeKey sdk.StoreKey
cdc *wire.Codec
coinKeeper bank.CoinKeeper
// caches
gs Pool
params Params
}
func NewKeeper(ctx sdk.Context, cdc *wire.Codec, key sdk.StoreKey, ck bank.CoinKeeper) Keeper {
keeper := Keeper{
storeKey: key,
cdc: cdc,
coinKeeper: ck,
}
return keeper
}
// InitGenesis - store genesis parameters
func (k Keeper) InitGenesis(ctx sdk.Context, data json.RawMessage) error {
var state GenesisState
if err := json.Unmarshal(data, &state); err != nil {
return err
}
k.setPool(ctx, state.Pool)
k.setParams(ctx, state.Params)
return nil
}
//_________________________________________________________________________
// get a single candidate
func (k Keeper) GetCandidate(ctx sdk.Context, addr sdk.Address) (candidate Candidate, found bool) {
store := ctx.KVStore(k.storeKey)
b := store.Get(GetCandidateKey(addr))
if b == nil {
return candidate, false
}
err := k.cdc.UnmarshalBinary(b, &candidate)
if err != nil {
panic(err)
}
return candidate, true
}
// Get the set of all candidates, retrieve a maxRetrieve number of records
func (k Keeper) GetCandidates(ctx sdk.Context, maxRetrieve int16) (candidates Candidates) {
store := ctx.KVStore(k.storeKey)
iterator := store.Iterator(subspace(CandidatesKey))
candidates = make([]Candidate, maxRetrieve)
i := 0
for ; ; i++ {
if !iterator.Valid() || i > int(maxRetrieve-1) {
iterator.Close()
break
}
bz := iterator.Value()
var candidate Candidate
err := k.cdc.UnmarshalBinary(bz, &candidate)
if err != nil {
panic(err)
}
candidates[i] = candidate
iterator.Next()
}
return candidates[:i] // trim
}
func (k Keeper) setCandidate(ctx sdk.Context, candidate Candidate) {
store := ctx.KVStore(k.storeKey)
address := candidate.Address
// retreive the old candidate record
oldCandidate, oldFound := k.GetCandidate(ctx, address)
// marshal the candidate record and add to the state
bz, err := k.cdc.MarshalBinary(candidate)
if err != nil {
panic(err)
}
store.Set(GetCandidateKey(candidate.Address), bz)
// mashal the new validator record
validator := candidate.validator()
bz, err = k.cdc.MarshalBinary(validator)
if err != nil {
panic(err)
}
// if the voting power is the same no need to update any of the other indexes
if oldFound && oldCandidate.Assets.Equal(candidate.Assets) {
return
}
// update the list ordered by voting power
if oldFound {
store.Delete(GetValidatorKey(address, oldCandidate.Assets, k.cdc))
}
store.Set(GetValidatorKey(address, validator.Power, k.cdc), bz)
// add to the validators to update list if is already a validator
// or is a new validator
setAcc := false
if store.Get(GetRecentValidatorKey(address)) != nil {
setAcc = true
// want to check in the else statement because inefficient
} else if k.isNewValidator(ctx, store, address) {
setAcc = true
}
if setAcc {
bz, err = k.cdc.MarshalBinary(validator.abciValidator(k.cdc))
if err != nil {
panic(err)
}
store.Set(GetAccUpdateValidatorKey(validator.Address), bz)
}
return
}
func (k Keeper) removeCandidate(ctx sdk.Context, address sdk.Address) {
// first retreive the old candidate record
candidate, found := k.GetCandidate(ctx, address)
if !found {
return
}
// delete the old candidate record
store := ctx.KVStore(k.storeKey)
store.Delete(GetCandidateKey(address))
store.Delete(GetValidatorKey(address, candidate.Assets, k.cdc))
// delete from recent and power weighted validator groups if the validator
// exists and add validator with zero power to the validator updates
if store.Get(GetRecentValidatorKey(address)) == nil {
return
}
bz, err := k.cdc.MarshalBinary(candidate.validator().abciValidatorZero(k.cdc))
if err != nil {
panic(err)
}
store.Set(GetAccUpdateValidatorKey(address), bz)
store.Delete(GetRecentValidatorKey(address))
}
//___________________________________________________________________________
// Get the validator set from the candidates. The correct subset is retrieved
// by iterating through an index of the candidates sorted by power, stored
// using the ValidatorsKey. Simultaniously the most recent the validator
// records are updated in store with the RecentValidatorsKey. This store is
// used to determine if a candidate is a validator without needing to iterate
// over the subspace as we do in GetValidators
func (k Keeper) GetValidators(ctx sdk.Context) (validators []Validator) {
store := ctx.KVStore(k.storeKey)
// clear the recent validators store, add to the ToKickOut Temp store
iterator := store.Iterator(subspace(RecentValidatorsKey))
for ; iterator.Valid(); iterator.Next() {
addr := AddrFromKey(iterator.Key())
// iterator.Value is the validator object
store.Set(GetToKickOutValidatorKey(addr), iterator.Value())
store.Delete(iterator.Key())
}
iterator.Close()
// add the actual validator power sorted store
maxValidators := k.GetParams(ctx).MaxValidators
iterator = store.ReverseIterator(subspace(ValidatorsKey)) // largest to smallest
validators = make([]Validator, maxValidators)
i := 0
for ; ; i++ {
if !iterator.Valid() || i > int(maxValidators-1) {
iterator.Close()
break
}
bz := iterator.Value()
var validator Validator
err := k.cdc.UnmarshalBinary(bz, &validator)
if err != nil {
panic(err)
}
validators[i] = validator
// remove from ToKickOut group
store.Delete(GetToKickOutValidatorKey(validator.Address))
// also add to the recent validators group
store.Set(GetRecentValidatorKey(validator.Address), bz)
iterator.Next()
}
// add any kicked out validators to the acc change
iterator = store.Iterator(subspace(ToKickOutValidatorsKey))
for ; iterator.Valid(); iterator.Next() {
key := iterator.Key()
addr := AddrFromKey(key)
// get the zero abci validator from the ToKickOut iterator value
bz := iterator.Value()
var validator Validator
err := k.cdc.UnmarshalBinary(bz, &validator)
if err != nil {
panic(err)
}
bz, err = k.cdc.MarshalBinary(validator.abciValidatorZero(k.cdc))
if err != nil {
panic(err)
}
store.Set(GetAccUpdateValidatorKey(addr), bz)
store.Delete(key)
}
iterator.Close()
return validators[:i] // trim
}
// TODO this is madly inefficient because need to call every time we set a candidate
// Should use something better than an iterator maybe?
// Used to determine if something has just been added to the actual validator set
func (k Keeper) isNewValidator(ctx sdk.Context, store sdk.KVStore, address sdk.Address) bool {
// add the actual validator power sorted store
maxVal := k.GetParams(ctx).MaxValidators
iterator := store.ReverseIterator(subspace(ValidatorsKey)) // largest to smallest
for i := 0; ; i++ {
if !iterator.Valid() || i > int(maxVal-1) {
iterator.Close()
break
}
bz := iterator.Value()
var val Validator
err := k.cdc.UnmarshalBinary(bz, &val)
if err != nil {
panic(err)
}
if bytes.Equal(val.Address, address) {
return true
}
iterator.Next()
}
return false
}
// Is the address provided a part of the most recently saved validator group?
func (k Keeper) IsRecentValidator(ctx sdk.Context, address sdk.Address) bool {
store := ctx.KVStore(k.storeKey)
if store.Get(GetRecentValidatorKey(address)) == nil {
return false
}
return true
}
//_________________________________________________________________________
// Accumulated updates to the validator set
// get the most recently updated validators
func (k Keeper) getAccUpdateValidators(ctx sdk.Context) (updates []abci.Validator) {
store := ctx.KVStore(k.storeKey)
iterator := store.Iterator(subspace(AccUpdateValidatorsKey)) //smallest to largest
for ; iterator.Valid(); iterator.Next() {
valBytes := iterator.Value()
var val abci.Validator
err := k.cdc.UnmarshalBinary(valBytes, &val)
if err != nil {
panic(err)
}
updates = append(updates, val)
}
iterator.Close()
return
}
// remove all validator update entries
func (k Keeper) clearAccUpdateValidators(ctx sdk.Context) {
store := ctx.KVStore(k.storeKey)
// delete subspace
iterator := store.Iterator(subspace(AccUpdateValidatorsKey))
for ; iterator.Valid(); iterator.Next() {
store.Delete(iterator.Key())
}
iterator.Close()
}
//_____________________________________________________________________
func (k Keeper) getDelegatorBond(ctx sdk.Context,
delegatorAddr, candidateAddr sdk.Address) (bond DelegatorBond, found bool) {
store := ctx.KVStore(k.storeKey)
delegatorBytes := store.Get(GetDelegatorBondKey(delegatorAddr, candidateAddr, k.cdc))
if delegatorBytes == nil {
return bond, false
}
err := k.cdc.UnmarshalBinary(delegatorBytes, &bond)
if err != nil {
panic(err)
}
return bond, true
}
// load all bonds of a delegator
func (k Keeper) getDelegatorBonds(ctx sdk.Context, delegator sdk.Address, maxRetrieve int16) (bonds []DelegatorBond) {
store := ctx.KVStore(k.storeKey)
delegatorPrefixKey := GetDelegatorBondsKey(delegator, k.cdc)
iterator := store.Iterator(subspace(delegatorPrefixKey)) //smallest to largest
bonds = make([]DelegatorBond, maxRetrieve)
i := 0
for ; ; i++ {
if !iterator.Valid() || i > int(maxRetrieve-1) {
iterator.Close()
break
}
bondBytes := iterator.Value()
var bond DelegatorBond
err := k.cdc.UnmarshalBinary(bondBytes, &bond)
if err != nil {
panic(err)
}
bonds[i] = bond
iterator.Next()
}
return bonds[:i] // trim
}
func (k Keeper) setDelegatorBond(ctx sdk.Context, bond DelegatorBond) {
store := ctx.KVStore(k.storeKey)
b, err := k.cdc.MarshalBinary(bond)
if err != nil {
panic(err)
}
store.Set(GetDelegatorBondKey(bond.DelegatorAddr, bond.CandidateAddr, k.cdc), b)
}
func (k Keeper) removeDelegatorBond(ctx sdk.Context, bond DelegatorBond) {
store := ctx.KVStore(k.storeKey)
store.Delete(GetDelegatorBondKey(bond.DelegatorAddr, bond.CandidateAddr, k.cdc))
}
//_______________________________________________________________________
// load/save the global staking params
func (k Keeper) GetParams(ctx sdk.Context) (params Params) {
// check if cached before anything
if k.params != (Params{}) {
return k.params
}
store := ctx.KVStore(k.storeKey)
b := store.Get(ParamKey)
if b == nil {
panic("Stored params should not have been nil")
}
err := k.cdc.UnmarshalBinary(b, ¶ms)
if err != nil {
panic(err)
}
return
}
func (k Keeper) setParams(ctx sdk.Context, params Params) {
store := ctx.KVStore(k.storeKey)
b, err := k.cdc.MarshalBinary(params)
if err != nil {
panic(err)
}
store.Set(ParamKey, b)
k.params = Params{} // clear the cache
}
//_______________________________________________________________________
// load/save the pool
func (k Keeper) GetPool(ctx sdk.Context) (gs Pool) {
// check if cached before anything
if k.gs != (Pool{}) {
return k.gs
}
store := ctx.KVStore(k.storeKey)
b := store.Get(PoolKey)
if b == nil {
panic("Stored pool should not have been nil")
}
err := k.cdc.UnmarshalBinary(b, &gs)
if err != nil {
panic(err) // This error should never occur big problem if does
}
return
}
func (k Keeper) setPool(ctx sdk.Context, p Pool) {
store := ctx.KVStore(k.storeKey)
b, err := k.cdc.MarshalBinary(p)
if err != nil {
panic(err)
}
store.Set(PoolKey, b)
k.gs = Pool{} // clear the cache
}