Skip to content

Commit

Permalink
epoching: event and hook upon a certain threshold amount of slashed v…
Browse files Browse the repository at this point in the history
…oting power (#35)
  • Loading branch information
SebastianElvis authored Jul 8, 2022
1 parent 66c0338 commit 8667398
Show file tree
Hide file tree
Showing 21 changed files with 1,314 additions and 238 deletions.
2 changes: 1 addition & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ func NewBabylonApp(
// register the staking hooks
// NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks
app.StakingKeeper = *stakingKeeper.SetHooks(
stakingtypes.NewMultiStakingHooks(app.DistrKeeper.Hooks(), app.SlashingKeeper.Hooks()),
stakingtypes.NewMultiStakingHooks(app.DistrKeeper.Hooks(), app.SlashingKeeper.Hooks(), app.EpochingKeeper.Hooks()),
)

app.AuthzKeeper = authzkeeper.NewKeeper(keys[authzkeeper.StoreKey], appCodec, app.BaseApp.MsgServiceRouter())
Expand Down
4 changes: 4 additions & 0 deletions x/epoching/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ func BeginBlocker(ctx sdk.Context, k keeper.Keeper, req abci.RequestBeginBlock)
if uint64(ctx.BlockHeight())-1 == epochBoundary.Uint64() {
// increase epoch number
incEpochNumber := k.IncEpochNumber(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, incEpochNumber)
// emit BeginEpoch event
Expand Down
8 changes: 6 additions & 2 deletions x/epoching/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,13 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState)
// set params for this module
k.SetParams(ctx, genState.Params)
// init epoch number
k.SetEpochNumber(ctx, sdk.NewUint(0))
k.InitEpochNumber(ctx)
// init msg queue length
k.SetQueueLength(ctx, sdk.NewUint(0))
k.InitQueueLength(ctx)
// init validator set
k.InitValidatorSet(ctx)
// init slashed voting power
k.InitSlashedVotingPower(ctx)
}

// ExportGenesis returns the capability module's exported genesis.
Expand Down
139 changes: 139 additions & 0 deletions x/epoching/keeper/epoch_msg_queue.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package keeper

import (
"github.com/babylonchain/babylon/x/epoching/types"
"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)

// InitQueueLength initialises the msg queue length to 0
func (k Keeper) InitQueueLength(ctx sdk.Context) {
store := ctx.KVStore(k.storeKey)

queueLenBytes, err := sdk.NewUint(0).Marshal()
if err != nil {
panic(sdkerrors.Wrap(types.ErrMarshal, err.Error()))
}

store.Set(types.QueueLengthKey, queueLenBytes)
}

// GetQueueLength fetches the number of queued messages
func (k Keeper) GetQueueLength(ctx sdk.Context) sdk.Uint {
store := ctx.KVStore(k.storeKey)

// get queue len in bytes from DB
bz := store.Get(types.QueueLengthKey)
if bz == nil {
panic(types.ErrUnknownQueueLen)
}
// unmarshal
var queueLen sdk.Uint
if err := queueLen.Unmarshal(bz); err != nil {
panic(sdkerrors.Wrap(types.ErrUnmarshal, err.Error()))
}

return queueLen
}

// setQueueLength sets the msg queue length
func (k Keeper) setQueueLength(ctx sdk.Context, queueLen sdk.Uint) {
store := ctx.KVStore(k.storeKey)

queueLenBytes, err := queueLen.Marshal()
if err != nil {
panic(sdkerrors.Wrap(types.ErrMarshal, err.Error()))
}

store.Set(types.QueueLengthKey, queueLenBytes)
}

// incQueueLength adds the queue length by 1
func (k Keeper) incQueueLength(ctx sdk.Context) {
queueLen := k.GetQueueLength(ctx)
incrementedQueueLen := queueLen.AddUint64(1)
k.setQueueLength(ctx, incrementedQueueLen)
}

// EnqueueMsg enqueues a message to the queue of the current epoch
func (k Keeper) EnqueueMsg(ctx sdk.Context, msg types.QueuedMessage) {
// prefix: QueuedMsgKey
store := ctx.KVStore(k.storeKey)
queuedMsgStore := prefix.NewStore(store, types.QueuedMsgKey)

// key: queueLenBytes
queueLen := k.GetQueueLength(ctx)
queueLenBytes, err := queueLen.Marshal()
if err != nil {
panic(sdkerrors.Wrap(types.ErrMarshal, err.Error()))
}
// value: msgBytes
msgBytes, err := k.cdc.Marshal(&msg)
if err != nil {
panic(sdkerrors.Wrap(types.ErrMarshal, err.Error()))
}
queuedMsgStore.Set(queueLenBytes, msgBytes)

// increment queue length
k.incQueueLength(ctx)
}

// GetEpochMsgs returns the set of messages queued in the current epoch
func (k Keeper) GetEpochMsgs(ctx sdk.Context) []*types.QueuedMessage {
queuedMsgs := []*types.QueuedMessage{}
store := ctx.KVStore(k.storeKey)

// add each queued msg to queuedMsgs
iterator := sdk.KVStorePrefixIterator(store, types.QueuedMsgKey)
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
queuedMsgBytes := iterator.Value()
var queuedMsg types.QueuedMessage
if err := k.cdc.Unmarshal(queuedMsgBytes, &queuedMsg); err != nil {
panic(sdkerrors.Wrap(types.ErrUnmarshal, err.Error()))
}
queuedMsgs = append(queuedMsgs, &queuedMsg)
}

return queuedMsgs
}

// ClearEpochMsgs removes all messages in the queue
func (k Keeper) ClearEpochMsgs(ctx sdk.Context) {
store := ctx.KVStore(k.storeKey)

// remove all epoch msgs
iterator := sdk.KVStorePrefixIterator(store, types.QueuedMsgKey)
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
key := iterator.Key()
store.Delete(key)
}

// set queue len to zero
k.setQueueLength(ctx, sdk.NewUint(0))
}

