/
abci.go
197 lines (183 loc) · 6.45 KB
/
abci.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
package qgb
import (
"errors"
"time"
sdkerrors "cosmossdk.io/errors"
"github.com/celestiaorg/celestia-app/x/qgb/keeper"
"github.com/celestiaorg/celestia-app/x/qgb/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)
const (
oneDay = 24 * time.Hour
oneWeek = 7 * oneDay
// AttestationExpiryTime is the expiration time of an attestation. When this
// much time has passed after an attestation has been published, it will be
// pruned from state.
AttestationExpiryTime = 3 * oneWeek // 3 weeks
)
// SignificantPowerDifferenceThreshold is the threshold of change in the
// validator set power that would trigger the creation of a new valset
// request.
var SignificantPowerDifferenceThreshold = sdk.NewDecWithPrec(5, 2) // 0.05
// EndBlocker is called at the end of every block.
func EndBlocker(ctx sdk.Context, k keeper.Keeper) {
// we always want to create the valset at first so that if there is a new
// validator set, then it is the one responsible for signing from now on.
handleValsetRequest(ctx, k)
handleDataCommitmentRequest(ctx, k)
pruneAttestations(ctx, k)
}
func handleDataCommitmentRequest(ctx sdk.Context, k keeper.Keeper) {
setDataCommitmentAttestation := func() {
dataCommitment, err := k.NextDataCommitment(ctx)
if err != nil {
panic(sdkerrors.Wrap(err, "couldn't get current data commitment"))
}
err = k.SetAttestationRequest(ctx, &dataCommitment)
if err != nil {
panic(err)
}
}
dataCommitmentWindow := int64(k.GetDataCommitmentWindowParam(ctx))
// this will keep executing until all the needed data commitments are
// created and we catchup to the current height
for {
hasLatestDataCommitment, err := k.HasDataCommitmentInStore(ctx)
if err != nil {
panic(err)
}
if hasLatestDataCommitment {
// if the store already has a data commitment, we use it to check if
// we need to create a new data commitment
latestDataCommitment, err := k.GetLatestDataCommitment(ctx)
if err != nil {
panic(err)
}
if ctx.BlockHeight()-int64(latestDataCommitment.EndBlock) >= dataCommitmentWindow {
setDataCommitmentAttestation()
} else {
// the needed data commitments are already created and we need
// to wait for the next window to elapse
break
}
} else {
// if the store doesn't have a data commitment, we check if the
// window has passed to create a new data commitment
if ctx.BlockHeight() >= dataCommitmentWindow {
setDataCommitmentAttestation()
} else {
// the first data commitment window hasn't elapsed yet to create
// a commitment
break
}
}
}
}
func handleValsetRequest(ctx sdk.Context, k keeper.Keeper) {
// get the latest valsets to compare against
var latestValset *types.Valset
if k.CheckLatestAttestationNonce(ctx) && k.GetLatestAttestationNonce(ctx) != 0 {
var err error
latestValset, err = k.GetLatestValset(ctx)
if err != nil {
panic(err)
}
}
latestUnbondingHeight := k.GetLatestUnBondingBlockHeight(ctx)
significantPowerDiff := false
if latestValset != nil {
vs, err := k.GetCurrentValset(ctx)
if err != nil {
// this condition should only occur in the simulator ref :
// https://github.com/Gravity-Bridge/Gravity-Bridge/issues/35
if errors.Is(err, types.ErrNoValidators) {
ctx.Logger().Error("no bonded validators",
"cause", err.Error(),
)
return
}
panic(err)
}
intCurrMembers, err := types.BridgeValidators(vs.Members).ToInternal()
if err != nil {
panic(sdkerrors.Wrap(err, "invalid current valset members"))
}
intLatestMembers, err := types.BridgeValidators(latestValset.Members).ToInternal()
if err != nil {
panic(sdkerrors.Wrap(err, "invalid latest valset members"))
}
significantPowerDiff = intCurrMembers.PowerDiff(*intLatestMembers).GT(SignificantPowerDifferenceThreshold)
}
if (latestValset == nil) || (latestUnbondingHeight == uint64(ctx.BlockHeight())) || significantPowerDiff {
// if the conditions are true, put in a new validator set request to be
// signed and submitted to EVM
valset, err := k.GetCurrentValset(ctx)
if err != nil {
panic(err)
}
err = k.SetAttestationRequest(ctx, &valset)
if err != nil {
panic(err)
}
}
}
// pruneAttestations runs basic checks on saved attestations to see if we need
// to prune or not. Then, it prunes all expired attestations from state.
func pruneAttestations(ctx sdk.Context, k keeper.Keeper) {
// If the attestation nonce hasn't been initialized yet, no pruning is
// required
if !k.CheckLatestAttestationNonce(ctx) {
return
}
earliestAttestation, found, err := k.GetAttestationByNonce(ctx, k.GetEarliestAvailableAttestationNonce(ctx))
if err != nil {
ctx.Logger().Error("error getting earliest attestation for pruning", "err", err.Error())
return
}
if !found {
ctx.Logger().Error("couldn't find earliest attestation for pruning")
return
}
if earliestAttestation == nil {
ctx.Logger().Error("nil earliest attestation")
return
}
currentBlockTime := ctx.BlockTime()
latestAttestationNonce := k.GetLatestAttestationNonce(ctx)
var newEarliestAvailableNonce uint64
for newEarliestAvailableNonce = earliestAttestation.GetNonce(); newEarliestAvailableNonce < latestAttestationNonce; newEarliestAvailableNonce++ {
newEarliestAttestation, found, err := k.GetAttestationByNonce(ctx, newEarliestAvailableNonce)
if err != nil {
ctx.Logger().Error("error getting attestation for pruning", "nonce", newEarliestAvailableNonce, "err", err.Error())
return
}
if !found {
ctx.Logger().Error("couldn't find attestation for pruning", "nonce", newEarliestAvailableNonce)
return
}
if newEarliestAttestation == nil {
ctx.Logger().Error("nil attestation for pruning", "nonce", newEarliestAvailableNonce)
return
}
attestationExpirationTime := newEarliestAttestation.BlockTime().Add(AttestationExpiryTime)
if attestationExpirationTime.After(currentBlockTime) {
// the current attestation is unexpired so subsequent ones are also
// unexpired persist the new earliest available attestation nonce
break
}
k.DeleteAttestation(ctx, newEarliestAvailableNonce)
}
if newEarliestAvailableNonce > earliestAttestation.GetNonce() {
// some attestations were pruned and we need to update the state for it
k.SetEarliestAvailableAttestationNonce(ctx, newEarliestAvailableNonce)
ctx.Logger().Debug(
"pruned attestations from QGB store",
"count",
newEarliestAvailableNonce-earliestAttestation.GetNonce(),
"new_earliest_available_nonce",
newEarliestAvailableNonce,
"latest_attestation_nonce",
latestAttestationNonce,
)
}
}