-
Notifications
You must be signed in to change notification settings - Fork 0
/
intent.go
237 lines (198 loc) · 8.19 KB
/
intent.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
package keeper
import (
"errors"
"fmt"
sdkmath "cosmossdk.io/math"
"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/nephirim/blackfury/internal/multierror"
"github.com/nephirim/blackfury/utils/addressutils"
prtypes "github.com/nephirim/blackfury/x/claimsmanager/types"
"github.com/nephirim/blackfury/x/interchainstaking/types"
)
func (k *Keeper) getStoreKey(zone *types.Zone, snapshot bool) []byte {
if snapshot {
return append(types.KeyPrefixSnapshotIntent, []byte(zone.ChainId)...)
}
return append(types.KeyPrefixIntent, []byte(zone.ChainId)...)
}
// GetDelegatorIntent returns intent info by zone and delegator.
func (k *Keeper) GetDelegatorIntent(ctx sdk.Context, zone *types.Zone, delegator string, snapshot bool) (types.DelegatorIntent, bool) {
intent := types.DelegatorIntent{}
store := prefix.NewStore(ctx.KVStore(k.storeKey), k.getStoreKey(zone, snapshot))
bz := store.Get([]byte(delegator))
if len(bz) == 0 {
// usually we'd return false here, but we always want to return an empty intent if one doesn't exist; keep standard Get* interface for consistency.
return types.DelegatorIntent{Delegator: delegator, Intents: nil}, true
}
k.cdc.MustUnmarshal(bz, &intent)
return intent, true
}
// SetDelegatorIntent store the delegator intent.
func (k *Keeper) SetDelegatorIntent(ctx sdk.Context, zone *types.Zone, intent types.DelegatorIntent, snapshot bool) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), k.getStoreKey(zone, snapshot))
bz := k.cdc.MustMarshal(&intent)
store.Set([]byte(intent.Delegator), bz)
}
// DeleteDelegatorIntent deletes delegator intent.
func (k *Keeper) DeleteDelegatorIntent(ctx sdk.Context, zone *types.Zone, delegator string, snapshot bool) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), k.getStoreKey(zone, snapshot))
store.Delete([]byte(delegator))
}
// IterateDelegatorIntents iterate through delegator intents for a given zone.
func (k *Keeper) IterateDelegatorIntents(ctx sdk.Context, zone *types.Zone, snapshot bool, fn func(index int64, intent types.DelegatorIntent) (stop bool)) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), k.getStoreKey(zone, snapshot))
iterator := sdk.KVStorePrefixIterator(store, nil)
defer iterator.Close()
i := int64(0)
for ; iterator.Valid(); iterator.Next() {
intent := types.DelegatorIntent{}
k.cdc.MustUnmarshal(iterator.Value(), &intent)
stop := fn(i, intent)
if stop {
break
}
i++
}
}
// AllDelegatorIntents returns every intent in the store for the specified zone.
func (k *Keeper) AllDelegatorIntents(ctx sdk.Context, zone *types.Zone, snapshot bool) []types.DelegatorIntent {
var intents []types.DelegatorIntent
k.IterateDelegatorIntents(ctx, zone, snapshot, func(_ int64, intent types.DelegatorIntent) (stop bool) {
intents = append(intents, intent)
return false
})
return intents
}
// AllDelegatorIntentsAsPointer returns every intent in the store for the specified zone.
func (k *Keeper) AllDelegatorIntentsAsPointer(ctx sdk.Context, zone *types.Zone, snapshot bool) []*types.DelegatorIntent {
var intents []*types.DelegatorIntent
k.IterateDelegatorIntents(ctx, zone, snapshot, func(_ int64, intent types.DelegatorIntent) (stop bool) {
intents = append(intents, &intent)
return false
})
return intents
}
// AggregateDelegatorIntents takes a snapshot of delegator intents for a given zone.
func (k *Keeper) AggregateDelegatorIntents(ctx sdk.Context, zone *types.Zone) error {
snapshot := false
aggregate := make(types.ValidatorIntents, 0)
ordinalizedIntentSum := sdk.ZeroDec()
// reduce intents
k.IterateDelegatorIntents(ctx, zone, snapshot, func(_ int64, delIntent types.DelegatorIntent) (stop bool) {
// addr, localErr := sdk.AccAddressFromBech32(intent.Delegator)
// if localErr != nil {
// err = localErr
// return true
// }
// balance := k.BankKeeper.GetBalance(ctx, addr, zone.LocalDenom)
balance := sdk.NewCoin(zone.LocalDenom, sdkmath.ZeroInt())
// grab offchain asset value, and raise the users' base value by this amount.
// currently ignoring base value (locally held assets)
k.ClaimsManagerKeeper.IterateLastEpochUserClaims(ctx, zone.ChainId, delIntent.Delegator, func(index int64, data prtypes.Claim) (stop bool) {
balance.Amount = balance.Amount.Add(sdkmath.NewIntFromUint64(data.Amount))
// claim amounts are in zone.baseDenom - but given weights are all relative to one another this okay.
k.Logger(ctx).Error(
"intents - found claim for user",
"user", delIntent.Delegator,
"claim amount", data.Amount,
"new balance", balance.Amount,
)
return false
})
valIntents := delIntent.Ordinalize(sdk.NewDecFromInt(balance.Amount)).Intents
k.Logger(ctx).Error(
"intents - ordinalized",
"user", delIntent.Delegator,
"new balance", balance.Amount,
"normal intents", delIntent.Intents,
"intents", valIntents,
)
for idx := range valIntents.Sort() {
valIntent, found := aggregate.GetForValoper(valIntents[idx].ValoperAddress)
ordinalizedIntentSum = ordinalizedIntentSum.Add(valIntents[idx].Weight)
if !found {
aggregate = append(aggregate, valIntents[idx])
} else {
valIntent.Weight = valIntent.Weight.Add(valIntents[idx].Weight)
aggregate = aggregate.SetForValoper(valIntents[idx].ValoperAddress, valIntent)
}
}
return false
})
if len(aggregate) > 0 && ordinalizedIntentSum.IsZero() {
return errors.New("ordinalized intent sum is zero, this may happen if no claims are recorded")
}
// normalise aggregated intents again.
newAggregate := make(types.ValidatorIntents, 0)
for _, valIntent := range aggregate.Sort() {
if !valIntent.Weight.IsZero() && valIntent.Weight.IsPositive() {
valIntent.Weight = valIntent.Weight.Quo(ordinalizedIntentSum)
newAggregate = append(newAggregate, valIntent)
}
}
k.Logger(ctx).Info(
"aggregates",
"agg", newAggregate,
"chain", zone.ChainId,
)
zone.AggregateIntent = newAggregate
k.SetZone(ctx, zone)
return nil
}
// UpdateDelegatorIntent updates delegator intents.
func (k *Keeper) UpdateDelegatorIntent(ctx sdk.Context, delegator sdk.AccAddress, zone *types.Zone, inAmount sdk.Coins, memoIntent types.ValidatorIntents) error {
snapshot := false
updateWithCoin := inAmount.IsValid()
updateWithMemo := memoIntent != nil
// this is here because we need access to the bankKeeper to ordinalize intent
delIntent, _ := k.GetDelegatorIntent(ctx, zone, delegator.String(), snapshot)
// ordinalize
// this is the currently held amount
// not aligned with last epoch claims
// balance := k.BankKeeper.GetBalance(ctx, sender, zone.BaseDenom)
// if balance.Amount.IsNil() {
// balance.Amount = math.ZeroInt()
// }
claimAmt := sdkmath.ZeroInt()
// grab offchain asset value, and raise the users' base value by this amount.
k.ClaimsManagerKeeper.IterateLastEpochUserClaims(ctx, zone.ChainId, delegator.String(), func(index int64, claim prtypes.Claim) (stop bool) {
claimAmt = claimAmt.Add(sdkmath.NewIntFromUint64(claim.Amount))
k.Logger(ctx).Error("Update intents - found claim for user", "user", delIntent.Delegator, "claim amount", claim.Amount, "new balance", claimAmt)
return false
})
// inAmount is ordinal with respect to the redemption rate, so we must scale
baseBalance := zone.RedemptionRate.Mul(sdk.NewDecFromInt(claimAmt))
if baseBalance.IsZero() {
return nil
}
if updateWithCoin {
delIntent = zone.UpdateIntentWithCoins(delIntent, baseBalance, inAmount, k.GetValidatorAddresses(ctx, zone.ChainId))
}
if updateWithMemo {
delIntent = zone.UpdateZoneIntentWithMemo(memoIntent, delIntent, baseBalance)
}
if len(delIntent.Intents) == 0 {
return nil
}
k.SetDelegatorIntent(ctx, zone, delIntent, snapshot)
return nil
}
func (k msgServer) validateValidatorIntents(ctx sdk.Context, zone types.Zone, intents []*types.ValidatorIntent) error {
errMap := make(map[string]error)
for i, intent := range intents {
var valAddrBytes []byte
valAddrBytes, err := addressutils.ValAddressFromBech32(intent.ValoperAddress, zone.GetValoperPrefix())
if err != nil {
return err
}
_, found := k.GetValidator(ctx, zone.ChainId, valAddrBytes)
if !found {
errMap[fmt.Sprintf("intent[%v]", i)] = fmt.Errorf("unable to find valoper %s", intent.ValoperAddress)
}
}
if len(errMap) > 0 {
return multierror.New(errMap)
}
return nil
}