diff --git a/keeper/keeper.go b/keeper/keeper.go index 636bcaa..d96be96 100644 --- a/keeper/keeper.go +++ b/keeper/keeper.go @@ -34,6 +34,7 @@ type Keeper struct { Params collections.Item[poa.Params] PendingValidators collections.Item[poa.Validators] UpdatedValidatorsCache collections.KeySet[string] + BeforeJailedValidators collections.KeySet[string] CachedBlockPower collections.Item[poa.PowerCache] AbsoluteChangedInBlockPower collections.Item[poa.PowerCache] @@ -63,6 +64,7 @@ func NewKeeper( Params: collections.NewItem(sb, poa.ParamsKey, "params", codec.CollValue[poa.Params](cdc)), PendingValidators: collections.NewItem(sb, poa.PendingValidatorsKey, "pending", codec.CollValue[poa.Validators](cdc)), UpdatedValidatorsCache: collections.NewKeySet(sb, poa.UpdatedValidatorsCacheKey, "updated_validators", collections.StringKey), + BeforeJailedValidators: collections.NewKeySet(sb, poa.BeforeJailedValidatorsKey, "before_jailed", collections.StringKey), CachedBlockPower: collections.NewItem(sb, poa.CachedPreviousBlockPowerKey, "cached_block", codec.CollValue[poa.PowerCache](cdc)), AbsoluteChangedInBlockPower: collections.NewItem(sb, poa.AbsoluteChangedInBlockPowerKey, "absolute_changed_power", codec.CollValue[poa.PowerCache](cdc)), diff --git a/keeper/poa.go b/keeper/poa.go index 346889b..6aaf7e1 100644 --- a/keeper/poa.go +++ b/keeper/poa.go @@ -88,6 +88,8 @@ func (k Keeper) SetPOAPower(ctx context.Context, valOpBech32 string, newShares i return stakingtypes.Validator{}, fmt.Errorf("issue getting consensus pubkey for %s", valOpBech32) } + consAddr := sdk.GetConsAddress(pk) + height := sdk.UnwrapSDKContext(ctx).BlockHeight() normalizedToken := k.stakingKeeper.TokensFromConsensusPower(ctx, currentPower) @@ -95,8 +97,16 @@ func (k Keeper) SetPOAPower(ctx context.Context, valOpBech32 string, newShares i if _, err := k.stakingKeeper.Slash(ctx, sdk.GetConsAddress(pk), height, normalizedToken.Int64(), sdkmath.LegacyOneDec()); err != nil { return stakingtypes.Validator{}, err } - - // TODO: delete the validator power index, or staking will handle? + // TODO: + if err := k.stakingKeeper.DeleteLastValidatorPower(ctx, valAddr); err != nil { + return stakingtypes.Validator{}, err + } + if err := k.stakingKeeper.DeleteValidatorByPowerIndex(ctx, val); err != nil { + return stakingtypes.Validator{}, err + } + if err := k.slashKeeper.DeleteMissedBlockBitmap(ctx, consAddr); err != nil { + return stakingtypes.Validator{}, err + } } else { // Sets the new consensus power for the validator (this is executed in the x/staking ApplyAndReturnValidatorUpdates method) diff --git a/keeper/staking_hooks.go b/keeper/staking_hooks.go new file mode 100644 index 0000000..1a17969 --- /dev/null +++ b/keeper/staking_hooks.go @@ -0,0 +1,84 @@ +package keeper + +import ( + "context" + "fmt" + + "cosmossdk.io/math" + "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// Before a validator is jailed, we must delete it from the power index. else: +// - ERR CONSENSUS FAILURE!!! err="should never retrieve a jailed validator from the power store" + +var _ stakingtypes.StakingHooks = Hooks{} + +// Hooks wrapper struct for staking keeper +type Hooks struct { + k Keeper +} + +// Hooks return the mesh-security hooks +func (k Keeper) Hooks() Hooks { + return Hooks{k} +} + +// BeforeValidatorModified implements types.StakingHooks. +func (h Hooks) BeforeValidatorModified(ctx context.Context, valAddr types.ValAddress) error { + fmt.Println("BeforeValidatorModified", valAddr.String()) + return h.k.BeforeJailedValidators.Set(ctx, valAddr.String()) +} + +// BeforeValidatorSlashed implements types.StakingHooks. +func (h Hooks) BeforeValidatorSlashed(ctx context.Context, valAddr types.ValAddress, fraction math.LegacyDec) error { + fmt.Println("BeforeValidatorSlashed", valAddr.String(), fraction.String()) + return nil +} + +// ---------------------------- + +// AfterDelegationModified implements types.StakingHooks. +func (h Hooks) AfterDelegationModified(ctx context.Context, delAddr types.AccAddress, valAddr types.ValAddress) error { + return nil +} + +// AfterUnbondingInitiated implements types.StakingHooks. +func (h Hooks) AfterUnbondingInitiated(ctx context.Context, id uint64) error { + return nil +} + +// AfterValidatorBeginUnbonding implements types.StakingHooks. +func (h Hooks) AfterValidatorBeginUnbonding(ctx context.Context, consAddr types.ConsAddress, valAddr types.ValAddress) error { + return nil +} + +// AfterValidatorBonded implements types.StakingHooks. +func (h Hooks) AfterValidatorBonded(ctx context.Context, consAddr types.ConsAddress, valAddr types.ValAddress) error { + return nil +} + +// AfterValidatorCreated implements types.StakingHooks. +func (h Hooks) AfterValidatorCreated(ctx context.Context, valAddr types.ValAddress) error { + return nil +} + +// AfterValidatorRemoved implements types.StakingHooks. +func (h Hooks) AfterValidatorRemoved(ctx context.Context, consAddr types.ConsAddress, valAddr types.ValAddress) error { + return nil +} + +// BeforeDelegationCreated implements types.StakingHooks. +func (h Hooks) BeforeDelegationCreated(ctx context.Context, delAddr types.AccAddress, valAddr types.ValAddress) error { + return nil +} + +// BeforeDelegationRemoved implements types.StakingHooks. +func (h Hooks) BeforeDelegationRemoved(ctx context.Context, delAddr types.AccAddress, valAddr types.ValAddress) error { + return nil +} + +// BeforeDelegationSharesModified implements types.StakingHooks. +func (h Hooks) BeforeDelegationSharesModified(ctx context.Context, delAddr types.AccAddress, valAddr types.ValAddress) error { + return nil +} diff --git a/keys.go b/keys.go index c4f39ae..dcd8a1c 100644 --- a/keys.go +++ b/keys.go @@ -20,6 +20,8 @@ var ( // UpdatedValidatorsCacheKey tracks recently updated validators from SetPower. UpdatedValidatorsCacheKey = collections.NewPrefix(4) + + BeforeJailedValidatorsKey = collections.NewPrefix(5) ) const ( diff --git a/module/abci.go b/module/abci.go index 435327d..3bb9173 100644 --- a/module/abci.go +++ b/module/abci.go @@ -10,19 +10,35 @@ import ( "github.com/strangelove-ventures/poa" ) +func (am AppModule) EndBlocker(ctx context.Context) error { + sk := am.keeper.GetStakingKeeper() + + // Front running x/staking maturity ? + if err := sk.UnbondAllMatureValidators(ctx); err != nil { + return err + } + + if err := am.handleBeforeJailedValidators(ctx); err != nil { + return err + } + + return nil +} + // BeginBlocker updates the validator set without applying updates. // Since this module depends on staking, that module will `ApplyAndReturnValidatorSetUpdates` from x/staking. func (am AppModule) BeginBlocker(ctx context.Context) error { sdkCtx := sdk.UnwrapSDKContext(ctx) defer telemetry.ModuleMeasureSince(poa.ModuleName, sdkCtx.BlockTime(), telemetry.MetricKeyBeginBlocker) - // iterate through any UpdatedValidatorsCache iterator, err := am.keeper.UpdatedValidatorsCache.Iterate(ctx, nil) if err != nil { return err } defer iterator.Close() + sk := am.keeper.GetStakingKeeper() + for ; iterator.Valid(); iterator.Next() { valOperAddr, err := iterator.Key() if err != nil { @@ -30,8 +46,6 @@ func (am AppModule) BeginBlocker(ctx context.Context) error { } fmt.Printf("UpdatedValidatorsCache: %s\n", valOperAddr) - sk := am.keeper.GetStakingKeeper() - valAddr, err := sk.ValidatorAddressCodec().StringToBytes(valOperAddr) if err != nil { return err @@ -42,11 +56,6 @@ func (am AppModule) BeginBlocker(ctx context.Context) error { return err } - // TODO: needed? - // if err := k.stakingKeeper.DeleteLastValidatorPower(ctx, valAddr); err != nil { - // return stakingtypes.Validator{}, err - // } - // Remove it from persisting across many blocks if err := sk.DeleteValidatorByPowerIndex(ctx, val); err != nil { return err @@ -57,6 +66,7 @@ func (am AppModule) BeginBlocker(ctx context.Context) error { } } + // reset caches if sdkCtx.BlockHeight() > 1 { // non gentx messages reset the cached block powers for IBC validations. if err := am.keeper.ResetCachedTotalPower(ctx); err != nil { @@ -85,3 +95,56 @@ func (am AppModule) BeginBlocker(ctx context.Context) error { return nil } + +func (am AppModule) handleBeforeJailedValidators(ctx context.Context) error { + sk := am.keeper.GetStakingKeeper() + + iterator, err := am.keeper.BeforeJailedValidators.Iterate(ctx, nil) + if err != nil { + return err + } + defer iterator.Close() + + // Why? we don't want it in the store w/ the val state change in x/staking + for ; iterator.Valid(); iterator.Next() { + valOperAddr, err := iterator.Key() + if err != nil { + return err + } + fmt.Printf("EndBlocker BeforeJailedValidators: %s\n", valOperAddr) + + valAddr, err := sk.ValidatorAddressCodec().StringToBytes(valOperAddr) + if err != nil { + return err + } + + val, err := sk.GetValidator(ctx, valAddr) + if err != nil { + return err + } + + if err := sk.DeleteValidatorByPowerIndex(ctx, val); err != nil { + return err + } + + // TODO: If this is used here, it persist ABCI Updates. When removes, it looks like the validator gets slashed every block in x/staking? (when we do the hack and force set jailed = false) + // if err := sk.DeleteLastValidatorPower(ctx, valAddr); err != nil { + // return err + // } + + // !IMPORTANT HACK: Set validator from jailed to not jailed to see what happens + // Okay so this like kind of worked for a split second + // Issue: the validator keeps trying to be converted to a jailed validator every single block when x/staking is calling it + val.Jailed = false + if err := sk.SetValidator(ctx, val); err != nil { + return err + } + + // remove it from persisting + if err := am.keeper.BeforeJailedValidators.Remove(ctx, valOperAddr); err != nil { + return err + } + } + + return nil +} diff --git a/module/module.go b/module/module.go index 496b667..d5cd1c2 100644 --- a/module/module.go +++ b/module/module.go @@ -27,6 +27,7 @@ var ( _ module.AppModule = AppModule{} _ module.AppModuleGenesis = AppModule{} _ appmodule.HasBeginBlocker = AppModule{} + _ appmodule.HasEndBlocker = AppModule{} ) // ConsensusVersion defines the current module consensus version. @@ -132,3 +133,7 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw func (am AppModule) BeginBlock(ctx context.Context) error { return am.BeginBlocker(ctx) } + +func (am AppModule) EndBlock(ctx context.Context) error { + return am.EndBlocker(ctx) +} diff --git a/simapp/app.go b/simapp/app.go index f1a78d2..eba7433 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -326,7 +326,11 @@ func NewSimApp( // register the staking hooks // NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks app.StakingKeeper.SetHooks( - stakingtypes.NewMultiStakingHooks(app.DistrKeeper.Hooks(), app.SlashingKeeper.Hooks()), + stakingtypes.NewMultiStakingHooks( + app.DistrKeeper.Hooks(), + app.SlashingKeeper.Hooks(), + app.POAKeeper.Hooks(), // TODO: Document / add to spawn + ), ) app.CircuitKeeper = circuitkeeper.NewKeeper(appCodec, runtime.NewKVStoreService(keys[circuittypes.StoreKey]), govModAddress, app.AccountKeeper.AddressCodec()) @@ -450,16 +454,16 @@ func NewSimApp( distrtypes.ModuleName, slashingtypes.ModuleName, evidencetypes.ModuleName, + poa.ModuleName, // TODO: has to be first stakingtypes.ModuleName, - poa.ModuleName, genutiltypes.ModuleName, authz.ModuleName, ) app.ModuleManager.SetOrderEndBlockers( crisistypes.ModuleName, govtypes.ModuleName, + poa.ModuleName, // TODO: pretty sure this has to go first so we can remove jailed before BlockValidatorUpdates stakingtypes.ModuleName, - poa.ModuleName, genutiltypes.ModuleName, feegrant.ModuleName, group.ModuleName,