forked from babylonchain/babylon
-
Notifications
You must be signed in to change notification settings - Fork 0
/
abci.go
151 lines (138 loc) · 5.06 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
package epoching
import (
"context"
"fmt"
"time"
"github.com/babylonchain/babylon/x/epoching/keeper"
"github.com/babylonchain/babylon/x/epoching/types"
abci "github.com/cometbft/cometbft/abci/types"
"github.com/cosmos/cosmos-sdk/telemetry"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// BeginBlocker is called at the beginning of every block.
// Upon each BeginBlock,
// - record the current BlockHash
// - if reaching the epoch beginning, then
// - increment epoch number
// - trigger AfterEpochBegins hook
// - emit BeginEpoch event
//
// - if reaching the sealer header, i.e., the 2nd header of a non-zero epoch, then
// - record the sealer header for the previous epoch
//
// NOTE: we follow Cosmos SDK's slashing/evidence modules for MVP. No need to modify them at the moment.
func BeginBlocker(ctx context.Context, k keeper.Keeper) error {
defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyBeginBlocker)
sdkCtx := sdk.UnwrapSDKContext(ctx)
// record the current AppHash
k.RecordAppHash(ctx)
// if this block is the first block of the next epoch
// note that we haven't incremented the epoch number yet
epoch := k.GetEpoch(ctx)
if epoch.IsFirstBlockOfNextEpoch(ctx) {
// increase epoch number
incEpoch := k.IncEpoch(ctx)
// record the AppHash referencing
// the last block of the previous epoch
k.RecordSealerAppHashForPrevEpoch(ctx)
// init the msg queue of this new epoch
k.InitMsgQueue(ctx)
// init the slashed voting power of this new epoch
k.InitSlashedVotingPower(ctx)
// store the current validator set
k.InitValidatorSet(ctx)
// trigger AfterEpochBegins hook
k.AfterEpochBegins(ctx, incEpoch.EpochNumber)
// emit BeginEpoch event
err := sdkCtx.EventManager().EmitTypedEvent(
&types.EventBeginEpoch{
EpochNumber: incEpoch.EpochNumber,
},
)
if err != nil {
return err
}
}
if epoch.IsLastBlock(ctx) {
// record the block hash of the last block
// of the epoch to be sealed
k.RecordSealerBlockHashForEpoch(ctx)
}
return nil
}
// EndBlocker is called at the end of every block.
// If reaching an epoch boundary, then
// - forward validator-related msgs (bonded -> unbonding) to the staking module
// - trigger AfterEpochEnds hook
// - emit EndEpoch event
// NOTE: The epoching module is not responsible for checkpoint-assisted unbonding (unbonding -> unbonded). Instead, it wraps the staking module and exposes interfaces to the checkpointing module. The checkpointing module will do the actual checkpoint-assisted unbonding upon each EndBlock.
func EndBlocker(ctx context.Context, k keeper.Keeper) ([]abci.ValidatorUpdate, error) {
defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyEndBlocker)
sdkCtx := sdk.UnwrapSDKContext(ctx)
validatorSetUpdate := []abci.ValidatorUpdate{}
// if reaching an epoch boundary, then
epoch := k.GetEpoch(ctx)
if epoch.IsLastBlock(ctx) {
// finalise this epoch, i.e., record the current header and the Merkle root of all AppHashs in this epoch
if err := k.RecordLastHeaderAndAppHashRoot(ctx); err != nil {
return nil, err
}
// get all msgs in the msg queue
queuedMsgs := k.GetCurrentEpochMsgs(ctx)
// forward each msg in the msg queue to the right keeper
for _, msg := range queuedMsgs {
res, err := k.HandleQueuedMsg(ctx, msg)
// skip this failed msg and emit and event signalling it
// we do not panic here as some users may wrap an invalid message
// (e.g., self-delegate coins more than its balance, wrong coding of addresses, ...)
// honest validators will have consistent execution results on the queued messages
if err != nil {
// emit an event signalling the failed execution
err := sdkCtx.EventManager().EmitTypedEvent(
&types.EventHandleQueuedMsg{
EpochNumber: epoch.EpochNumber,
Height: msg.BlockHeight,
TxId: msg.TxId,
MsgId: msg.MsgId,
Error: err.Error(),
},
)
if err != nil {
return nil, err
}
// skip this failed msg
continue
}
// for each event, emit an wrapped event EventTypeHandleQueuedMsg, which attaches the original attributes plus the original event type, the epoch number, txid and msgid to the event here
for _, event := range res.Events {
err := sdkCtx.EventManager().EmitTypedEvent(
&types.EventHandleQueuedMsg{
OriginalEventType: event.Type,
EpochNumber: epoch.EpochNumber,
TxId: msg.TxId,
MsgId: msg.MsgId,
OriginalAttributes: event.Attributes,
},
)
if err != nil {
return nil, err
}
}
}
// update validator set
validatorSetUpdate = k.ApplyAndReturnValidatorSetUpdates(ctx)
sdkCtx.Logger().Info(fmt.Sprintf("Epoching: validator set update of epoch %d: %v", epoch.EpochNumber, validatorSetUpdate))
// trigger AfterEpochEnds hook
k.AfterEpochEnds(ctx, epoch.EpochNumber)
// emit EndEpoch event
err := sdkCtx.EventManager().EmitTypedEvent(
&types.EventEndEpoch{
EpochNumber: epoch.EpochNumber,
},
)
if err != nil {
return nil, err
}
}
return validatorSetUpdate, nil
}