/
abci.go
173 lines (156 loc) · 5.72 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
package gravity
import (
"context"
sdkerrors "cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/palomachain/paloma/x/gravity/keeper"
log "github.com/sirupsen/logrus"
)
// EndBlocker is called at the end of every block
func EndBlocker(ctx context.Context, k keeper.Keeper) {
// slashing(ctx, k)
err := createBatch(ctx, k)
if err != nil {
log.Error(err)
}
err = attestationTally(ctx, k)
if err != nil {
log.Error(err)
}
err = cleanupTimedOutBatches(ctx, k)
if err != nil {
log.Error(err)
}
err = pruneAttestations(ctx, k)
if err != nil {
log.Error(err)
}
}
// Create a batch of transactions for each token
func createBatch(ctx context.Context, k keeper.Keeper) error {
sdkCtx := sdk.UnwrapSDKContext(ctx)
if sdkCtx.BlockHeight()%50 == 0 {
denoms, err := k.GetAllERC20ToDenoms(ctx)
if err != nil {
return err
}
for _, erc20ToDenom := range denoms {
tokenContract, err := k.GetERC20OfDenom(ctx, erc20ToDenom.GetChainReferenceId(), erc20ToDenom.Denom)
if err != nil {
return err
}
_, err = k.BuildOutgoingTXBatch(ctx, erc20ToDenom.ChainReferenceId, *tokenContract, keeper.OutgoingTxBatchSize)
if err != nil {
return sdkerrors.Wrap(err, "Could not build outgoing tx batch")
}
}
}
return nil
}
// Iterate over all attestations currently being voted on in order of nonce and
// "Observe" those who have passed the threshold. Break the loop once we see
// an attestation that has not passed the threshold
func attestationTally(ctx context.Context, k keeper.Keeper) error {
attmap, keys, err := k.GetAttestationMapping(ctx)
if err != nil {
return err
}
// This iterates over all keys (event nonces) in the attestation mapping. Each value contains
// a slice with one or more attestations at that event nonce. There can be multiple attestations
// at one event nonce when validators disagree about what event happened at that nonce.
for _, nonce := range keys {
// This iterates over all attestations at a particular event nonce.
// They are ordered by when the first attestation at the event nonce was received.
// This order is not important.
for _, att := range attmap[nonce] {
// We check if the event nonce is exactly 1 higher than the last attestation that was
// observed. If it is not, we just move on to the next nonce. This will skip over all
// attestations that have already been observed.
//
// Once we hit an event nonce that is one higher than the last observed event, we stop
// skipping over this conditional and start calling tryAttestation (counting votes)
// Once an attestation at a given event nonce has enough votes and becomes observed,
// every other attestation at that nonce will be skipped, since the lastObservedEventNonce
// will be incremented.
//
// Then we go to the next event nonce in the attestation mapping, if there is one. This
// nonce will once again be one higher than the lastObservedEventNonce.
// If there is an attestation at this event nonce which has enough votes to be observed,
// we skip the other attestations and move on to the next nonce again.
// If no attestation becomes observed, when we get to the next nonce, every attestation in
// it will be skipped. The same will happen for every nonce after that.
lastEventNonce, err := k.GetLastObservedEventNonce(ctx)
if err != nil {
return err
}
if nonce == uint64(lastEventNonce)+1 {
err := k.TryAttestation(ctx, &att) //nolint:gosec
if err != nil {
return err
}
}
}
}
return nil
}
// cleanupTimedOutBatches deletes batches that have passed their expiration
func cleanupTimedOutBatches(ctx context.Context, k keeper.Keeper) error {
sdkCtx := sdk.UnwrapSDKContext(ctx)
currentTime := uint64(sdkCtx.BlockTime().Unix())
batches, err := k.GetOutgoingTxBatches(ctx)
if err != nil {
return err
}
for _, batch := range batches {
if batch.BatchTimeout < currentTime {
err := k.CancelOutgoingTXBatch(ctx, batch.TokenContract, batch.BatchNonce)
if err != nil {
return sdkerrors.Wrap(err, "failed to cancel outgoing txbatch")
}
}
}
return nil
}
// Iterate over all attestations currently being voted on in order of nonce
// and prune those that are older than the current nonce and no longer have any
// use. This could be combined with create attestation and save some computation
// but (A) pruning keeps the iteration small in the first place and (B) there is
// already enough nuance in the other handler that it's best not to complicate it further
func pruneAttestations(ctx context.Context, k keeper.Keeper) error {
attmap, keys, err := k.GetAttestationMapping(ctx)
if err != nil {
return err
}
// we delete all attestations earlier than the current event nonce
// minus some buffer value. This buffer value is purely to allow
// frontends and other UI components to view recent oracle history
const eventsToKeep = 1000
lastNonce, err := k.GetLastObservedEventNonce(ctx)
if err != nil {
return err
}
var cutoff uint64
if lastNonce <= eventsToKeep {
return nil
} else {
cutoff = lastNonce - eventsToKeep
}
// This iterates over all keys (event nonces) in the attestation mapping. Each value contains
// a slice with one or more attestations at that event nonce. There can be multiple attestations
// at one event nonce when validators disagree about what event happened at that nonce.
for _, nonce := range keys {
// This iterates over all attestations at a particular event nonce.
// They are ordered by when the first attestation at the event nonce was received.
// This order is not important.
for _, att := range attmap[nonce] {
// delete all before the cutoff
if nonce < cutoff {
err := k.DeleteAttestation(ctx, att)
if err != nil {
return err
}
}
}
}
return nil
}