# BeginBlocker and EndBlocker

BeginBlocker and EndBlocker are optional methods module developers can implement in their module. They will be triggered at the beginning and at the end of each block respectively, when the BeginBlock and EndBlock ABCI messages are received from the underlying consensus engine.

# Pre-requisite Readings

# BeginBlocker and EndBlocker

BeginBlocker and EndBlocker are a way for module developers to add automatic execution of logic to their module. This is a powerful tool that should be used carefully, as complex automatic functions can slow down or even halt the chain.

When needed, BeginBlocker and EndBlocker are implemented as part of the AppModule interface. The BeginBlock and EndBlock methods of the interface implemented in module.go generally defer to BeginBlocker and EndBlocker methods respectively, which are usually implemented in a abci.go file.

The actual implementation of BeginBlocker and EndBlocker in ./abci.go are very similar to that of a handler:

  • They generally use the keeper and ctx to retrieve information about the latest state.
  • If needed, they use the keeper and ctx to trigger state-transitions.
  • If needed, they can emit events via the ctx's EventManager.

A specificity of the EndBlocker is that it can return validator updates to the underlying consensus engine in the form of an []abci.ValidatorUpdates. This is the preferred way to implement custom validator changes.

It is possible for developers to defined the order of execution between the BeginBlocker/EndBlocker functions of each of their application's modules via the module's manager SetOrderBeginBlocker/SetOrderEndBlocker methods. For more on the module manager, click here.

See an example implementation of BeginBlocker from the distr module:

Copy // BeginBlocker sets the proposer for determining distribution during endblock // and distribute rewards for the previous block func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k keeper.Keeper) { // determine the total power signing the block var previousTotalPower, sumPreviousPrecommitPower int64 for _, voteInfo := range req.LastCommitInfo.GetVotes() { previousTotalPower += voteInfo.Validator.Power if voteInfo.SignedLastBlock { sumPreviousPrecommitPower += voteInfo.Validator.Power } } // TODO this is Tendermint-dependent // ref https://github.com/cosmos/cosmos-sdk/issues/3095 if ctx.BlockHeight() > 1 { previousProposer := k.GetPreviousProposerConsAddr(ctx) k.AllocateTokens(ctx, sumPreviousPrecommitPower, previousTotalPower, previousProposer, req.LastCommitInfo.GetVotes()) } // record the proposer for when we payout on the next block consAddr := sdk.ConsAddress(req.Header.ProposerAddress) k.SetPreviousProposerConsAddr(ctx, consAddr) }

and an example implementation of EndBlocker from the staking module:

Copy func EndBlocker(ctx sdk.Context, k keeper.Keeper) []abci.ValidatorUpdate { // Calculate validator set changes. // // NOTE: ApplyAndReturnValidatorSetUpdates has to come before // UnbondAllMatureValidatorQueue. // This fixes a bug when the unbonding period is instant (is the case in // some of the tests). The test expected the validator to be completely // unbonded after the Endblocker (go from Bonded -> Unbonding during // ApplyAndReturnValidatorSetUpdates and then Unbonding -> Unbonded during // UnbondAllMatureValidatorQueue). validatorUpdates := k.ApplyAndReturnValidatorSetUpdates(ctx) // Unbond all mature validators from the unbonding queue. k.UnbondAllMatureValidatorQueue(ctx) // Remove all mature unbonding delegations from the ubd queue. matureUnbonds := k.DequeueAllMatureUBDQueue(ctx, ctx.BlockHeader().Time) for _, dvPair := range matureUnbonds { err := k.CompleteUnbonding(ctx, dvPair.DelegatorAddress, dvPair.ValidatorAddress) if err != nil { continue } ctx.EventManager().EmitEvent( sdk.NewEvent( types.EventTypeCompleteUnbonding, sdk.NewAttribute(types.AttributeKeyValidator, dvPair.ValidatorAddress.String()), sdk.NewAttribute(types.AttributeKeyDelegator, dvPair.DelegatorAddress.String()), ), ) } // Remove all mature redelegations from the red queue. matureRedelegations := k.DequeueAllMatureRedelegationQueue(ctx, ctx.BlockHeader().Time) for _, dvvTriplet := range matureRedelegations { err := k.CompleteRedelegation(ctx, dvvTriplet.DelegatorAddress, dvvTriplet.ValidatorSrcAddress, dvvTriplet.ValidatorDstAddress) if err != nil { continue } ctx.EventManager().EmitEvent( sdk.NewEvent( types.EventTypeCompleteRedelegation, sdk.NewAttribute(types.AttributeKeyDelegator, dvvTriplet.DelegatorAddress.String()), sdk.NewAttribute(types.AttributeKeySrcValidator, dvvTriplet.ValidatorSrcAddress.String()), sdk.NewAttribute(types.AttributeKeyDstValidator, dvvTriplet.ValidatorDstAddress.String()), ), ) } return validatorUpdates }

# Next

Learn about keepers