// HandleQueuedMsg unwraps a QueuedMessage and forwards it to the staking module
func (k Keeper) HandleQueuedMsg(ctx sdk.Context, msg *types.QueuedMessage) (*sdk.Result, error) {
var unwrappedMsgWithType sdk.Msg
// TODO: after we bump to Cosmos SDK v0.46, add MsgCancelUnbondingDelegation
switch unwrappedMsg := msg.Msg.(type) {
case *types.QueuedMessage_MsgCreateValidator:
unwrappedMsgWithType = unwrappedMsg.MsgCreateValidator
case *types.QueuedMessage_MsgDelegate:
unwrappedMsgWithType = unwrappedMsg.MsgDelegate
case *types.QueuedMessage_MsgUndelegate:
unwrappedMsgWithType = unwrappedMsg.MsgUndelegate
case *types.QueuedMessage_MsgBeginRedelegate:
unwrappedMsgWithType = unwrappedMsg.MsgBeginRedelegate
default:
panic(sdkerrors.Wrap(types.ErrInvalidQueuedMessageType, msg.String()))
}

// get the handler function from router
handler := k.router.Handler(unwrappedMsgWithType)
// handle the unwrapped message
return handler(ctx, unwrappedMsgWithType)
}
128 changes: 128 additions & 0 deletions x/epoching/keeper/epoch_slashed_val_set.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package keeper

