-
Notifications
You must be signed in to change notification settings - Fork 17
/
hooks.go
183 lines (155 loc) · 5.7 KB
/
hooks.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
package keeper
import (
"fmt"
"github.com/cometbft/cometbft/libs/log"
sdk "github.com/cosmos/cosmos-sdk/types"
epochstypes "github.com/lum-network/chain/x/epochs/types"
"github.com/lum-network/chain/x/millions/types"
)
// BeforeEpochStart is a hook triggered every defined epoch
// Currently triggers the undelegation of epochUnbonding withdrawals
func (k Keeper) BeforeEpochStart(ctx sdk.Context, epochInfo epochstypes.EpochInfo) {
logger := k.Logger(ctx).With("ctx", "epoch_unbonding")
// Proceed daily based epoch
if epochInfo.Identifier == epochstypes.DAY_EPOCH {
k.processEpochUnbondings(ctx, epochInfo, logger)
}
}
func (k Keeper) processEpochUnbondings(ctx sdk.Context, epochInfo epochstypes.EpochInfo, logger log.Logger) (successCount, errorCount, skippedCount int) {
// Update the epoch tracker
epochTracker, err := k.UpdateEpochTracker(ctx, epochInfo, types.WithdrawalTrackerType)
if err != nil {
logger.Error(
fmt.Sprintf("Unable to update epoch tracker, err: %v", err),
"epoch_number", epochInfo.CurrentEpoch,
)
return
}
// Create temporary context
cacheCtx, writeCache := ctx.CacheContext()
// Get epoch unbondings
epochUnbondings := k.GetEpochUnbondings(cacheCtx, epochTracker.EpochNumber)
// For each unbonding, try to proceed the undelegation otherwise, rollback the entire operation
for _, epochUnbonding := range epochUnbondings {
success, err := k.processEpochUnbonding(cacheCtx, epochUnbonding, epochTracker, logger)
if err != nil {
logger.Error(
fmt.Sprintf("Error processing epoch unbonding: %v", err),
"pool_id", epochUnbonding.GetPoolId(),
"epoch_number", epochUnbonding.GetEpochNumber(),
)
errorCount++
break
} else if success {
successCount++
} else {
skippedCount++
}
}
// If there was an error, we are supposed to cancel the entire operation
if errorCount > 0 {
// Log out the critical error
logger.Error(
"epoch unbonding undelegate processed with errors, cache not written",
"nbr_success", successCount,
"nbr_error", errorCount,
"nbr_skipped", skippedCount,
)
// Allocate new cache context
rollbackTmpCacheCtx, writeRollbackCache := ctx.CacheContext()
// Get epoch unbondings
epochUnbondings = k.GetEpochUnbondings(rollbackTmpCacheCtx, epochTracker.EpochNumber)
// Rollback the operations and put everything back up in the next epoch
// We explicitly use a custom tailored cache for this operation
for _, epochUnbonding := range epochUnbondings {
for _, wid := range epochUnbonding.WithdrawalIds {
withdrawal, err := k.GetPoolWithdrawal(rollbackTmpCacheCtx, epochUnbonding.PoolId, wid)
if err != nil {
logger.Error(fmt.Sprintf("Failure in getting the pool withdrawals for pool %d, err: %v", epochUnbonding.PoolId, err))
return 0, 1, 0
}
if err := k.AddEpochUnbonding(rollbackTmpCacheCtx, withdrawal, false); err != nil {
logger.Error(fmt.Sprintf("Failure in adding the withdrawal to epoch unbonding for pool %d, err: %v", epochUnbonding.PoolId, err))
return 0, 1, 0
}
}
if err := k.RemoveEpochUnbonding(rollbackTmpCacheCtx, epochUnbonding); err != nil {
logger.Error(fmt.Sprintf("Failure in removing the epoch unbonding for pool %d, err: %v", epochUnbonding.PoolId, err))
return 0, 1, 0
}
}
// Write the rollback cache
writeRollbackCache()
logger.Info(
"epoch unbonding processed with failure, rollback done",
"nbr_success", successCount,
"nbr_error", errorCount,
"nbr_skipped", skippedCount,
)
} else {
// Write the operation succeed cache
writeCache()
logger.Info(
"epoch unbonding undelegate processed",
"nbr_success", successCount,
"nbr_error", errorCount,
"nbr_skipped", skippedCount,
)
}
return successCount, errorCount, skippedCount
}
func (k Keeper) processEpochUnbonding(ctx sdk.Context, epochUnbonding types.EpochUnbonding, epochTracker types.EpochTracker, logger log.Logger) (bool, error) {
pool, err := k.GetPool(ctx, epochUnbonding.GetPoolId())
if err != nil {
return false, err
}
// Epoch unbonding is supposed to happen every X where X is (unbonding_frequency/7)+1
// Modulo operation allows to ensure that this one can run
if pool.IsInvalidEpochUnbonding(epochTracker) {
logger.Info(
"Unbonding isn't supposed to trigger at this epoch",
"pool_id", epochUnbonding.PoolId,
"epoch_number", epochUnbonding.GetEpochNumber(),
)
// Fail safe - each withdrawal is added back to the next executable epoch unbonding
// Then we just remove the actual epoch entity
for _, wid := range epochUnbonding.WithdrawalIds {
withdrawal, err := k.GetPoolWithdrawal(ctx, epochUnbonding.PoolId, wid)
if err != nil {
return false, err
}
if err := k.AddEpochUnbonding(ctx, withdrawal, false); err != nil {
return false, err
}
}
if err := k.RemoveEpochUnbonding(ctx, epochUnbonding); err != nil {
return false, err
}
return false, nil
}
// In case everything worked fine, we just proceed as expected
// Undelegate the batch withdrawals to remote zone, then remove the actual epoch entity
if err := k.UndelegateWithdrawalsOnRemoteZone(ctx, epochUnbonding); err != nil {
return false, err
}
if err := k.RemoveEpochUnbonding(ctx, epochUnbonding); err != nil {
return false, err
}
return true, nil
}
func (k Keeper) AfterEpochEnd(ctx sdk.Context, epochInfo epochstypes.EpochInfo) {}
// Hooks wrapper struct for incentives keeper
type Hooks struct {
k Keeper
}
var _ epochstypes.EpochHooks = Hooks{}
func (k Keeper) Hooks() Hooks {
return Hooks{k}
}
// epochs hooks
func (h Hooks) BeforeEpochStart(ctx sdk.Context, epochInfo epochstypes.EpochInfo) {
h.k.BeforeEpochStart(ctx, epochInfo)
}
func (h Hooks) AfterEpochEnd(ctx sdk.Context, epochInfo epochstypes.EpochInfo) {
h.k.AfterEpochEnd(ctx, epochInfo)
}