import (
"github.com/babylonchain/babylon/x/epoching/types"
"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)

// setSlashedVotingPower sets the total amount of voting power that has been slashed in the epoch
func (k Keeper) setSlashedVotingPower(ctx sdk.Context, epochNumber sdk.Uint, power int64) {
store := k.slashedVotingPowerStore(ctx)

// key: epochNumber
epochNumberBytes, err := epochNumber.Marshal()
if err != nil {
panic(sdkerrors.Wrap(types.ErrMarshal, err.Error()))
}
// value: power
powerBytes, err := sdk.NewInt(power).Marshal()
if err != nil {
panic(sdkerrors.Wrap(types.ErrMarshal, err.Error()))
}

store.Set(epochNumberBytes, powerBytes)
}

// InitSlashedVotingPower sets the slashed voting power of the current epoch to 0
// This is called upon initialising the genesis state and upon a new epoch
func (k Keeper) InitSlashedVotingPower(ctx sdk.Context) {
epochNumber := k.GetEpochNumber(ctx)
k.setSlashedVotingPower(ctx, epochNumber, 0)
}

// GetSlashedVotingPower fetches the amount of slashed voting power of a given epoch
func (k Keeper) GetSlashedVotingPower(ctx sdk.Context, epochNumber sdk.Uint) int64 {
store := k.slashedVotingPowerStore(ctx)

// key: epochNumber
epochNumberBytes, err := epochNumber.Marshal()
if err != nil {
panic(sdkerrors.Wrap(types.ErrMarshal, err.Error()))
}
bz := store.Get(epochNumberBytes)
if bz == nil {
panic(types.ErrUnknownSlashedVotingPower)
}
// get value
var slashedVotingPower sdk.Int
if err := slashedVotingPower.Unmarshal(bz); err != nil {
panic(sdkerrors.Wrap(types.ErrUnmarshal, err.Error()))
}

return slashedVotingPower.Int64()
}

// AddSlashedValidator adds a slashed validator to the set of the current epoch
// This is called upon hook `BeforeValidatorSlashed` exposed by the staking module
func (k Keeper) AddSlashedValidator(ctx sdk.Context, valAddr sdk.ValAddress) {
epochNumber := k.GetEpochNumber(ctx)
store := k.slashedValSetStore(ctx, epochNumber)

// insert into "set of slashed addresses" as KV pair, where
// - key: valAddr
// - value: empty
store.Set(valAddr, []byte{})

// add voting power
slashedVotingPower := k.GetSlashedVotingPower(ctx, epochNumber)
thisVotingPower := k.GetValidatorVotingPower(ctx, epochNumber, valAddr)
k.setSlashedVotingPower(ctx, epochNumber, slashedVotingPower+thisVotingPower)
}

// GetSlashedValidators returns the set of slashed validators of a given epoch
func (k Keeper) GetSlashedValidators(ctx sdk.Context, epochNumber sdk.Uint) []sdk.ValAddress {
addrs := []sdk.ValAddress{}
store := k.slashedValSetStore(ctx, epochNumber)
// add each valAddr, which is the key
iterator := store.Iterator(nil, nil)
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
addr := sdk.ValAddress(iterator.Key())
addrs = append(addrs, addr)
}

return addrs
}

// ClearSlashedValidators removes all slashed validators in the set
// TODO: This is called upon the epoch is checkpointed
func (k Keeper) ClearSlashedValidators(ctx sdk.Context, epochNumber sdk.Uint) {
// prefix : SlashedValidatorSetKey || epochNumber
store := k.slashedValSetStore(ctx, epochNumber)

// remove all entries with this prefix
iterator := store.Iterator(nil, nil)
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
key := iterator.Key()
store.Delete(key)
}

// forget the slashed voting power of this epoch
epochNumberBytes, err := epochNumber.Marshal()
if err != nil {
panic(sdkerrors.Wrap(types.ErrMarshal, err.Error()))
}
k.slashedVotingPowerStore(ctx).Delete(epochNumberBytes)
}

// slashedValSetStore returns the KVStore of the slashed validator set for a given epoch
// prefix : SlashedValidatorSetKey || epochNumber
func (k Keeper) slashedValSetStore(ctx sdk.Context, epochNumber sdk.Uint) prefix.Store {
store := ctx.KVStore(k.storeKey)
slashedValStore := prefix.NewStore(store, types.SlashedValidatorSetKey)
epochNumberBytes, err := epochNumber.Marshal()
if err != nil {
panic(sdkerrors.Wrap(types.ErrMarshal, err.Error()))
}
return prefix.NewStore(slashedValStore, epochNumberBytes)
}

// slashedVotingPower returns the KVStore of the slashed voting power
// prefix: SlashedVotingPowerKey
func (k Keeper) slashedVotingPowerStore(ctx sdk.Context) prefix.Store {
store := ctx.KVStore(k.storeKey)
return prefix.NewStore(store, types.SlashedVotingPowerKey)
}
Loading

0 comments on commit 8667398

Please sign in to comment.