From de68c60f9ce96f8dadae91f2a1315cf58b3a2d0c Mon Sep 17 00:00:00 2001 From: sontrinh16 Date: Mon, 12 Jun 2023 21:52:44 +0700 Subject: [PATCH 1/3] copy paste slashing module from LSM --- docs/core/proto-docs.md | 37 + go.mod | 2 + go.sum | 2 + proto/cosmos/staking/v1beta1/staking.proto | 18 + simapp/export.go | 2 +- simapp/test_helpers.go | 44 +- third_party/proto/cosmos_proto/cosmos.proto | 70 + x/auth/legacy/v043/store_test.go | 2 +- x/distribution/keeper/hooks.go | 50 +- x/distribution/keeper/keeper.go | 47 + x/distribution/simulation/operations_test.go | 2 +- x/distribution/types/events.go | 13 +- x/distribution/types/expected_keepers.go | 3 + x/evidence/keeper/infraction_test.go | 2 +- x/genutil/legacy/v040/migrate.go | 19 +- x/gov/keeper/tally_test.go | 18 +- x/slashing/README.md | 7 + x/slashing/abci_test.go | 8 +- x/slashing/app_test.go | 12 +- x/slashing/client/rest/query.go | 111 - x/slashing/client/rest/rest.go | 15 - x/slashing/client/rest/tx.go | 63 - .../grpc_query_test.go => testutil/grpc.go} | 34 +- x/slashing/client/testutil/suite.go | 4 +- x/slashing/handler.go | 26 - x/slashing/handler_test.go | 295 -- x/slashing/{ => keeper}/genesis.go | 16 +- x/slashing/{ => keeper}/genesis_test.go | 15 +- x/slashing/keeper/grpc_query_test.go | 4 +- x/slashing/keeper/hooks.go | 83 +- x/slashing/keeper/infractions.go | 21 +- x/slashing/keeper/keeper.go | 8 +- x/slashing/keeper/keeper_test.go | 134 +- x/slashing/keeper/migrations.go | 5 +- x/slashing/keeper/querier.go | 15 +- x/slashing/keeper/querier_test.go | 6 +- x/slashing/keeper/signing_info_test.go | 14 +- x/slashing/keeper/unjail.go | 21 +- x/slashing/legacy/v039/types.go | 78 - x/slashing/legacy/v040/keys.go | 69 - x/slashing/legacy/v040/migrate.go | 64 - x/slashing/legacy/v040/migrate_test.go | 140 - x/slashing/legacy/v043/store.go | 20 - x/slashing/legacy/v043/store_test.go | 68 - x/slashing/module.go | 43 +- x/slashing/simulation/decoder_test.go | 4 +- x/slashing/simulation/operations.go | 14 +- x/slashing/simulation/operations_test.go | 72 +- x/slashing/simulation/params.go | 2 +- x/slashing/spec/02_state.md | 6 +- x/slashing/spec/03_messages.md | 2 +- x/slashing/spec/04_begin_block.md | 2 +- x/slashing/spec/05_hooks.md | 8 +- x/slashing/spec/06_events.md | 15 +- x/slashing/spec/07_tombstone.md | 4 +- x/slashing/spec/09_client.md | 68 +- x/slashing/spec/README.md | 34 +- x/slashing/types/events.go | 1 + x/slashing/types/expected_keepers.go | 22 +- x/slashing/types/genesis.go | 8 +- x/slashing/types/msg.go | 13 +- x/staking/app_test.go | 2 +- x/staking/client/testutil/suite.go | 4 +- x/staking/genesis.go | 2 +- x/staking/genesis_test.go | 4 +- x/staking/handler_test.go | 32 +- x/staking/keeper/alias_functions.go | 4 +- x/staking/keeper/delegation.go | 16 +- x/staking/keeper/delegation_test.go | 70 +- x/staking/keeper/grpc_query.go | 4 +- x/staking/keeper/grpc_query_test.go | 2 +- x/staking/keeper/hooks.go | 57 +- x/staking/keeper/invariants.go | 2 +- x/staking/keeper/msg_server.go | 6 +- x/staking/keeper/querier.go | 7 +- x/staking/keeper/querier_test.go | 2 +- x/staking/keeper/query_utils.go | 4 +- x/staking/keeper/slash.go | 2 +- x/staking/keeper/slash_test.go | 22 +- x/staking/keeper/test_common.go | 2 +- x/staking/keeper/tokenize_share_record.go | 151 ++ x/staking/keeper/unbonding_test.go | 18 +- x/staking/keeper/validator.go | 10 +- x/staking/keeper/validator_bench_test.go | 2 +- x/staking/keeper/validator_test.go | 72 +- x/staking/legacy/v040/genesis.pb.go | 2 +- x/staking/simulation/decoder_test.go | 2 +- x/staking/simulation/genesis.go | 2 +- x/staking/simulation/operations.go | 2 +- x/staking/simulation/operations_test.go | 4 +- x/staking/teststaking/helper.go | 2 +- x/staking/types/delegation.go | 7 +- x/staking/types/delegation_test.go | 8 +- x/staking/types/errors.go | 96 +- x/staking/types/expected_keepers.go | 25 +- x/staking/types/hooks.go | 83 +- x/staking/types/hooks_template.go | 34 +- x/staking/types/keys.go | 42 + x/staking/types/staking.pb.go | 2402 ++++++++++------- x/staking/types/tokenize_share_record.go | 18 + 100 files changed, 2683 insertions(+), 2548 deletions(-) create mode 100644 x/slashing/README.md delete mode 100644 x/slashing/client/rest/query.go delete mode 100644 x/slashing/client/rest/rest.go delete mode 100644 x/slashing/client/rest/tx.go rename x/slashing/client/{rest/grpc_query_test.go => testutil/grpc.go} (77%) delete mode 100644 x/slashing/handler.go delete mode 100644 x/slashing/handler_test.go rename x/slashing/{ => keeper}/genesis.go (79%) rename x/slashing/{ => keeper}/genesis_test.go (84%) delete mode 100644 x/slashing/legacy/v039/types.go delete mode 100644 x/slashing/legacy/v040/keys.go delete mode 100644 x/slashing/legacy/v040/migrate.go delete mode 100644 x/slashing/legacy/v040/migrate_test.go delete mode 100644 x/slashing/legacy/v043/store.go delete mode 100644 x/slashing/legacy/v043/store_test.go create mode 100644 x/staking/keeper/tokenize_share_record.go create mode 100644 x/staking/types/tokenize_share_record.go diff --git a/docs/core/proto-docs.md b/docs/core/proto-docs.md index 535020d37b38..62342b6066a5 100644 --- a/docs/core/proto-docs.md +++ b/docs/core/proto-docs.md @@ -473,11 +473,13 @@ - [Description](#cosmos.staking.v1beta1.Description) - [HistoricalInfo](#cosmos.staking.v1beta1.HistoricalInfo) - [Params](#cosmos.staking.v1beta1.Params) + - [PendingTokenizeShareAuthorizations](#cosmos.staking.v1beta1.PendingTokenizeShareAuthorizations) - [Pool](#cosmos.staking.v1beta1.Pool) - [Redelegation](#cosmos.staking.v1beta1.Redelegation) - [RedelegationEntry](#cosmos.staking.v1beta1.RedelegationEntry) - [RedelegationEntryResponse](#cosmos.staking.v1beta1.RedelegationEntryResponse) - [RedelegationResponse](#cosmos.staking.v1beta1.RedelegationResponse) + - [TokenizeShareRecord](#cosmos.staking.v1beta1.TokenizeShareRecord) - [UnbondingDelegation](#cosmos.staking.v1beta1.UnbondingDelegation) - [UnbondingDelegationEntry](#cosmos.staking.v1beta1.UnbondingDelegationEntry) - [ValAddresses](#cosmos.staking.v1beta1.ValAddresses) @@ -6656,6 +6658,7 @@ validator. | `delegator_address` | [string](#string) | | delegator_address is the bech32-encoded address of the delegator. | | `validator_address` | [string](#string) | | validator_address is the bech32-encoded address of the validator. | | `shares` | [string](#string) | | shares define the delegation shares received. | +| `validator_bond` | [bool](#bool) | | has this delegation been marked as a validator self bond. | @@ -6736,6 +6739,22 @@ Params defines the parameters for the staking module. + + +### PendingTokenizeShareAuthorizations +PendingTokenizeShareAuthorizations stores a list of addresses that have their +tokenize share enablement in progress + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `addresses` | [string](#string) | repeated | | + + + + + + ### Pool @@ -6830,6 +6849,24 @@ responses. + + +### TokenizeShareRecord +TokenizeShareRecord represents a tokenized delegation + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `id` | [uint64](#uint64) | | | +| `owner` | [string](#string) | | | +| `module_account` | [string](#string) | | module account take the role of delegator | +| `validator` | [string](#string) | | validator delegated to for tokenize share record creation | + + + + + + ### UnbondingDelegation diff --git a/go.mod b/go.mod index bb1d15af872f..68f46ab7885e 100644 --- a/go.mod +++ b/go.mod @@ -52,6 +52,8 @@ require ( gopkg.in/yaml.v2 v2.4.0 ) +require cosmossdk.io/errors v1.0.0-beta.7 + require ( cosmossdk.io/api v0.2.6 // indirect cosmossdk.io/depinject v1.0.0-alpha.3 // indirect diff --git a/go.sum b/go.sum index e5f2f8ed52ad..5dc156bc3126 100644 --- a/go.sum +++ b/go.sum @@ -44,6 +44,8 @@ cosmossdk.io/core v0.5.1 h1:vQVtFrIYOQJDV3f7rw4pjjVqc1id4+mE0L9hHP66pyI= cosmossdk.io/core v0.5.1/go.mod h1:KZtwHCLjcFuo0nmDc24Xy6CRNEL9Vl/MeimQ2aC7NLE= cosmossdk.io/depinject v1.0.0-alpha.3 h1:6evFIgj//Y3w09bqOUOzEpFj5tsxBqdc5CfkO7z+zfw= cosmossdk.io/depinject v1.0.0-alpha.3/go.mod h1:eRbcdQ7MRpIPEM5YUJh8k97nxHpYbc3sMUnEtt8HPWU= +cosmossdk.io/errors v1.0.0-beta.7 h1:gypHW76pTQGVnHKo6QBkb4yFOJjC+sUGRc5Al3Odj1w= +cosmossdk.io/errors v1.0.0-beta.7/go.mod h1:mz6FQMJRku4bY7aqS/Gwfcmr/ue91roMEKAmDUDpBfE= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU= filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= diff --git a/proto/cosmos/staking/v1beta1/staking.proto b/proto/cosmos/staking/v1beta1/staking.proto index 27581a12cf9a..31ae7b0db3d0 100644 --- a/proto/cosmos/staking/v1beta1/staking.proto +++ b/proto/cosmos/staking/v1beta1/staking.proto @@ -198,6 +198,8 @@ message Delegation { string validator_address = 2 [(gogoproto.moretags) = "yaml:\"validator_address\""]; // shares define the delegation shares received. string shares = 3 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false]; + // has this delegation been marked as a validator self bond. + bool validator_bond = 4; } // UnbondingDelegation stores all of a single delegator's unbonding bonds @@ -367,4 +369,20 @@ enum InfractionType { // ValidatorUpdates defines an array of abci.ValidatorUpdate objects. message ValidatorUpdates { repeated tendermint.abci.ValidatorUpdate updates = 1 [(gogoproto.nullable) = false]; +} + +// TokenizeShareRecord represents a tokenized delegation +message TokenizeShareRecord { + option (gogoproto.equal) = true; + + uint64 id = 1; + string owner = 2; + string module_account = 3; // module account take the role of delegator + string validator = 4; // validator delegated to for tokenize share record creation +} + +// PendingTokenizeShareAuthorizations stores a list of addresses that have their +// tokenize share enablement in progress +message PendingTokenizeShareAuthorizations { + repeated string addresses = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; } \ No newline at end of file diff --git a/simapp/export.go b/simapp/export.go index e6a0525fba40..9f952cf146c9 100644 --- a/simapp/export.go +++ b/simapp/export.go @@ -154,7 +154,7 @@ func (app *SimApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs [] for ; iter.Valid(); iter.Next() { addr := sdk.ValAddress(stakingtypes.AddressFromValidatorsKey(iter.Key())) - validator, found := app.StakingKeeper.GetValidator(ctx, addr) + validator, found := app.StakingKeeper.GetLiquidValidator(ctx, addr) if !found { panic("expected validator, not found") } diff --git a/simapp/test_helpers.go b/simapp/test_helpers.go index c428fdb2e2bc..cb672ca31068 100644 --- a/simapp/test_helpers.go +++ b/simapp/test_helpers.go @@ -22,6 +22,7 @@ import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" "github.com/cosmos/cosmos-sdk/simapp/helpers" sdk "github.com/cosmos/cosmos-sdk/types" @@ -85,6 +86,31 @@ func Setup(isCheckTx bool) *SimApp { return app } +// For LSM replaced module useage +func SetupLSM(t *testing.T, _ bool) *SimApp { + t.Helper() + + pk := ed25519.GenPrivKey().PubKey() + pubKey, err := cryptocodec.ToTmPubKeyInterface(pk) + require.NoError(t, err) + + // create validator set with single validator + validator := tmtypes.NewValidator(pubKey, 1) + valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) + + // generate genesis account + senderPrivKey := secp256k1.GenPrivKey() + acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), 0, 0) + balance := banktypes.Balance{ + Address: acc.GetAddress().String(), + Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100000000000000))), + } + + app := SetupWithGenesisValSet(t, valSet, []authtypes.GenesisAccount{acc}, balance) + + return app +} + // SetupWithGenesisValSet initializes a new SimApp with a validator set and genesis accounts // that also act as delegators. For simplicity, each validator is bonded with a delegation // of one consensus engine unit (10^6) in the default token of the simapp from first genesis @@ -119,7 +145,7 @@ func SetupWithGenesisValSet(t *testing.T, valSet *tmtypes.ValidatorSet, genAccs MinSelfDelegation: sdk.ZeroInt(), } validators = append(validators, validator) - delegations = append(delegations, stakingtypes.NewDelegation(genAccs[0].GetAddress(), val.Address.Bytes(), sdk.OneDec())) + delegations = append(delegations, stakingtypes.NewDelegation(genAccs[0].GetAddress(), val.Address.Bytes(), sdk.OneDec(), false)) } // set validators and delegations @@ -200,6 +226,22 @@ func SetupWithGenesisAccounts(genAccs []authtypes.GenesisAccount, balances ...ba return app } +// SetupWithGenesisAccounts initializes a new SimApp with the provided genesis +// accounts and possible balances. +// For LSM replaced module useage +func SetupWithGenesisAccountsLSM(t *testing.T, genAccs []authtypes.GenesisAccount, balances ...banktypes.Balance) *SimApp { + t.Helper() + pk := ed25519.GenPrivKey().PubKey() + pubKey, err := cryptocodec.ToTmPubKeyInterface(pk) + require.NoError(t, err) + + // create validator set with single validator + validator := tmtypes.NewValidator(pubKey, 1) + valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) + + return SetupWithGenesisValSet(t, valSet, genAccs, balances...) +} + type GenerateAccountStrategy func(int) []sdk.AccAddress // createRandomAccounts is a strategy used by addTestAddrs() in order to generated addresses in random order. diff --git a/third_party/proto/cosmos_proto/cosmos.proto b/third_party/proto/cosmos_proto/cosmos.proto index 167b170757bc..8429ed8dd57a 100644 --- a/third_party/proto/cosmos_proto/cosmos.proto +++ b/third_party/proto/cosmos_proto/cosmos.proto @@ -13,4 +13,74 @@ extend google.protobuf.MessageOptions { extend google.protobuf.FieldOptions { string accepts_interface = 93001; + + // scalar is used to indicate that this field follows the formatting defined + // by the named scalar which should be declared with declare_scalar. Code + // generators may choose to use this information to map this field to a + // language-specific type representing the scalar. + string scalar = 93002; + + // declare_interface declares an interface type to be used with + // accepts_interface and implements_interface. Interface names are + // expected to follow the following convention such that their declaration + // can be discovered by tools: for a given interface type a.b.C, it is + // expected that the declaration will be found in a protobuf file named + // a/b/interfaces.proto in the file descriptor set. + repeated InterfaceDescriptor declare_interface = 793021; + + // declare_scalar declares a scalar type to be used with + // the scalar field option. Scalar names are + // expected to follow the following convention such that their declaration + // can be discovered by tools: for a given scalar type a.b.C, it is + // expected that the declaration will be found in a protobuf file named + // a/b/scalars.proto in the file descriptor set. + repeated ScalarDescriptor declare_scalar = 793022; +} + +// InterfaceDescriptor describes an interface type to be used with +// accepts_interface and implements_interface and declared by declare_interface. +message InterfaceDescriptor { + + // name is the name of the interface. It should be a short-name (without + // a period) such that the fully qualified name of the interface will be + // package.name, ex. for the package a.b and interface named C, the + // fully-qualified name will be a.b.C. + string name = 1; + + // description is a human-readable description of the interface and its + // purpose. + string description = 2; +} + +// ScalarDescriptor describes an scalar type to be used with +// the scalar field option and declared by declare_scalar. +// Scalars extend simple protobuf built-in types with additional +// syntax and semantics, for instance to represent big integers. +// Scalars should ideally define an encoding such that there is only one +// valid syntactical representation for a given semantic meaning, +// i.e. the encoding should be deterministic. +message ScalarDescriptor { + + // name is the name of the scalar. It should be a short-name (without + // a period) such that the fully qualified name of the scalar will be + // package.name, ex. for the package a.b and scalar named C, the + // fully-qualified name will be a.b.C. + string name = 1; + + // description is a human-readable description of the scalar and its + // encoding format. For instance a big integer or decimal scalar should + // specify precisely the expected encoding format. + string description = 2; + + // field_type is the type of field with which this scalar can be used. + // Scalars can be used with one and only one type of field so that + // encoding standards and simple and clear. Currently only string and + // bytes fields are supported for scalars. + repeated ScalarType field_type = 3; +} + +enum ScalarType { + SCALAR_TYPE_UNSPECIFIED = 0; + SCALAR_TYPE_STRING = 1; + SCALAR_TYPE_BYTES = 2; } diff --git a/x/auth/legacy/v043/store_test.go b/x/auth/legacy/v043/store_test.go index fb593c14ba7c..84122c55f699 100644 --- a/x/auth/legacy/v043/store_test.go +++ b/x/auth/legacy/v043/store_test.go @@ -548,7 +548,7 @@ func TestMigrateVestingAccounts(t *testing.T) { delegatorAddr := addrs[0] _, valAddr := createValidator(t, ctx, app, tc.tokenAmount*2) - validator, found := app.StakingKeeper.GetValidator(ctx, valAddr) + validator, found := app.StakingKeeper.GetLiquidValidator(ctx, valAddr) require.True(t, found) tc.prepareFunc(app, ctx, validator, delegatorAddr) diff --git a/x/distribution/keeper/hooks.go b/x/distribution/keeper/hooks.go index 4ca75691f511..d05a4a15d0cf 100644 --- a/x/distribution/keeper/hooks.go +++ b/x/distribution/keeper/hooks.go @@ -17,13 +17,14 @@ var _ stakingtypes.StakingHooks = Hooks{} func (k Keeper) Hooks() Hooks { return Hooks{k} } // initialize validator distribution record -func (h Hooks) AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) { +func (h Hooks) AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) error { val := h.k.stakingKeeper.Validator(ctx, valAddr) h.k.initializeValidator(ctx, val) + return nil } // AfterValidatorRemoved performs clean up after a validator is removed -func (h Hooks) AfterValidatorRemoved(ctx sdk.Context, _ sdk.ConsAddress, valAddr sdk.ValAddress) { +func (h Hooks) AfterValidatorRemoved(ctx sdk.Context, _ sdk.ConsAddress, valAddr sdk.ValAddress) error { // fetch outstanding outstanding := h.k.GetValidatorOutstandingRewardsCoins(ctx, valAddr) @@ -47,7 +48,7 @@ func (h Hooks) AfterValidatorRemoved(ctx sdk.Context, _ sdk.ConsAddress, valAddr withdrawAddr := h.k.GetDelegatorWithdrawAddr(ctx, accAddr) if err := h.k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, withdrawAddr, coins); err != nil { - panic(err) + return err } } } @@ -73,38 +74,61 @@ func (h Hooks) AfterValidatorRemoved(ctx sdk.Context, _ sdk.ConsAddress, valAddr // clear current rewards h.k.DeleteValidatorCurrentRewards(ctx, valAddr) + + return nil +} + +func (h Hooks) BeforeTokenizeShareRecordRemoved(ctx sdk.Context, recordID uint64) error { + err := h.k.WithdrawSingleShareRecordReward(ctx, recordID) + if err != nil { + h.k.Logger(ctx).Error(err.Error()) + } + return err } // increment period -func (h Hooks) BeforeDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { +func (h Hooks) BeforeDelegationCreated(ctx sdk.Context, _ sdk.AccAddress, valAddr sdk.ValAddress) error { val := h.k.stakingKeeper.Validator(ctx, valAddr) - h.k.IncrementValidatorPeriod(ctx, val) + _ = h.k.IncrementValidatorPeriod(ctx, val) + return nil } // withdraw delegation rewards (which also increments period) -func (h Hooks) BeforeDelegationSharesModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { +func (h Hooks) BeforeDelegationSharesModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) error { val := h.k.stakingKeeper.Validator(ctx, valAddr) del := h.k.stakingKeeper.Delegation(ctx, delAddr, valAddr) if _, err := h.k.withdrawDelegationRewards(ctx, val, del); err != nil { - panic(err) + return err } + + return nil } // create new delegation period record -func (h Hooks) AfterDelegationModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { +func (h Hooks) AfterDelegationModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) error { h.k.initializeDelegation(ctx, valAddr, delAddr) + return nil } // record the slash event -func (h Hooks) BeforeValidatorSlashed(ctx sdk.Context, valAddr sdk.ValAddress, fraction sdk.Dec) { +func (h Hooks) BeforeValidatorSlashed(ctx sdk.Context, valAddr sdk.ValAddress, fraction sdk.Dec) error { h.k.updateValidatorSlashFraction(ctx, valAddr, fraction) + return nil } -func (h Hooks) BeforeValidatorModified(_ sdk.Context, _ sdk.ValAddress) {} -func (h Hooks) AfterValidatorBonded(_ sdk.Context, _ sdk.ConsAddress, _ sdk.ValAddress) {} -func (h Hooks) AfterValidatorBeginUnbonding(_ sdk.Context, _ sdk.ConsAddress, _ sdk.ValAddress) {} -func (h Hooks) BeforeDelegationRemoved(_ sdk.Context, _ sdk.AccAddress, _ sdk.ValAddress) {} +func (h Hooks) BeforeValidatorModified(_ sdk.Context, _ sdk.ValAddress) error { return nil } +func (h Hooks) AfterValidatorBonded(_ sdk.Context, _ sdk.ConsAddress, _ sdk.ValAddress) error { + return nil +} + +func (h Hooks) AfterValidatorBeginUnbonding(_ sdk.Context, _ sdk.ConsAddress, _ sdk.ValAddress) error { + return nil +} + +func (h Hooks) BeforeDelegationRemoved(_ sdk.Context, _ sdk.AccAddress, _ sdk.ValAddress) error { + return nil +} func (h Hooks) AfterUnbondingInitiated(_ sdk.Context, _ uint64) error { return nil } diff --git a/x/distribution/keeper/keeper.go b/x/distribution/keeper/keeper.go index 22e1a2894402..647d3950ef12 100644 --- a/x/distribution/keeper/keeper.go +++ b/x/distribution/keeper/keeper.go @@ -80,6 +80,53 @@ func (k Keeper) SetWithdrawAddr(ctx sdk.Context, delegatorAddr sdk.AccAddress, w return nil } +func (k Keeper) WithdrawSingleShareRecordReward(ctx sdk.Context, recordID uint64) error { + record, err := k.stakingKeeper.GetTokenizeShareRecord(ctx, recordID) + if err != nil { + return err + } + + owner, err := sdk.AccAddressFromBech32(record.Owner) + if err != nil { + return err + } + + valAddr, err := sdk.ValAddressFromBech32(record.Validator) + if err != nil { + return err + } + + val := k.stakingKeeper.Validator(ctx, valAddr) + del := k.stakingKeeper.Delegation(ctx, record.GetModuleAddress(), valAddr) + if val != nil && del != nil { + // withdraw rewards into reward module account and send it to reward owner + cacheCtx, write := ctx.CacheContext() + _, err = k.WithdrawDelegationRewards(cacheCtx, record.GetModuleAddress(), valAddr) + if err != nil { + return err + } + write() + } + + // apply changes when the module account has positive balance + balances := k.bankKeeper.GetAllBalances(ctx, record.GetModuleAddress()) + if !balances.Empty() { + err = k.bankKeeper.SendCoins(ctx, record.GetModuleAddress(), owner, balances) + if err != nil { + return err + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeWithdrawTokenizeShareReward, + sdk.NewAttribute(types.AttributeKeyWithdrawAddress, owner.String()), + sdk.NewAttribute(sdk.AttributeKeyAmount, balances.String()), + ), + ) + } + return nil +} + // withdraw rewards from a delegation func (k Keeper) WithdrawDelegationRewards(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) (sdk.Coins, error) { val := k.stakingKeeper.Validator(ctx, valAddr) diff --git a/x/distribution/simulation/operations_test.go b/x/distribution/simulation/operations_test.go index 0e5dfdd4613f..d198e4f42248 100644 --- a/x/distribution/simulation/operations_test.go +++ b/x/distribution/simulation/operations_test.go @@ -97,7 +97,7 @@ func (suite *SimTestSuite) TestSimulateMsgWithdrawDelegatorReward() { delTokens := suite.app.StakingKeeper.TokensFromConsensusPower(suite.ctx, 2) validator0, issuedShares := validator0.AddTokensFromDel(delTokens) delegator := accounts[1] - delegation := stakingtypes.NewDelegation(delegator.Address, validator0.GetOperator(), issuedShares) + delegation := stakingtypes.NewDelegation(delegator.Address, validator0.GetOperator(), issuedShares, false) suite.app.StakingKeeper.SetDelegation(suite.ctx, delegation) suite.app.DistrKeeper.SetDelegatorStartingInfo(suite.ctx, validator0.GetOperator(), delegator.Address, distrtypes.NewDelegatorStartingInfo(2, sdk.OneDec(), 200)) diff --git a/x/distribution/types/events.go b/x/distribution/types/events.go index ce4c0ef62e3c..a45d931c9b33 100644 --- a/x/distribution/types/events.go +++ b/x/distribution/types/events.go @@ -2,12 +2,13 @@ package types // distribution module event types const ( - EventTypeSetWithdrawAddress = "set_withdraw_address" - EventTypeRewards = "rewards" - EventTypeCommission = "commission" - EventTypeWithdrawRewards = "withdraw_rewards" - EventTypeWithdrawCommission = "withdraw_commission" - EventTypeProposerReward = "proposer_reward" + EventTypeSetWithdrawAddress = "set_withdraw_address" + EventTypeRewards = "rewards" + EventTypeCommission = "commission" + EventTypeWithdrawRewards = "withdraw_rewards" + EventTypeWithdrawCommission = "withdraw_commission" + EventTypeWithdrawTokenizeShareReward = "withdraw_tokenize_share_reward" + EventTypeProposerReward = "proposer_reward" AttributeKeyWithdrawAddress = "withdraw_address" AttributeKeyValidator = "validator" diff --git a/x/distribution/types/expected_keepers.go b/x/distribution/types/expected_keepers.go index 9b869c972735..45ad26d9232d 100644 --- a/x/distribution/types/expected_keepers.go +++ b/x/distribution/types/expected_keepers.go @@ -27,6 +27,7 @@ type BankKeeper interface { SendCoinsFromModuleToModule(ctx sdk.Context, senderModule string, recipientModule string, amt sdk.Coins) error SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error + SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error } // StakingKeeper expected staking keeper (noalias) @@ -65,6 +66,8 @@ type StakingKeeper interface { GetLastValidatorPower(ctx sdk.Context, valAddr sdk.ValAddress) int64 GetAllSDKDelegations(ctx sdk.Context) []stakingtypes.Delegation + GetTokenizeShareRecordsByOwner(ctx sdk.Context, owner sdk.AccAddress) (tokenizeShareRecords []stakingtypes.TokenizeShareRecord) + GetTokenizeShareRecord(ctx sdk.Context, id uint64) (tokenizeShareRecord stakingtypes.TokenizeShareRecord, err error) } // StakingHooks event hooks for staking validator object (noalias) diff --git a/x/evidence/keeper/infraction_test.go b/x/evidence/keeper/infraction_test.go index 4c7368b1df7c..0e811c54ec90 100644 --- a/x/evidence/keeper/infraction_test.go +++ b/x/evidence/keeper/infraction_test.go @@ -64,7 +64,7 @@ func (suite *KeeperTestSuite) TestHandleDoubleSign() { // require we be able to unbond now ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) del, _ := suite.app.StakingKeeper.GetDelegation(ctx, sdk.AccAddress(operatorAddr), operatorAddr) - validator, _ := suite.app.StakingKeeper.GetValidator(ctx, operatorAddr) + validator, _ := suite.app.StakingKeeper.GetLiquidValidator(ctx, operatorAddr) totalBond := validator.TokensFromShares(del.GetShares()).TruncateInt() tstaking.Ctx = ctx tstaking.Denom = stakingParams.BondDenom diff --git a/x/genutil/legacy/v040/migrate.go b/x/genutil/legacy/v040/migrate.go index a022d9fcf985..5ec1dbcd2f9c 100644 --- a/x/genutil/legacy/v040/migrate.go +++ b/x/genutil/legacy/v040/migrate.go @@ -22,8 +22,9 @@ import ( v039mint "github.com/cosmos/cosmos-sdk/x/mint/legacy/v039" v040mint "github.com/cosmos/cosmos-sdk/x/mint/legacy/v040" v036params "github.com/cosmos/cosmos-sdk/x/params/legacy/v036" - v039slashing "github.com/cosmos/cosmos-sdk/x/slashing/legacy/v039" - v040slashing "github.com/cosmos/cosmos-sdk/x/slashing/legacy/v040" + + // v039slashing "github.com/cosmos/cosmos-sdk/x/slashing/legacy/v039" + // v040slashing "github.com/cosmos/cosmos-sdk/x/slashing/legacy/v040" v038staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v038" v040staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v040" v038upgrade "github.com/cosmos/cosmos-sdk/x/upgrade/legacy/v038" @@ -154,20 +155,6 @@ func Migrate(appState types.AppMap, clientCtx client.Context) types.AppMap { appState[v040mint.ModuleName] = v040Codec.MustMarshalJSON(v040mint.Migrate(mintGenState)) } - // Migrate x/slashing. - if appState[v039slashing.ModuleName] != nil { - // unmarshal relative source genesis application state - var slashingGenState v039slashing.GenesisState - v039Codec.MustUnmarshalJSON(appState[v039slashing.ModuleName], &slashingGenState) - - // delete deprecated x/slashing genesis state - delete(appState, v039slashing.ModuleName) - - // Migrate relative source genesis application state and marshal it into - // the respective key. - appState[v040slashing.ModuleName] = v040Codec.MustMarshalJSON(v040slashing.Migrate(slashingGenState)) - } - // Migrate x/staking. if appState[v038staking.ModuleName] != nil { // unmarshal relative source genesis application state diff --git a/x/gov/keeper/tally_test.go b/x/gov/keeper/tally_test.go index 7347cfbe670b..7d97d4d26ad8 100644 --- a/x/gov/keeper/tally_test.go +++ b/x/gov/keeper/tally_test.go @@ -246,7 +246,7 @@ func TestTallyDelgatorOverride(t *testing.T) { addrs, valAddrs := createValidators(t, ctx, app, []int64{5, 6, 7}) delTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 30) - val1, found := app.StakingKeeper.GetValidator(ctx, valAddrs[0]) + val1, found := app.StakingKeeper.GetLiquidValidator(ctx, valAddrs[0]) require.True(t, found) _, err := app.StakingKeeper.Delegate(ctx, addrs[4], delTokens, stakingtypes.Unbonded, val1, true) @@ -282,7 +282,7 @@ func TestTallyDelgatorInherit(t *testing.T) { addrs, vals := createValidators(t, ctx, app, []int64{5, 6, 7}) delTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 30) - val3, found := app.StakingKeeper.GetValidator(ctx, vals[2]) + val3, found := app.StakingKeeper.GetLiquidValidator(ctx, vals[2]) require.True(t, found) _, err := app.StakingKeeper.Delegate(ctx, addrs[3], delTokens, stakingtypes.Unbonded, val3, true) @@ -317,9 +317,9 @@ func TestTallyDelgatorMultipleOverride(t *testing.T) { addrs, vals := createValidators(t, ctx, app, []int64{5, 6, 7}) delTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 10) - val1, found := app.StakingKeeper.GetValidator(ctx, vals[0]) + val1, found := app.StakingKeeper.GetLiquidValidator(ctx, vals[0]) require.True(t, found) - val2, found := app.StakingKeeper.GetValidator(ctx, vals[1]) + val2, found := app.StakingKeeper.GetLiquidValidator(ctx, vals[1]) require.True(t, found) _, err := app.StakingKeeper.Delegate(ctx, addrs[3], delTokens, stakingtypes.Unbonded, val1, true) @@ -359,9 +359,9 @@ func TestTallyDelgatorMultipleInherit(t *testing.T) { addrs, vals := createValidators(t, ctx, app, []int64{5, 6, 7}) delTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 10) - val2, found := app.StakingKeeper.GetValidator(ctx, vals[1]) + val2, found := app.StakingKeeper.GetLiquidValidator(ctx, vals[1]) require.True(t, found) - val3, found := app.StakingKeeper.GetValidator(ctx, vals[2]) + val3, found := app.StakingKeeper.GetLiquidValidator(ctx, vals[2]) require.True(t, found) _, err := app.StakingKeeper.Delegate(ctx, addrs[3], delTokens, stakingtypes.Unbonded, val2, true) @@ -398,9 +398,9 @@ func TestTallyJailedValidator(t *testing.T) { addrs, valAddrs := createValidators(t, ctx, app, []int64{25, 6, 7}) delTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 10) - val2, found := app.StakingKeeper.GetValidator(ctx, valAddrs[1]) + val2, found := app.StakingKeeper.GetLiquidValidator(ctx, valAddrs[1]) require.True(t, found) - val3, found := app.StakingKeeper.GetValidator(ctx, valAddrs[2]) + val3, found := app.StakingKeeper.GetLiquidValidator(ctx, valAddrs[2]) require.True(t, found) _, err := app.StakingKeeper.Delegate(ctx, addrs[3], delTokens, stakingtypes.Unbonded, val2, true) @@ -441,7 +441,7 @@ func TestTallyValidatorMultipleDelegations(t *testing.T) { addrs, valAddrs := createValidators(t, ctx, app, []int64{10, 10, 10}) delTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 10) - val2, found := app.StakingKeeper.GetValidator(ctx, valAddrs[1]) + val2, found := app.StakingKeeper.GetLiquidValidator(ctx, valAddrs[1]) require.True(t, found) _, err := app.StakingKeeper.Delegate(ctx, addrs[0], delTokens, stakingtypes.Unbonded, val2, true) diff --git a/x/slashing/README.md b/x/slashing/README.md new file mode 100644 index 000000000000..9f79636b9892 --- /dev/null +++ b/x/slashing/README.md @@ -0,0 +1,7 @@ + + +# Slashing + +* [Slashing](spec/README.md) - validator punishment mechanisms. diff --git a/x/slashing/abci_test.go b/x/slashing/abci_test.go index 6121e3572b88..41fb1c8e112a 100644 --- a/x/slashing/abci_test.go +++ b/x/slashing/abci_test.go @@ -8,16 +8,16 @@ import ( abci "github.com/tendermint/tendermint/abci/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - "github.com/cosmos/cosmos-sdk/simapp" + simapp "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/slashing" "github.com/cosmos/cosmos-sdk/x/staking" "github.com/cosmos/cosmos-sdk/x/staking/teststaking" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + sdkstaking "github.com/cosmos/cosmos-sdk/x/staking/types" ) func TestBeginBlocker(t *testing.T) { - app := simapp.Setup(false) + app := simapp.SetupLSM(t, false) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) pks := simapp.CreateTestPubKeys(1) @@ -97,5 +97,5 @@ func TestBeginBlocker(t *testing.T) { // validator should be jailed validator, found := app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(pk)) require.True(t, found) - require.Equal(t, stakingtypes.Unbonding, validator.GetStatus()) + require.Equal(t, sdkstaking.Unbonding, validator.GetStatus()) } diff --git a/x/slashing/app_test.go b/x/slashing/app_test.go index 028d41755607..9abad1653dd9 100644 --- a/x/slashing/app_test.go +++ b/x/slashing/app_test.go @@ -10,11 +10,13 @@ import ( "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" - "github.com/cosmos/cosmos-sdk/simapp" + simapp "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/cosmos-sdk/x/slashing/types" + sdkslashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + sdkstaking "github.com/cosmos/cosmos-sdk/x/staking/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) @@ -28,7 +30,7 @@ var ( func checkValidator(t *testing.T, app *simapp.SimApp, _ sdk.AccAddress, expFound bool) stakingtypes.Validator { ctxCheck := app.BaseApp.NewContext(true, tmproto.Header{}) - validator, found := app.StakingKeeper.GetValidator(ctxCheck, sdk.ValAddress(addr1)) + validator, found := app.StakingKeeper.GetLiquidValidator(ctxCheck, sdk.ValAddress(addr1)) require.Equal(t, expFound, found) return validator } @@ -57,7 +59,7 @@ func TestSlashingMsgs(t *testing.T) { }, } - app := simapp.SetupWithGenesisAccounts(accs, balances...) + app := simapp.SetupWithGenesisAccountsLSM(t, accs, balances...) simapp.CheckBalance(t, app, addr1, sdk.Coins{genCoin}) description := stakingtypes.NewDescription("foo_moniker", "", "", "", "") @@ -79,7 +81,7 @@ func TestSlashingMsgs(t *testing.T) { validator := checkValidator(t, app, addr1, true) require.Equal(t, sdk.ValAddress(addr1).String(), validator.OperatorAddress) - require.Equal(t, stakingtypes.Bonded, validator.Status) + require.Equal(t, sdkstaking.Bonded, validator.Status) require.True(sdk.IntEq(t, bondTokens, validator.BondedTokens())) unjailMsg := &types.MsgUnjail{ValidatorAddr: sdk.ValAddress(addr1).String()} @@ -90,5 +92,5 @@ func TestSlashingMsgs(t *testing.T) { _, res, err := simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, []sdk.Msg{unjailMsg}, "", []uint64{0}, []uint64{1}, false, false, priv1) require.Error(t, err) require.Nil(t, res) - require.True(t, errors.Is(types.ErrValidatorNotJailed, err)) + require.True(t, errors.Is(sdkslashingtypes.ErrValidatorNotJailed, err)) } diff --git a/x/slashing/client/rest/query.go b/x/slashing/client/rest/query.go deleted file mode 100644 index d5d4a3a187f1..000000000000 --- a/x/slashing/client/rest/query.go +++ /dev/null @@ -1,111 +0,0 @@ -package rest - -import ( - "fmt" - "net/http" - - "github.com/gorilla/mux" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/types/bech32/legacybech32" //nolint:staticcheck - "github.com/cosmos/cosmos-sdk/types/rest" - "github.com/cosmos/cosmos-sdk/x/slashing/types" -) - -func registerQueryRoutes(clientCtx client.Context, r *mux.Router) { - r.HandleFunc( - "/slashing/validators/{validatorPubKey}/signing_info", - signingInfoHandlerFn(clientCtx), - ).Methods("GET") - - r.HandleFunc( - "/slashing/signing_infos", - signingInfoHandlerListFn(clientCtx), - ).Methods("GET") - - r.HandleFunc( - "/slashing/parameters", - queryParamsHandlerFn(clientCtx), - ).Methods("GET") -} - -// Deprecated: http request handler to query signing info -func signingInfoHandlerFn(clientCtx client.Context) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - pk, err := legacybech32.UnmarshalPubKey(legacybech32.ConsPK, vars["validatorPubKey"]) - if rest.CheckBadRequestError(w, err) { - return - } - - clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) - if !ok { - return - } - - params := types.QuerySigningInfoRequest{ConsAddress: pk.Address().String()} - - bz, err := clientCtx.LegacyAmino.MarshalJSON(params) - if rest.CheckBadRequestError(w, err) { - return - } - - route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QuerySigningInfo) - res, height, err := clientCtx.QueryWithData(route, bz) - if rest.CheckInternalServerError(w, err) { - return - } - - clientCtx = clientCtx.WithHeight(height) - rest.PostProcessResponse(w, clientCtx, res) - } -} - -// http request handler to query signing info -func signingInfoHandlerListFn(clientCtx client.Context) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - _, page, limit, err := rest.ParseHTTPArgsWithLimit(r, 0) - if rest.CheckBadRequestError(w, err) { - return - } - - clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) - if !ok { - return - } - - params := types.NewQuerySigningInfosParams(page, limit) - bz, err := clientCtx.LegacyAmino.MarshalJSON(params) - if rest.CheckInternalServerError(w, err) { - return - } - - route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QuerySigningInfos) - res, height, err := clientCtx.QueryWithData(route, bz) - if rest.CheckInternalServerError(w, err) { - return - } - - clientCtx = clientCtx.WithHeight(height) - rest.PostProcessResponse(w, clientCtx, res) - } -} - -func queryParamsHandlerFn(clientCtx client.Context) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) - if !ok { - return - } - - route := fmt.Sprintf("custom/%s/parameters", types.QuerierRoute) - - res, height, err := clientCtx.QueryWithData(route, nil) - if rest.CheckInternalServerError(w, err) { - return - } - - clientCtx = clientCtx.WithHeight(height) - rest.PostProcessResponse(w, clientCtx, res) - } -} diff --git a/x/slashing/client/rest/rest.go b/x/slashing/client/rest/rest.go deleted file mode 100644 index 059cb17c6bdc..000000000000 --- a/x/slashing/client/rest/rest.go +++ /dev/null @@ -1,15 +0,0 @@ -package rest - -import ( - "github.com/gorilla/mux" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/rest" -) - -func RegisterHandlers(clientCtx client.Context, rtr *mux.Router) { - r := rest.WithHTTPDeprecationHeaders(rtr) - - registerQueryRoutes(clientCtx, r) - registerTxHandlers(clientCtx, r) -} diff --git a/x/slashing/client/rest/tx.go b/x/slashing/client/rest/tx.go deleted file mode 100644 index eaac614fa1c4..000000000000 --- a/x/slashing/client/rest/tx.go +++ /dev/null @@ -1,63 +0,0 @@ -package rest - -import ( - "bytes" - "net/http" - - "github.com/gorilla/mux" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/tx" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/rest" - "github.com/cosmos/cosmos-sdk/x/slashing/types" -) - -func registerTxHandlers(clientCtx client.Context, r *mux.Router) { - r.HandleFunc("/slashing/validators/{validatorAddr}/unjail", NewUnjailRequestHandlerFn(clientCtx)).Methods("POST") -} - -// Unjail TX body -type UnjailReq struct { - BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` -} - -// NewUnjailRequestHandlerFn returns an HTTP REST handler for creating a MsgUnjail -// transaction. -func NewUnjailRequestHandlerFn(clientCtx client.Context) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - bech32Validator := vars["validatorAddr"] - - var req UnjailReq - if !rest.ReadRESTReq(w, r, clientCtx.LegacyAmino, &req) { - return - } - - req.BaseReq = req.BaseReq.Sanitize() - if !req.BaseReq.ValidateBasic(w) { - return - } - - fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From) - if rest.CheckBadRequestError(w, err) { - return - } - - valAddr, err := sdk.ValAddressFromBech32(bech32Validator) - if rest.CheckInternalServerError(w, err) { - return - } - - if !bytes.Equal(fromAddr, valAddr) { - rest.WriteErrorResponse(w, http.StatusUnauthorized, "must use own validator address") - return - } - - msg := types.NewMsgUnjail(valAddr) - if rest.CheckBadRequestError(w, msg.ValidateBasic()) { - return - } - tx.WriteGeneratedTxResponse(clientCtx, w, req.BaseReq, msg) - } -} diff --git a/x/slashing/client/rest/grpc_query_test.go b/x/slashing/client/testutil/grpc.go similarity index 77% rename from x/slashing/client/rest/grpc_query_test.go rename to x/slashing/client/testutil/grpc.go index 39daf06f69c5..fc41baf953aa 100644 --- a/x/slashing/client/rest/grpc_query_test.go +++ b/x/slashing/client/testutil/grpc.go @@ -1,46 +1,18 @@ -package rest_test +package testutil import ( "fmt" - "testing" "time" "github.com/gogo/protobuf/proto" - "github.com/stretchr/testify/suite" "github.com/cosmos/cosmos-sdk/testutil" - "github.com/cosmos/cosmos-sdk/testutil/network" sdk "github.com/cosmos/cosmos-sdk/types" grpctypes "github.com/cosmos/cosmos-sdk/types/grpc" "github.com/cosmos/cosmos-sdk/types/query" "github.com/cosmos/cosmos-sdk/x/slashing/types" ) -type IntegrationTestSuite struct { - suite.Suite - - cfg network.Config - network *network.Network -} - -func (s *IntegrationTestSuite) SetupSuite() { - s.T().Log("setting up integration test suite") - - cfg := network.DefaultConfig() - cfg.NumValidators = 1 - - s.cfg = cfg - s.network = network.New(s.T(), cfg) - - _, err := s.network.WaitForHeight(1) - s.Require().NoError(err) -} - -func (s *IntegrationTestSuite) TearDownSuite() { - s.T().Log("tearing down integration test suite") - s.network.Cleanup() -} - func (s *IntegrationTestSuite) TestGRPCQueries() { val := s.network.Validators[0] baseURL := val.APIAddress @@ -128,7 +100,3 @@ func (s *IntegrationTestSuite) TestGRPCQueries() { }) } } - -func TestIntegrationTestSuite(t *testing.T) { - suite.Run(t, new(IntegrationTestSuite)) -} diff --git a/x/slashing/client/testutil/suite.go b/x/slashing/client/testutil/suite.go index cf5cf997b6c3..50b4b151b377 100644 --- a/x/slashing/client/testutil/suite.go +++ b/x/slashing/client/testutil/suite.go @@ -31,9 +31,11 @@ func NewIntegrationTestSuite(cfg network.Config) *IntegrationTestSuite { func (s *IntegrationTestSuite) SetupSuite() { s.T().Log("setting up integration test suite") + var err error s.network = network.New(s.T(), s.cfg) + s.Require().NoError(err) - _, err := s.network.WaitForHeight(1) + _, err = s.network.WaitForHeight(1) s.Require().NoError(err) } diff --git a/x/slashing/handler.go b/x/slashing/handler.go deleted file mode 100644 index eab71edb4188..000000000000 --- a/x/slashing/handler.go +++ /dev/null @@ -1,26 +0,0 @@ -package slashing - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/slashing/keeper" - "github.com/cosmos/cosmos-sdk/x/slashing/types" -) - -// NewHandler creates an sdk.Handler for all the slashing type messages -func NewHandler(k keeper.Keeper) sdk.Handler { - return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { - ctx = ctx.WithEventManager(sdk.NewEventManager()) - - msgServer := keeper.NewMsgServerImpl(k) - - switch msg := msg.(type) { - case *types.MsgUnjail: - res, err := msgServer.Unjail(sdk.WrapSDKContext(ctx), msg) - return sdk.WrapServiceResult(ctx, res, err) - - default: - return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", types.ModuleName, msg) - } - } -} diff --git a/x/slashing/handler_test.go b/x/slashing/handler_test.go deleted file mode 100644 index 25fed487ba79..000000000000 --- a/x/slashing/handler_test.go +++ /dev/null @@ -1,295 +0,0 @@ -package slashing_test - -import ( - "errors" - "strings" - "testing" - "time" - - "github.com/stretchr/testify/require" - tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - - "github.com/cosmos/cosmos-sdk/simapp" - "github.com/cosmos/cosmos-sdk/testutil/testdata" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/slashing" - "github.com/cosmos/cosmos-sdk/x/slashing/keeper" - "github.com/cosmos/cosmos-sdk/x/slashing/testslashing" - "github.com/cosmos/cosmos-sdk/x/slashing/types" - "github.com/cosmos/cosmos-sdk/x/staking" - "github.com/cosmos/cosmos-sdk/x/staking/teststaking" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" -) - -func TestCannotUnjailUnlessJailed(t *testing.T) { - // initial setup - app := simapp.Setup(false) - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - pks := simapp.CreateTestPubKeys(1) - simapp.AddTestAddrsFromPubKeys(app, ctx, pks, app.StakingKeeper.TokensFromConsensusPower(ctx, 200)) - - tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - slh := slashing.NewHandler(app.SlashingKeeper) - addr, val := sdk.ValAddress(pks[0].Address()), pks[0] - - amt := tstaking.CreateValidatorWithValPower(addr, val, 100, true) - staking.EndBlocker(ctx, app.StakingKeeper) - require.Equal( - t, app.BankKeeper.GetAllBalances(ctx, sdk.AccAddress(addr)), - sdk.Coins{sdk.NewCoin(app.StakingKeeper.GetParams(ctx).BondDenom, InitTokens.Sub(amt))}, - ) - require.Equal(t, amt, app.StakingKeeper.Validator(ctx, addr).GetBondedTokens()) - - // assert non-jailed validator can't be unjailed - res, err := slh(ctx, types.NewMsgUnjail(addr)) - require.Error(t, err) - require.Nil(t, res) - require.True(t, errors.Is(types.ErrValidatorNotJailed, err)) -} - -func TestCannotUnjailUnlessMeetMinSelfDelegation(t *testing.T) { - // initial setup - app := simapp.Setup(false) - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - pks := simapp.CreateTestPubKeys(1) - simapp.AddTestAddrsFromPubKeys(app, ctx, pks, app.StakingKeeper.TokensFromConsensusPower(ctx, 200)) - - tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - slh := slashing.NewHandler(app.SlashingKeeper) - addr, val := sdk.ValAddress(pks[0].Address()), pks[0] - amt := app.StakingKeeper.TokensFromConsensusPower(ctx, 100) - msg := tstaking.CreateValidatorMsg(addr, val, amt) - msg.MinSelfDelegation = amt - tstaking.Handle(msg, true) - - staking.EndBlocker(ctx, app.StakingKeeper) - require.Equal( - t, app.BankKeeper.GetAllBalances(ctx, sdk.AccAddress(addr)), - sdk.Coins{sdk.NewCoin(app.StakingKeeper.GetParams(ctx).BondDenom, InitTokens.Sub(amt))}, - ) - - tstaking.Undelegate(sdk.AccAddress(addr), addr, sdk.OneInt(), true) - require.True(t, app.StakingKeeper.Validator(ctx, addr).IsJailed()) - - // assert non-jailed validator can't be unjailed - res, err := slh(ctx, types.NewMsgUnjail(addr)) - require.Error(t, err) - require.Nil(t, res) - require.True(t, errors.Is(types.ErrSelfDelegationTooLowToUnjail, err)) -} - -func TestJailedValidatorDelegations(t *testing.T) { - // initial setup - app := simapp.Setup(false) - ctx := app.BaseApp.NewContext(false, tmproto.Header{Time: time.Unix(0, 0)}) - pks := simapp.CreateTestPubKeys(3) - - simapp.AddTestAddrsFromPubKeys(app, ctx, pks, app.StakingKeeper.TokensFromConsensusPower(ctx, 20)) - app.SlashingKeeper.SetParams(ctx, testslashing.TestParams()) - - tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - stakingParams := app.StakingKeeper.GetParams(ctx) - app.StakingKeeper.SetParams(ctx, stakingParams) - valAddr, consAddr := sdk.ValAddress(pks[1].Address()), sdk.ConsAddress(pks[0].Address()) - - amt := tstaking.CreateValidatorWithValPower(valAddr, pks[1], 10, true) - staking.EndBlocker(ctx, app.StakingKeeper) - - // set dummy signing info - newInfo := types.NewValidatorSigningInfo(consAddr, 0, 0, time.Unix(0, 0), false, 0) - app.SlashingKeeper.SetValidatorSigningInfo(ctx, consAddr, newInfo) - - // delegate tokens to the validator - delAddr := sdk.AccAddress(pks[2].Address()) - tstaking.Delegate(delAddr, valAddr, amt) - - // unbond validator total self-delegations (which should jail the validator) - valAcc := sdk.AccAddress(valAddr) - tstaking.Undelegate(valAcc, valAddr, amt, true) - _, err := app.StakingKeeper.CompleteUnbonding(ctx, sdk.AccAddress(valAddr), valAddr) - require.Nil(t, err, "expected complete unbonding validator to be ok, got: %v", err) - - // verify validator still exists and is jailed - validator, found := app.StakingKeeper.GetValidator(ctx, valAddr) - require.True(t, found) - require.True(t, validator.IsJailed()) - - // verify the validator cannot unjail itself - res, err := slashing.NewHandler(app.SlashingKeeper)(ctx, types.NewMsgUnjail(valAddr)) - require.Error(t, err) - require.Nil(t, res) - - // self-delegate to validator - tstaking.Delegate(valAcc, valAddr, amt) - - // verify the validator can now unjail itself - res, err = slashing.NewHandler(app.SlashingKeeper)(ctx, types.NewMsgUnjail(valAddr)) - require.NoError(t, err) - require.NotNil(t, res) -} - -func TestInvalidMsg(t *testing.T) { - k := keeper.Keeper{} - h := slashing.NewHandler(k) - - res, err := h(sdk.NewContext(nil, tmproto.Header{}, false, nil), testdata.NewTestMsg()) - require.Error(t, err) - require.Nil(t, res) - require.True(t, strings.Contains(err.Error(), "unrecognized slashing message type")) -} - -// Test a validator through uptime, downtime, revocation, -// unrevocation, starting height reset, and revocation again -func TestHandleAbsentValidator(t *testing.T) { - // initial setup - app := simapp.Setup(false) - ctx := app.BaseApp.NewContext(false, tmproto.Header{Time: time.Unix(0, 0)}) - pks := simapp.CreateTestPubKeys(1) - simapp.AddTestAddrsFromPubKeys(app, ctx, pks, app.StakingKeeper.TokensFromConsensusPower(ctx, 200)) - app.SlashingKeeper.SetParams(ctx, testslashing.TestParams()) - - power := int64(100) - addr, val := sdk.ValAddress(pks[0].Address()), pks[0] - slh := slashing.NewHandler(app.SlashingKeeper) - tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - - amt := tstaking.CreateValidatorWithValPower(addr, val, power, true) - staking.EndBlocker(ctx, app.StakingKeeper) - - require.Equal( - t, app.BankKeeper.GetAllBalances(ctx, sdk.AccAddress(addr)), - sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.GetParams(ctx).BondDenom, InitTokens.Sub(amt))), - ) - require.Equal(t, amt, app.StakingKeeper.Validator(ctx, addr).GetBondedTokens()) - - // will exist since the validator has been bonded - info, found := app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) - require.True(t, found) - require.Equal(t, int64(0), info.StartHeight) - require.Equal(t, int64(0), info.IndexOffset) - require.Equal(t, int64(0), info.MissedBlocksCounter) - require.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil) - height := int64(0) - - // 1000 first blocks OK - for ; height < app.SlashingKeeper.SignedBlocksWindow(ctx); height++ { - ctx = ctx.WithBlockHeight(height) - app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, true) - } - info, found = app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) - require.True(t, found) - require.Equal(t, int64(0), info.StartHeight) - require.Equal(t, int64(0), info.MissedBlocksCounter) - - // 500 blocks missed - for ; height < app.SlashingKeeper.SignedBlocksWindow(ctx)+(app.SlashingKeeper.SignedBlocksWindow(ctx)-app.SlashingKeeper.MinSignedPerWindow(ctx)); height++ { - ctx = ctx.WithBlockHeight(height) - app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, false) - } - info, found = app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) - require.True(t, found) - require.Equal(t, int64(0), info.StartHeight) - require.Equal(t, app.SlashingKeeper.SignedBlocksWindow(ctx)-app.SlashingKeeper.MinSignedPerWindow(ctx), info.MissedBlocksCounter) - - // validator should be bonded still - validator, _ := app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, stakingtypes.Bonded, validator.GetStatus()) - - bondPool := app.StakingKeeper.GetBondedPool(ctx) - require.True(sdk.IntEq(t, amt, app.BankKeeper.GetBalance(ctx, bondPool.GetAddress(), app.StakingKeeper.BondDenom(ctx)).Amount)) - - // 501st block missed - ctx = ctx.WithBlockHeight(height) - app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, false) - info, found = app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) - require.True(t, found) - require.Equal(t, int64(0), info.StartHeight) - // counter now reset to zero - require.Equal(t, int64(0), info.MissedBlocksCounter) - - // end block - staking.EndBlocker(ctx, app.StakingKeeper) - - // validator should have been jailed - validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, stakingtypes.Unbonding, validator.GetStatus()) - - slashAmt := amt.ToDec().Mul(app.SlashingKeeper.SlashFractionDowntime(ctx)).RoundInt() - - // validator should have been slashed - require.True(t, amt.Sub(slashAmt).Equal(validator.GetTokens())) - - height++ - ctx = ctx.WithBlockHeight(height) - app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, false) - info, found = app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) - require.True(t, found) - require.Equal(t, int64(0), info.StartHeight) - // validator missed block should not be incremented since it is jailed (#11425) - require.Equal(t, int64(0), info.MissedBlocksCounter) - - // end block - staking.EndBlocker(ctx, app.StakingKeeper) - - // validator should not have been slashed any more, since it was already jailed - validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.True(t, amt.Sub(slashAmt).Equal(validator.GetTokens())) - - // unrevocation should fail prior to jail expiration - res, err := slh(ctx, types.NewMsgUnjail(addr)) - require.Error(t, err) - require.Nil(t, res) - - // unrevocation should succeed after jail expiration - ctx = ctx.WithBlockHeader(tmproto.Header{Time: time.Unix(1, 0).Add(app.SlashingKeeper.DowntimeJailDuration(ctx))}) - res, err = slh(ctx, types.NewMsgUnjail(addr)) - require.NoError(t, err) - require.NotNil(t, res) - - // end block - staking.EndBlocker(ctx, app.StakingKeeper) - - // validator should be rebonded now - validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, stakingtypes.Bonded, validator.GetStatus()) - - // validator should have been slashed - require.True(t, amt.Sub(slashAmt).Equal(app.BankKeeper.GetBalance(ctx, bondPool.GetAddress(), app.StakingKeeper.BondDenom(ctx)).Amount)) - - // Validator start height should not have been changed - info, found = app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) - require.True(t, found) - require.Equal(t, int64(0), info.StartHeight) - // validator missed block should not have been changed - require.Equal(t, int64(0), info.MissedBlocksCounter) - - // validator should not be immediately jailed again - height++ - ctx = ctx.WithBlockHeight(height) - app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, false) - validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, stakingtypes.Bonded, validator.GetStatus()) - - // 500 signed blocks - nextHeight := height + app.SlashingKeeper.MinSignedPerWindow(ctx) + 1 - for ; height < nextHeight; height++ { - ctx = ctx.WithBlockHeight(height) - app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, false) - } - - // end block - staking.EndBlocker(ctx, app.StakingKeeper) - - // validator should be jailed again after 500 unsigned blocks - nextHeight = height + app.SlashingKeeper.MinSignedPerWindow(ctx) + 1 - for ; height <= nextHeight; height++ { - ctx = ctx.WithBlockHeight(height) - app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, false) - } - - // end block - staking.EndBlocker(ctx, app.StakingKeeper) - - validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, stakingtypes.Unbonding, validator.GetStatus()) -} diff --git a/x/slashing/genesis.go b/x/slashing/keeper/genesis.go similarity index 79% rename from x/slashing/genesis.go rename to x/slashing/keeper/genesis.go index 2c8b6675762b..0c13fc816e1e 100644 --- a/x/slashing/genesis.go +++ b/x/slashing/keeper/genesis.go @@ -1,22 +1,24 @@ -package slashing +package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/slashing/keeper" "github.com/cosmos/cosmos-sdk/x/slashing/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + sdkstaking "github.com/cosmos/cosmos-sdk/x/staking/types" ) // InitGenesis initialize default parameters // and the keeper's address to pubkey map -func InitGenesis(ctx sdk.Context, keeper keeper.Keeper, stakingKeeper types.StakingKeeper, data *types.GenesisState) { +func (keeper Keeper) InitGenesis(ctx sdk.Context, stakingKeeper types.StakingKeeper, data *types.GenesisState) { stakingKeeper.IterateValidators(ctx, - func(index int64, validator stakingtypes.ValidatorI) bool { + func(index int64, validator sdkstaking.ValidatorI) bool { consPk, err := validator.ConsPubKey() if err != nil { panic(err) } - keeper.AddPubkey(ctx, consPk) + err = keeper.AddPubkey(ctx, consPk) + if err != nil { + panic(err) + } return false }, ) @@ -45,7 +47,7 @@ func InitGenesis(ctx sdk.Context, keeper keeper.Keeper, stakingKeeper types.Stak // ExportGenesis writes the current store values // to a genesis file, which can be imported again // with InitGenesis -func ExportGenesis(ctx sdk.Context, keeper keeper.Keeper) (data *types.GenesisState) { +func (keeper Keeper) ExportGenesis(ctx sdk.Context) (data *types.GenesisState) { params := keeper.GetParams(ctx) signingInfos := make([]types.SigningInfo, 0) missedBlocks := make([]types.ValidatorMissedBlocks, 0) diff --git a/x/slashing/genesis_test.go b/x/slashing/keeper/genesis_test.go similarity index 84% rename from x/slashing/genesis_test.go rename to x/slashing/keeper/genesis_test.go index 1467fac6efd1..88fbf60ed664 100644 --- a/x/slashing/genesis_test.go +++ b/x/slashing/keeper/genesis_test.go @@ -1,4 +1,4 @@ -package slashing_test +package keeper_test import ( "testing" @@ -7,15 +7,14 @@ import ( "github.com/stretchr/testify/require" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - "github.com/cosmos/cosmos-sdk/simapp" + simapp "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/slashing" "github.com/cosmos/cosmos-sdk/x/slashing/testslashing" "github.com/cosmos/cosmos-sdk/x/slashing/types" ) func TestExportAndInitGenesis(t *testing.T) { - app := simapp.Setup(false) + app := simapp.SetupLSM(t, false) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) app.SlashingKeeper.SetParams(ctx, testslashing.TestParams()) @@ -29,7 +28,7 @@ func TestExportAndInitGenesis(t *testing.T) { app.SlashingKeeper.SetValidatorSigningInfo(ctx, sdk.ConsAddress(addrDels[0]), info1) app.SlashingKeeper.SetValidatorSigningInfo(ctx, sdk.ConsAddress(addrDels[1]), info2) - genesisState := slashing.ExportGenesis(ctx, app.SlashingKeeper) + genesisState := app.SlashingKeeper.ExportGenesis(ctx) require.Equal(t, genesisState.Params, testslashing.TestParams()) require.Len(t, genesisState.SigningInfos, 2) @@ -42,16 +41,18 @@ func TestExportAndInitGenesis(t *testing.T) { ok := app.SlashingKeeper.IsTombstoned(ctx, sdk.ConsAddress(addrDels[0])) require.True(t, ok) - newInfo1, ok := app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(addrDels[0])) + newInfo1, _ := app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(addrDels[0])) require.NotEqual(t, info1, newInfo1) // Initialise genesis with genesis state before tombstone - slashing.InitGenesis(ctx, app.SlashingKeeper, app.StakingKeeper, genesisState) + + app.SlashingKeeper.InitGenesis(ctx, app.StakingKeeper, genesisState) // Validator isTombstoned should return false as GenesisState is initialised ok = app.SlashingKeeper.IsTombstoned(ctx, sdk.ConsAddress(addrDels[0])) require.False(t, ok) newInfo1, ok = app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(addrDels[0])) + require.True(t, ok) newInfo2, ok := app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(addrDels[1])) require.True(t, ok) require.Equal(t, info1, newInfo1) diff --git a/x/slashing/keeper/grpc_query_test.go b/x/slashing/keeper/grpc_query_test.go index 38f216697f8c..6be7406fc5aa 100644 --- a/x/slashing/keeper/grpc_query_test.go +++ b/x/slashing/keeper/grpc_query_test.go @@ -9,7 +9,7 @@ import ( tmproto "github.com/tendermint/tendermint/proto/tendermint/types" "github.com/cosmos/cosmos-sdk/baseapp" - "github.com/cosmos/cosmos-sdk/simapp" + simapp "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" @@ -28,7 +28,7 @@ type SlashingTestSuite struct { } func (suite *SlashingTestSuite) SetupTest() { - app := simapp.Setup(false) + app := simapp.SetupLSM(suite.T(), false) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) app.AccountKeeper.SetParams(ctx, authtypes.DefaultParams()) diff --git a/x/slashing/keeper/hooks.go b/x/slashing/keeper/hooks.go index fdcc7814adff..c120086436db 100644 --- a/x/slashing/keeper/hooks.go +++ b/x/slashing/keeper/hooks.go @@ -9,11 +9,13 @@ import ( "github.com/cosmos/cosmos-sdk/x/slashing/types" ) -func (k Keeper) AfterValidatorBonded(ctx sdk.Context, address sdk.ConsAddress, _ sdk.ValAddress) { +func (k Keeper) AfterValidatorBonded(ctx sdk.Context, address sdk.ConsAddress, _ sdk.ValAddress) error { // Update the signing info start height or create a new signing info - _, found := k.GetValidatorSigningInfo(ctx, address) - if !found { - signingInfo := types.NewValidatorSigningInfo( + signingInfo, found := k.GetValidatorSigningInfo(ctx, address) + if found { + signingInfo.StartHeight = ctx.BlockHeight() + } else { + signingInfo = types.NewValidatorSigningInfo( address, ctx.BlockHeight(), 0, @@ -21,8 +23,10 @@ func (k Keeper) AfterValidatorBonded(ctx sdk.Context, address sdk.ConsAddress, _ false, 0, ) - k.SetValidatorSigningInfo(ctx, address, signingInfo) } + + k.SetValidatorSigningInfo(ctx, address, signingInfo) + return nil } // AfterValidatorCreated adds the address-pubkey relation when a validator is created. @@ -32,14 +36,18 @@ func (k Keeper) AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) e if err != nil { return err } - k.AddPubkey(ctx, consPk) - return nil + return k.AddPubkey(ctx, consPk) } // AfterValidatorRemoved deletes the address-pubkey relation when a validator is removed, -func (k Keeper) AfterValidatorRemoved(ctx sdk.Context, address sdk.ConsAddress) { +func (k Keeper) AfterValidatorRemoved(ctx sdk.Context, address sdk.ConsAddress) error { k.deleteAddrPubkeyRelation(ctx, crypto.Address(address)) + return nil +} + +func (k Keeper) AfterUnbondingInitiated(ctx sdk.Context, address sdk.ConsAddress) error { + return nil } // Hooks wrapper struct for slashing keeper @@ -47,7 +55,6 @@ type Hooks struct { k Keeper } -// Implements StakingHooks interface var _ types.StakingHooks = Hooks{} // Return the wrapper struct @@ -56,27 +63,53 @@ func (k Keeper) Hooks() Hooks { } // Implements sdk.ValidatorHooks -func (h Hooks) AfterValidatorBonded(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) { - h.k.AfterValidatorBonded(ctx, consAddr, valAddr) +func (h Hooks) AfterValidatorBonded(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) error { + return h.k.AfterValidatorBonded(ctx, consAddr, valAddr) } // Implements sdk.ValidatorHooks -func (h Hooks) AfterValidatorRemoved(ctx sdk.Context, consAddr sdk.ConsAddress, _ sdk.ValAddress) { - h.k.AfterValidatorRemoved(ctx, consAddr) +func (h Hooks) AfterValidatorRemoved(ctx sdk.Context, consAddr sdk.ConsAddress, _ sdk.ValAddress) error { + return h.k.AfterValidatorRemoved(ctx, consAddr) } // Implements sdk.ValidatorHooks -func (h Hooks) AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) { - h.k.AfterValidatorCreated(ctx, valAddr) -} - -func (h Hooks) AfterValidatorBeginUnbonding(_ sdk.Context, _ sdk.ConsAddress, _ sdk.ValAddress) {} -func (h Hooks) BeforeValidatorModified(_ sdk.Context, _ sdk.ValAddress) {} -func (h Hooks) BeforeDelegationCreated(_ sdk.Context, _ sdk.AccAddress, _ sdk.ValAddress) {} -func (h Hooks) BeforeDelegationSharesModified(_ sdk.Context, _ sdk.AccAddress, _ sdk.ValAddress) {} -func (h Hooks) BeforeDelegationRemoved(_ sdk.Context, _ sdk.AccAddress, _ sdk.ValAddress) {} -func (h Hooks) AfterDelegationModified(_ sdk.Context, _ sdk.AccAddress, _ sdk.ValAddress) {} -func (h Hooks) BeforeValidatorSlashed(_ sdk.Context, _ sdk.ValAddress, _ sdk.Dec) {} -func (h Hooks) AfterUnbondingInitiated(_ sdk.Context, _ uint64) error { +func (h Hooks) AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) error { + return h.k.AfterValidatorCreated(ctx, valAddr) +} + +// Implements sdk.ValidatorHooks - just addition to fulfill the staking hook interface +func (h Hooks) BeforeTokenizeShareRecordRemoved(_ sdk.Context, _ uint64) error { + return nil +} + +func (h Hooks) AfterValidatorBeginUnbonding(_ sdk.Context, _ sdk.ConsAddress, _ sdk.ValAddress) error { + return nil +} + +func (h Hooks) BeforeValidatorModified(_ sdk.Context, _ sdk.ValAddress) error { + return nil +} + +func (h Hooks) BeforeDelegationCreated(_ sdk.Context, _ sdk.AccAddress, _ sdk.ValAddress) error { + return nil +} + +func (h Hooks) BeforeDelegationSharesModified(_ sdk.Context, _ sdk.AccAddress, _ sdk.ValAddress) error { + return nil +} + +func (h Hooks) BeforeDelegationRemoved(_ sdk.Context, _ sdk.AccAddress, _ sdk.ValAddress) error { + return nil +} + +func (h Hooks) AfterDelegationModified(_ sdk.Context, _ sdk.AccAddress, _ sdk.ValAddress) error { + return nil +} + +func (h Hooks) BeforeValidatorSlashed(_ sdk.Context, _ sdk.ValAddress, _ sdk.Dec) error { + return nil +} + +func (h Hooks) AfterUnbondingInitiated(ctx sdk.Context, id uint64) error { return nil } diff --git a/x/slashing/keeper/infractions.go b/x/slashing/keeper/infractions.go index 509448b35507..94500caa8f4c 100644 --- a/x/slashing/keeper/infractions.go +++ b/x/slashing/keeper/infractions.go @@ -6,22 +6,20 @@ import ( cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/slashing/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + sdkstaking "github.com/cosmos/cosmos-sdk/x/staking/types" ) // HandleValidatorSignature handles a validator signature, must be called once per validator per block. func (k Keeper) HandleValidatorSignature(ctx sdk.Context, addr cryptotypes.Address, power int64, signed bool) { + logger := k.Logger(ctx) + height := ctx.BlockHeight() + // fetch the validator public key consAddr := sdk.ConsAddress(addr) - - // don't update missed blocks when validator's jailed - if k.sk.IsValidatorJailed(ctx, consAddr) { - return + if _, err := k.GetPubkey(ctx, addr); err != nil { + panic(fmt.Sprintf("Validator consensus-address %s not found", consAddr)) } - logger := k.Logger(ctx) - height := ctx.BlockHeight() - // fetch signing info signInfo, found := k.GetValidatorSigningInfo(ctx, consAddr) if !found { @@ -38,7 +36,6 @@ func (k Keeper) HandleValidatorSignature(ctx sdk.Context, addr cryptotypes.Addre // That way we avoid needing to read/write the whole array each time previous := k.GetValidatorMissedBlockBitArray(ctx, consAddr, index) missed := !signed - switch { case !previous && missed: // Array value has changed from not missed to missed, increment counter @@ -79,7 +76,7 @@ func (k Keeper) HandleValidatorSignature(ctx sdk.Context, addr cryptotypes.Addre // if we are past the minimum height and the validator has missed too many blocks, punish them if height > minHeight && signInfo.MissedBlocksCounter > maxMissed { validator := k.sk.ValidatorByConsAddr(ctx, consAddr) - if validator != nil { + if validator != nil && !validator.IsJailed() { // Downtime confirmed: slash and jail the validator // We need to retrieve the stake distribution which signed the block, so we subtract ValidatorUpdateDelay from the evidence height, // and subtract an additional 1 since this is the LastCommit. @@ -87,6 +84,7 @@ func (k Keeper) HandleValidatorSignature(ctx sdk.Context, addr cryptotypes.Addre // i.e. at the end of the pre-genesis block (none) = at the beginning of the genesis block. // That's fine since this is just used to filter unbonding delegations & redelegations. distributionHeight := height - sdk.ValidatorUpdateDelay - 1 + ctx.EventManager().EmitEvent( sdk.NewEvent( types.EventTypeSlash, @@ -96,7 +94,8 @@ func (k Keeper) HandleValidatorSignature(ctx sdk.Context, addr cryptotypes.Addre sdk.NewAttribute(types.AttributeKeyJailed, consAddr.String()), ), ) - k.sk.Slash(ctx, consAddr, distributionHeight, power, k.SlashFractionDowntime(ctx), stakingtypes.Downtime) + k.sk.Slash(ctx, consAddr, distributionHeight, power, k.SlashFractionDowntime(ctx), sdkstaking.Downtime) + k.sk.Jail(ctx, consAddr) signInfo.JailedUntil = ctx.BlockHeader().Time.Add(k.DowntimeJailDuration(ctx)) diff --git a/x/slashing/keeper/keeper.go b/x/slashing/keeper/keeper.go index aa7c3746c153..cf8cf8808ff6 100644 --- a/x/slashing/keeper/keeper.go +++ b/x/slashing/keeper/keeper.go @@ -7,6 +7,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/slashing/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" @@ -14,14 +15,14 @@ import ( // Keeper of the slashing store type Keeper struct { - storeKey sdk.StoreKey + storeKey storetypes.StoreKey cdc codec.BinaryCodec sk types.StakingKeeper paramspace types.ParamSubspace } // NewKeeper creates a slashing keeper -func NewKeeper(cdc codec.BinaryCodec, key sdk.StoreKey, sk types.StakingKeeper, paramspace types.ParamSubspace) Keeper { +func NewKeeper(cdc codec.BinaryCodec, key storetypes.StoreKey, sk types.StakingKeeper, paramspace types.ParamSubspace) Keeper { // set KeyTable if it has not already been set if !paramspace.HasKeyTable() { paramspace = paramspace.WithKeyTable(types.ParamKeyTable()) @@ -81,14 +82,13 @@ func (k Keeper) Slash(ctx sdk.Context, consAddr sdk.ConsAddress, fraction sdk.De // Jail attempts to jail a validator. The slash is delegated to the staking module // to make the necessary validator changes. func (k Keeper) Jail(ctx sdk.Context, consAddr sdk.ConsAddress) { + k.sk.Jail(ctx, consAddr) ctx.EventManager().EmitEvent( sdk.NewEvent( types.EventTypeSlash, sdk.NewAttribute(types.AttributeKeyJailed, consAddr.String()), ), ) - - k.sk.Jail(ctx, consAddr) } func (k Keeper) deleteAddrPubkeyRelation(ctx sdk.Context, addr cryptotypes.Address) { diff --git a/x/slashing/keeper/keeper_test.go b/x/slashing/keeper/keeper_test.go index 2e7e727a2697..311d8bd37a48 100644 --- a/x/slashing/keeper/keeper_test.go +++ b/x/slashing/keeper/keeper_test.go @@ -7,80 +7,19 @@ import ( "github.com/stretchr/testify/require" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - "github.com/cosmos/cosmos-sdk/simapp" + simapp "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/slashing/testslashing" "github.com/cosmos/cosmos-sdk/x/staking" "github.com/cosmos/cosmos-sdk/x/staking/teststaking" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + sdkstaking "github.com/cosmos/cosmos-sdk/x/staking/types" ) -func TestUnJailNotBonded(t *testing.T) { - app := simapp.Setup(false) - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - - p := app.StakingKeeper.GetParams(ctx) - p.MaxValidators = 5 - app.StakingKeeper.SetParams(ctx, p) - - addrDels := simapp.AddTestAddrsIncremental(app, ctx, 6, app.StakingKeeper.TokensFromConsensusPower(ctx, 200)) - valAddrs := simapp.ConvertAddrsToValAddrs(addrDels) - pks := simapp.CreateTestPubKeys(6) - tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - - // create max (5) validators all with the same power - for i := uint32(0); i < p.MaxValidators; i++ { - addr, val := valAddrs[i], pks[i] - tstaking.CreateValidatorWithValPower(addr, val, 100, true) - } - - staking.EndBlocker(ctx, app.StakingKeeper) - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - - // create a 6th validator with less power than the cliff validator (won't be bonded) - addr, val := valAddrs[5], pks[5] - amt := app.StakingKeeper.TokensFromConsensusPower(ctx, 50) - msg := tstaking.CreateValidatorMsg(addr, val, amt) - msg.MinSelfDelegation = amt - tstaking.Handle(msg, true) - - staking.EndBlocker(ctx, app.StakingKeeper) - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - - tstaking.CheckValidator(addr, stakingtypes.Unbonded, false) - - // unbond below minimum self-delegation - require.Equal(t, p.BondDenom, tstaking.Denom) - tstaking.Undelegate(sdk.AccAddress(addr), addr, app.StakingKeeper.TokensFromConsensusPower(ctx, 1), true) - - staking.EndBlocker(ctx, app.StakingKeeper) - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - - // verify that validator is jailed - tstaking.CheckValidator(addr, -1, true) - - // verify we cannot unjail (yet) - require.Error(t, app.SlashingKeeper.Unjail(ctx, addr)) - - staking.EndBlocker(ctx, app.StakingKeeper) - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - // bond to meet minimum self-delegation - tstaking.DelegateWithPower(sdk.AccAddress(addr), addr, 1) - - staking.EndBlocker(ctx, app.StakingKeeper) - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - - // verify we can immediately unjail - require.NoError(t, app.SlashingKeeper.Unjail(ctx, addr)) - - tstaking.CheckValidator(addr, -1, false) -} - // Test a new validator entering the validator set // Ensure that SigningInfo.StartHeight is set correctly // and that they are not immediately jailed func TestHandleNewValidator(t *testing.T) { - app := simapp.Setup(false) + app := simapp.SetupLSM(t, false) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) addrDels := simapp.AddTestAddrsIncremental(app, ctx, 1, app.StakingKeeper.TokensFromConsensusPower(ctx, 200)) @@ -114,9 +53,11 @@ func TestHandleNewValidator(t *testing.T) { // validator should be bonded still, should not have been jailed or slashed validator, _ := app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, stakingtypes.Bonded, validator.GetStatus()) + require.Equal(t, sdkstaking.Bonded, validator.GetStatus()) bondPool := app.StakingKeeper.GetBondedPool(ctx) expTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 100) + // adding genesis validator tokens + expTokens = expTokens.Add(app.StakingKeeper.TokensFromConsensusPower(ctx, 1)) require.True(t, expTokens.Equal(app.BankKeeper.GetBalance(ctx, bondPool.GetAddress(), app.StakingKeeper.BondDenom(ctx)).Amount)) } @@ -124,7 +65,7 @@ func TestHandleNewValidator(t *testing.T) { // Ensure that they're only slashed once func TestHandleAlreadyJailed(t *testing.T) { // initial setup - app := simapp.Setup(false) + app := simapp.SetupLSM(t, false) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) addrDels := simapp.AddTestAddrsIncremental(app, ctx, 1, app.StakingKeeper.TokensFromConsensusPower(ctx, 200)) @@ -156,7 +97,7 @@ func TestHandleAlreadyJailed(t *testing.T) { // validator should have been jailed and slashed validator, _ := app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, stakingtypes.Unbonding, validator.GetStatus()) + require.Equal(t, sdkstaking.Unbonding, validator.GetStatus()) // validator should have been slashed resultingTokens := amt.Sub(app.StakingKeeper.TokensFromConsensusPower(ctx, 1)) @@ -177,7 +118,7 @@ func TestHandleAlreadyJailed(t *testing.T) { func TestValidatorDippingInAndOut(t *testing.T) { // initial setup // TestParams set the SignedBlocksWindow to 1000 and MaxMissedBlocksPerWindow to 500 - app := simapp.Setup(false) + app := simapp.SetupLSM(t, false) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) app.SlashingKeeper.SetParams(ctx, testslashing.TestParams()) @@ -195,7 +136,9 @@ func TestValidatorDippingInAndOut(t *testing.T) { valAddr := sdk.ValAddress(addr) tstaking.CreateValidatorWithValPower(valAddr, val, power, true) - staking.EndBlocker(ctx, app.StakingKeeper) + validatorUpdates := staking.EndBlocker(ctx, app.StakingKeeper) + require.Equal(t, 2, len(validatorUpdates)) + tstaking.CheckValidator(valAddr, sdkstaking.Bonded, false) // 100 first blocks OK height := int64(0) @@ -205,52 +148,48 @@ func TestValidatorDippingInAndOut(t *testing.T) { } // kick first validator out of validator set - tstaking.CreateValidatorWithValPower(sdk.ValAddress(pks[1].Address()), pks[1], 101, true) - validatorUpdates := staking.EndBlocker(ctx, app.StakingKeeper) + tstaking.CreateValidatorWithValPower(sdk.ValAddress(pks[1].Address()), pks[1], power+1, true) + validatorUpdates = staking.EndBlocker(ctx, app.StakingKeeper) require.Equal(t, 2, len(validatorUpdates)) - tstaking.CheckValidator(valAddr, stakingtypes.Unbonding, false) + tstaking.CheckValidator(sdk.ValAddress(pks[1].Address()), sdkstaking.Bonded, false) + tstaking.CheckValidator(valAddr, sdkstaking.Unbonding, false) // 600 more blocks happened - height = 700 + height += 600 ctx = ctx.WithBlockHeight(height) // validator added back in - tstaking.DelegateWithPower(sdk.AccAddress(pks[2].Address()), sdk.ValAddress(pks[0].Address()), 50) + tstaking.DelegateWithPower(sdk.AccAddress(pks[2].Address()), valAddr, 50) validatorUpdates = staking.EndBlocker(ctx, app.StakingKeeper) require.Equal(t, 2, len(validatorUpdates)) - tstaking.CheckValidator(valAddr, stakingtypes.Bonded, false) - newPower := int64(150) + tstaking.CheckValidator(valAddr, sdkstaking.Bonded, false) + newPower := power + 50 // validator misses a block app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), newPower, false) height++ // shouldn't be jailed/kicked yet - tstaking.CheckValidator(valAddr, stakingtypes.Bonded, false) + tstaking.CheckValidator(valAddr, sdkstaking.Bonded, false) - // validator misses 500 more blocks, 501 total - latest := height - for ; height < latest+500; height++ { + // validator misses an additional 500 more blocks, after the cooling off period of SignedBlockWindow (here 1000 blocks). + latest := app.SlashingKeeper.SignedBlocksWindow(ctx) + height + for ; height < latest+app.SlashingKeeper.MinSignedPerWindow(ctx); height++ { ctx = ctx.WithBlockHeight(height) app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), newPower, false) } // should now be jailed & kicked staking.EndBlocker(ctx, app.StakingKeeper) - tstaking.CheckValidator(valAddr, stakingtypes.Unbonding, true) + tstaking.CheckValidator(valAddr, sdkstaking.Unbonding, true) // check all the signing information signInfo, found := app.SlashingKeeper.GetValidatorSigningInfo(ctx, consAddr) require.True(t, found) - require.Equal(t, int64(0), signInfo.MissedBlocksCounter) - require.Equal(t, int64(0), signInfo.IndexOffset) - - // array should be cleared - for offset := int64(0); offset < app.SlashingKeeper.SignedBlocksWindow(ctx); offset++ { - missed := app.SlashingKeeper.GetValidatorMissedBlockBitArray(ctx, consAddr, offset) - require.False(t, missed) - } + require.Equal(t, int64(700), signInfo.StartHeight) + require.Equal(t, int64(499), signInfo.MissedBlocksCounter) + require.Equal(t, int64(499), signInfo.IndexOffset) // some blocks pass height = int64(5000) @@ -258,21 +197,26 @@ func TestValidatorDippingInAndOut(t *testing.T) { // validator rejoins and starts signing again app.StakingKeeper.Unjail(ctx, consAddr) + app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), newPower, true) - height++ // validator should not be kicked since we reset counter/array when it was jailed staking.EndBlocker(ctx, app.StakingKeeper) - tstaking.CheckValidator(valAddr, stakingtypes.Bonded, false) + tstaking.CheckValidator(valAddr, sdkstaking.Bonded, false) + + // check start height is correctly set + signInfo, found = app.SlashingKeeper.GetValidatorSigningInfo(ctx, consAddr) + require.True(t, found) + require.Equal(t, height, signInfo.StartHeight) - // validator misses 501 blocks - latest = height - for ; height < latest+501; height++ { + // validator misses 501 blocks after SignedBlockWindow period (1000 blocks) + latest = app.SlashingKeeper.SignedBlocksWindow(ctx) + height + for ; height < latest+app.SlashingKeeper.MinSignedPerWindow(ctx); height++ { ctx = ctx.WithBlockHeight(height) app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), newPower, false) } // validator should now be jailed & kicked staking.EndBlocker(ctx, app.StakingKeeper) - tstaking.CheckValidator(valAddr, stakingtypes.Unbonding, true) + tstaking.CheckValidator(valAddr, sdkstaking.Unbonding, true) } diff --git a/x/slashing/keeper/migrations.go b/x/slashing/keeper/migrations.go index 84f19c01ab7c..ea1dba047b7b 100644 --- a/x/slashing/keeper/migrations.go +++ b/x/slashing/keeper/migrations.go @@ -2,7 +2,6 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" - v043 "github.com/cosmos/cosmos-sdk/x/slashing/legacy/v043" ) // Migrator is a struct for handling in-place store migrations. @@ -16,6 +15,6 @@ func NewMigrator(keeper Keeper) Migrator { } // Migrate1to2 migrates from version 1 to 2. -func (m Migrator) Migrate1to2(ctx sdk.Context) error { - return v043.MigrateStore(ctx, m.keeper.storeKey) +func (m Migrator) Migrate1to2(_ sdk.Context) error { + return nil } diff --git a/x/slashing/keeper/querier.go b/x/slashing/keeper/querier.go index 036f3a16dde3..7f63a26ff153 100644 --- a/x/slashing/keeper/querier.go +++ b/x/slashing/keeper/querier.go @@ -1,6 +1,7 @@ package keeper import ( + errorsmod "cosmossdk.io/errors" abci "github.com/tendermint/tendermint/abci/types" "github.com/cosmos/cosmos-sdk/client" @@ -24,7 +25,7 @@ func NewQuerier(k Keeper, legacyQuerierCdc *codec.LegacyAmino) sdk.Querier { return querySigningInfos(ctx, req, k, legacyQuerierCdc) default: - return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint: %s", types.ModuleName, path[0]) + return nil, errorsmod.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint: %s", types.ModuleName, path[0]) } } } @@ -34,7 +35,7 @@ func queryParams(ctx sdk.Context, k Keeper, legacyQuerierCdc *codec.LegacyAmino) res, err := codec.MarshalJSONIndent(legacyQuerierCdc, params) if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + return nil, errorsmod.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } return res, nil @@ -45,17 +46,17 @@ func querySigningInfo(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacyQu err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms) if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) + return nil, errorsmod.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } signingInfo, found := k.GetValidatorSigningInfo(ctx, sdk.ConsAddress(params.ConsAddress)) if !found { - return nil, sdkerrors.Wrap(types.ErrNoSigningInfoFound, params.ConsAddress) + return nil, errorsmod.Wrap(types.ErrNoSigningInfoFound, params.ConsAddress) } res, err := codec.MarshalJSONIndent(legacyQuerierCdc, signingInfo) if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + return nil, errorsmod.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } return res, nil @@ -66,7 +67,7 @@ func querySigningInfos(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacyQ err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms) if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) + return nil, errorsmod.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } var signingInfos []types.ValidatorSigningInfo @@ -85,7 +86,7 @@ func querySigningInfos(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacyQ res, err := codec.MarshalJSONIndent(legacyQuerierCdc, signingInfos) if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + return nil, errorsmod.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } return res, nil diff --git a/x/slashing/keeper/querier_test.go b/x/slashing/keeper/querier_test.go index bda5fe4bc5f6..939e83708bfb 100644 --- a/x/slashing/keeper/querier_test.go +++ b/x/slashing/keeper/querier_test.go @@ -9,14 +9,14 @@ import ( tmproto "github.com/tendermint/tendermint/proto/tendermint/types" "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/simapp" + simapp "github.com/cosmos/cosmos-sdk/simapp" "github.com/cosmos/cosmos-sdk/x/slashing/keeper" "github.com/cosmos/cosmos-sdk/x/slashing/testslashing" "github.com/cosmos/cosmos-sdk/x/slashing/types" ) func TestNewQuerier(t *testing.T) { - app := simapp.Setup(false) + app := simapp.SetupLSM(t, false) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) app.SlashingKeeper.SetParams(ctx, testslashing.TestParams()) legacyQuerierCdc := codec.NewAminoCodec(app.LegacyAmino()) @@ -34,7 +34,7 @@ func TestNewQuerier(t *testing.T) { func TestQueryParams(t *testing.T) { cdc := codec.NewLegacyAmino() legacyQuerierCdc := codec.NewAminoCodec(cdc) - app := simapp.Setup(false) + app := simapp.SetupLSM(t, false) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) app.SlashingKeeper.SetParams(ctx, testslashing.TestParams()) diff --git a/x/slashing/keeper/signing_info_test.go b/x/slashing/keeper/signing_info_test.go index 2748042b3600..58edf5b369ac 100644 --- a/x/slashing/keeper/signing_info_test.go +++ b/x/slashing/keeper/signing_info_test.go @@ -7,17 +7,17 @@ import ( "github.com/stretchr/testify/require" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - "github.com/cosmos/cosmos-sdk/simapp" + simapp "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/slashing/types" ) func TestGetSetValidatorSigningInfo(t *testing.T) { - app := simapp.Setup(false) + app := simapp.SetupLSM(t, false) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) addrDels := simapp.AddTestAddrsIncremental(app, ctx, 1, app.StakingKeeper.TokensFromConsensusPower(ctx, 200)) - info, found := app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(addrDels[0])) + _, found := app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(addrDels[0])) require.False(t, found) newInfo := types.NewValidatorSigningInfo( sdk.ConsAddress(addrDels[0]), @@ -28,7 +28,7 @@ func TestGetSetValidatorSigningInfo(t *testing.T) { int64(10), ) app.SlashingKeeper.SetValidatorSigningInfo(ctx, sdk.ConsAddress(addrDels[0]), newInfo) - info, found = app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(addrDels[0])) + info, found := app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(addrDels[0])) require.True(t, found) require.Equal(t, info.StartHeight, int64(4)) require.Equal(t, info.IndexOffset, int64(3)) @@ -37,7 +37,7 @@ func TestGetSetValidatorSigningInfo(t *testing.T) { } func TestGetSetValidatorMissedBlockBitArray(t *testing.T) { - app := simapp.Setup(false) + app := simapp.SetupLSM(t, false) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) addrDels := simapp.AddTestAddrsIncremental(app, ctx, 1, app.StakingKeeper.TokensFromConsensusPower(ctx, 200)) @@ -49,7 +49,7 @@ func TestGetSetValidatorMissedBlockBitArray(t *testing.T) { } func TestTombstoned(t *testing.T) { - app := simapp.Setup(false) + app := simapp.SetupLSM(t, false) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) addrDels := simapp.AddTestAddrsIncremental(app, ctx, 1, app.StakingKeeper.TokensFromConsensusPower(ctx, 200)) @@ -73,7 +73,7 @@ func TestTombstoned(t *testing.T) { } func TestJailUntil(t *testing.T) { - app := simapp.Setup(false) + app := simapp.SetupLSM(t, false) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) addrDels := simapp.AddTestAddrsIncremental(app, ctx, 1, app.StakingKeeper.TokensFromConsensusPower(ctx, 200)) diff --git a/x/slashing/keeper/unjail.go b/x/slashing/keeper/unjail.go index 23a9121e5472..b0930e73dd50 100644 --- a/x/slashing/keeper/unjail.go +++ b/x/slashing/keeper/unjail.go @@ -2,8 +2,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/slashing/types" + sdkslashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" ) // Unjail calls the staking Unjail function to unjail a validator if the @@ -11,26 +10,18 @@ import ( func (k Keeper) Unjail(ctx sdk.Context, validatorAddr sdk.ValAddress) error { validator := k.sk.Validator(ctx, validatorAddr) if validator == nil { - return types.ErrNoValidatorForAddress + return sdkslashingtypes.ErrNoValidatorForAddress } // cannot be unjailed if no self-delegation exists selfDel := k.sk.Delegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) if selfDel == nil { - return types.ErrMissingSelfDelegation - } - - tokens := validator.TokensFromShares(selfDel.GetShares()).TruncateInt() - minSelfBond := validator.GetMinSelfDelegation() - if tokens.LT(minSelfBond) { - return sdkerrors.Wrapf( - types.ErrSelfDelegationTooLowToUnjail, "%s less than %s", tokens, minSelfBond, - ) + return sdkslashingtypes.ErrMissingSelfDelegation } // cannot be unjailed if not jailed if !validator.IsJailed() { - return types.ErrValidatorNotJailed + return sdkslashingtypes.ErrValidatorNotJailed } consAddr, err := validator.GetConsAddr() @@ -49,12 +40,12 @@ func (k Keeper) Unjail(ctx sdk.Context, validatorAddr sdk.ValAddress) error { if found { // cannot be unjailed if tombstoned if info.Tombstoned { - return types.ErrValidatorJailed + return sdkslashingtypes.ErrValidatorJailed } // cannot be unjailed until out of jail if ctx.BlockHeader().Time.Before(info.JailedUntil) { - return types.ErrValidatorJailed + return sdkslashingtypes.ErrValidatorJailed } } diff --git a/x/slashing/legacy/v039/types.go b/x/slashing/legacy/v039/types.go deleted file mode 100644 index 1261861e81ad..000000000000 --- a/x/slashing/legacy/v039/types.go +++ /dev/null @@ -1,78 +0,0 @@ -package v039 - -import ( - "time" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -const ( - ModuleName = "slashing" -) - -// Default parameter namespace -const ( - DefaultParamspace = ModuleName - DefaultSignedBlocksWindow = int64(100) - DefaultDowntimeJailDuration = 60 * 10 * time.Second -) - -var ( - DefaultMinSignedPerWindow = sdk.NewDecWithPrec(5, 1) - DefaultSlashFractionDoubleSign = sdk.NewDec(1).Quo(sdk.NewDec(20)) - DefaultSlashFractionDowntime = sdk.NewDec(1).Quo(sdk.NewDec(100)) -) - -// Params - used for initializing default parameter for slashing at genesis -type Params struct { - SignedBlocksWindow int64 `json:"signed_blocks_window" yaml:"signed_blocks_window"` - MinSignedPerWindow sdk.Dec `json:"min_signed_per_window" yaml:"min_signed_per_window"` - DowntimeJailDuration time.Duration `json:"downtime_jail_duration" yaml:"downtime_jail_duration"` - SlashFractionDoubleSign sdk.Dec `json:"slash_fraction_double_sign" yaml:"slash_fraction_double_sign"` - SlashFractionDowntime sdk.Dec `json:"slash_fraction_downtime" yaml:"slash_fraction_downtime"` -} - -// NewParams creates a new Params object -func NewParams( - signedBlocksWindow int64, minSignedPerWindow sdk.Dec, downtimeJailDuration time.Duration, - slashFractionDoubleSign, slashFractionDowntime sdk.Dec, -) Params { - return Params{ - SignedBlocksWindow: signedBlocksWindow, - MinSignedPerWindow: minSignedPerWindow, - DowntimeJailDuration: downtimeJailDuration, - SlashFractionDoubleSign: slashFractionDoubleSign, - SlashFractionDowntime: slashFractionDowntime, - } -} - -// DefaultParams defines the parameters for this module -func DefaultParams() Params { - return NewParams( - DefaultSignedBlocksWindow, DefaultMinSignedPerWindow, DefaultDowntimeJailDuration, - DefaultSlashFractionDoubleSign, DefaultSlashFractionDowntime, - ) -} - -// ValidatorSigningInfo defines the signing info for a validator -type ValidatorSigningInfo struct { - Address sdk.ConsAddress `json:"address" yaml:"address"` // validator consensus address - StartHeight int64 `json:"start_height" yaml:"start_height"` // height at which validator was first a candidate OR was unjailed - IndexOffset int64 `json:"index_offset" yaml:"index_offset"` // index offset into signed block bit array - JailedUntil time.Time `json:"jailed_until" yaml:"jailed_until"` // timestamp validator cannot be unjailed until - Tombstoned bool `json:"tombstoned" yaml:"tombstoned"` // whether or not a validator has been tombstoned (killed out of validator set) - MissedBlocksCounter int64 `json:"missed_blocks_counter" yaml:"missed_blocks_counter"` // missed blocks counter (to avoid scanning the array every time) -} - -// MissedBlock -type MissedBlock struct { - Index int64 `json:"index" yaml:"index"` - Missed bool `json:"missed" yaml:"missed"` -} - -// GenesisState - all slashing state that must be provided at genesis -type GenesisState struct { - Params Params `json:"params" yaml:"params"` - SigningInfos map[string]ValidatorSigningInfo `json:"signing_infos" yaml:"signing_infos"` - MissedBlocks map[string][]MissedBlock `json:"missed_blocks" yaml:"missed_blocks"` -} diff --git a/x/slashing/legacy/v040/keys.go b/x/slashing/legacy/v040/keys.go deleted file mode 100644 index e440f4381725..000000000000 --- a/x/slashing/legacy/v040/keys.go +++ /dev/null @@ -1,69 +0,0 @@ -// Package v040 is copy-pasted from: -// https://github.com/cosmos/cosmos-sdk/blob/v0.41.0/x/slashing/types/keys.go -package v040 - -import ( - "encoding/binary" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/kv" - v040auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v040" -) - -const ( - // ModuleName is the name of the module - ModuleName = "slashing" - - // StoreKey is the store key string for slashing - StoreKey = ModuleName - - // RouterKey is the message route for slashing - RouterKey = ModuleName - - // QuerierRoute is the querier route for slashing - QuerierRoute = ModuleName -) - -// Keys for slashing store -// Items are stored with the following key: values -// -// - 0x01: ValidatorSigningInfo -// -// - 0x02: bool -// -// - 0x03: crypto.PubKey -var ( - ValidatorSigningInfoKeyPrefix = []byte{0x01} // Prefix for signing info - ValidatorMissedBlockBitArrayKeyPrefix = []byte{0x02} // Prefix for missed block bit array - AddrPubkeyRelationKeyPrefix = []byte{0x03} // Prefix for address-pubkey relation -) - -// ValidatorSigningInfoKey - stored by *Consensus* address (not operator address) -func ValidatorSigningInfoKey(v sdk.ConsAddress) []byte { - return append(ValidatorSigningInfoKeyPrefix, v.Bytes()...) -} - -// ValidatorSigningInfoAddress - extract the address from a validator signing info key -func ValidatorSigningInfoAddress(key []byte) (v sdk.ConsAddress) { - kv.AssertKeyAtLeastLength(key, 2) - addr := key[1:] - kv.AssertKeyLength(addr, v040auth.AddrLen) - return sdk.ConsAddress(addr) -} - -// ValidatorMissedBlockBitArrayPrefixKey - stored by *Consensus* address (not operator address) -func ValidatorMissedBlockBitArrayPrefixKey(v sdk.ConsAddress) []byte { - return append(ValidatorMissedBlockBitArrayKeyPrefix, v.Bytes()...) -} - -// ValidatorMissedBlockBitArrayKey - stored by *Consensus* address (not operator address) -func ValidatorMissedBlockBitArrayKey(v sdk.ConsAddress, i int64) []byte { - b := make([]byte, 8) - binary.LittleEndian.PutUint64(b, uint64(i)) - return append(ValidatorMissedBlockBitArrayPrefixKey(v), b...) -} - -// AddrPubkeyRelationKey gets pubkey relation key used to get the pubkey from the address -func AddrPubkeyRelationKey(address []byte) []byte { - return append(AddrPubkeyRelationKeyPrefix, address...) -} diff --git a/x/slashing/legacy/v040/migrate.go b/x/slashing/legacy/v040/migrate.go deleted file mode 100644 index d8a4c124e2e6..000000000000 --- a/x/slashing/legacy/v040/migrate.go +++ /dev/null @@ -1,64 +0,0 @@ -package v040 - -import ( - "sort" - - v039slashing "github.com/cosmos/cosmos-sdk/x/slashing/legacy/v039" - v040slashing "github.com/cosmos/cosmos-sdk/x/slashing/types" -) - -// Migrate accepts exported x/slashing genesis state from v0.39 and migrates it -// to v0.40 x/slashing genesis state. The migration includes: -// -// - Chaning SigningInfos and MissedBlocks from map to array. -// - Convert addresses from bytes to bech32 strings. -// - Re-encode in v0.40 GenesisState. -func Migrate(oldGenState v039slashing.GenesisState) *v040slashing.GenesisState { - // Note that the two following `for` loop over a map's keys, so are not - // deterministic. - newSigningInfos := make([]v040slashing.SigningInfo, 0, len(oldGenState.SigningInfos)) - for address, signingInfo := range oldGenState.SigningInfos { - newSigningInfos = append(newSigningInfos, v040slashing.SigningInfo{ - Address: address, - ValidatorSigningInfo: v040slashing.ValidatorSigningInfo{ - Address: signingInfo.Address.String(), - StartHeight: signingInfo.StartHeight, - IndexOffset: signingInfo.IndexOffset, - JailedUntil: signingInfo.JailedUntil, - Tombstoned: signingInfo.Tombstoned, - MissedBlocksCounter: signingInfo.MissedBlocksCounter, - }, - }) - } - newValidatorMissedBlocks := make([]v040slashing.ValidatorMissedBlocks, 0, len(oldGenState.MissedBlocks)) - for address, validatorMissedBlocks := range oldGenState.MissedBlocks { - newMissedBlocks := make([]v040slashing.MissedBlock, len(validatorMissedBlocks)) - for i, missedBlock := range validatorMissedBlocks { - newMissedBlocks[i] = v040slashing.MissedBlock{ - Index: missedBlock.Index, - Missed: missedBlock.Missed, - } - } - - newValidatorMissedBlocks = append(newValidatorMissedBlocks, v040slashing.ValidatorMissedBlocks{ - Address: address, - MissedBlocks: newMissedBlocks, - }) - } - - // We sort these two arrays by address, so that we get determinstic states. - sort.Slice(newSigningInfos, func(i, j int) bool { return newSigningInfos[i].Address < newSigningInfos[j].Address }) - sort.Slice(newValidatorMissedBlocks, func(i, j int) bool { return newValidatorMissedBlocks[i].Address < newValidatorMissedBlocks[j].Address }) - - return &v040slashing.GenesisState{ - Params: v040slashing.Params{ - SignedBlocksWindow: oldGenState.Params.SignedBlocksWindow, - MinSignedPerWindow: oldGenState.Params.MinSignedPerWindow, - DowntimeJailDuration: oldGenState.Params.DowntimeJailDuration, - SlashFractionDoubleSign: oldGenState.Params.SlashFractionDoubleSign, - SlashFractionDowntime: oldGenState.Params.SlashFractionDowntime, - }, - SigningInfos: newSigningInfos, - MissedBlocks: newValidatorMissedBlocks, - } -} diff --git a/x/slashing/legacy/v040/migrate_test.go b/x/slashing/legacy/v040/migrate_test.go deleted file mode 100644 index f11d2e9d26ee..000000000000 --- a/x/slashing/legacy/v040/migrate_test.go +++ /dev/null @@ -1,140 +0,0 @@ -package v040_test - -import ( - "encoding/json" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/simapp" - sdk "github.com/cosmos/cosmos-sdk/types" - v039slashing "github.com/cosmos/cosmos-sdk/x/slashing/legacy/v039" - v040slashing "github.com/cosmos/cosmos-sdk/x/slashing/legacy/v040" -) - -func TestMigrate(t *testing.T) { - encodingConfig := simapp.MakeTestEncodingConfig() - clientCtx := client.Context{}. - WithInterfaceRegistry(encodingConfig.InterfaceRegistry). - WithTxConfig(encodingConfig.TxConfig). - WithLegacyAmino(encodingConfig.Amino). - WithJSONCodec(encodingConfig.Marshaler) - - addr1, err := sdk.ConsAddressFromBech32("cosmosvalcons104cjmxkrg8y8lmrp25de02e4zf00zle4mzs685") - require.NoError(t, err) - addr2, err := sdk.ConsAddressFromBech32("cosmosvalcons10e4c5p6qk0sycy9u6u43t7csmlx9fyadr9yxph") - require.NoError(t, err) - - gs := v039slashing.GenesisState{ - Params: v039slashing.DefaultParams(), - SigningInfos: map[string]v039slashing.ValidatorSigningInfo{ - "cosmosvalcons10e4c5p6qk0sycy9u6u43t7csmlx9fyadr9yxph": { - Address: addr2, - IndexOffset: 615501, - MissedBlocksCounter: 1, - Tombstoned: false, - }, - "cosmosvalcons104cjmxkrg8y8lmrp25de02e4zf00zle4mzs685": { - Address: addr1, - IndexOffset: 2, - MissedBlocksCounter: 2, - Tombstoned: false, - }, - }, - MissedBlocks: map[string][]v039slashing.MissedBlock{ - "cosmosvalcons10e4c5p6qk0sycy9u6u43t7csmlx9fyadr9yxph": { - { - Index: 2, - Missed: true, - }, - }, - "cosmosvalcons104cjmxkrg8y8lmrp25de02e4zf00zle4mzs685": { - { - Index: 3, - Missed: true, - }, - { - Index: 4, - Missed: true, - }, - }, - }, - } - - migrated := v040slashing.Migrate(gs) - // Check that in `signing_infos` and `missed_blocks`, the address - // cosmosvalcons104cjmxkrg8y8lmrp25de02e4zf00zle4mzs685 - // should always come before the address - // cosmosvalcons10e4c5p6qk0sycy9u6u43t7csmlx9fyadr9yxph - // (in alphabetic order, basically). - expected := `{ - "missed_blocks": [ - { - "address": "cosmosvalcons104cjmxkrg8y8lmrp25de02e4zf00zle4mzs685", - "missed_blocks": [ - { - "index": "3", - "missed": true - }, - { - "index": "4", - "missed": true - } - ] - }, - { - "address": "cosmosvalcons10e4c5p6qk0sycy9u6u43t7csmlx9fyadr9yxph", - "missed_blocks": [ - { - "index": "2", - "missed": true - } - ] - } - ], - "params": { - "downtime_jail_duration": "600s", - "min_signed_per_window": "0.500000000000000000", - "signed_blocks_window": "100", - "slash_fraction_double_sign": "0.050000000000000000", - "slash_fraction_downtime": "0.010000000000000000" - }, - "signing_infos": [ - { - "address": "cosmosvalcons104cjmxkrg8y8lmrp25de02e4zf00zle4mzs685", - "validator_signing_info": { - "address": "cosmosvalcons104cjmxkrg8y8lmrp25de02e4zf00zle4mzs685", - "index_offset": "2", - "jailed_until": "0001-01-01T00:00:00Z", - "missed_blocks_counter": "2", - "start_height": "0", - "tombstoned": false - } - }, - { - "address": "cosmosvalcons10e4c5p6qk0sycy9u6u43t7csmlx9fyadr9yxph", - "validator_signing_info": { - "address": "cosmosvalcons10e4c5p6qk0sycy9u6u43t7csmlx9fyadr9yxph", - "index_offset": "615501", - "jailed_until": "0001-01-01T00:00:00Z", - "missed_blocks_counter": "1", - "start_height": "0", - "tombstoned": false - } - } - ] -}` - - bz, err := clientCtx.Codec.MarshalJSON(migrated) - require.NoError(t, err) - - // Indent the JSON bz correctly. - var jsonObj map[string]interface{} - err = json.Unmarshal(bz, &jsonObj) - require.NoError(t, err) - indentedBz, err := json.MarshalIndent(jsonObj, "", " ") - require.NoError(t, err) - - require.Equal(t, expected, string(indentedBz)) -} diff --git a/x/slashing/legacy/v043/store.go b/x/slashing/legacy/v043/store.go deleted file mode 100644 index daee1d3fc157..000000000000 --- a/x/slashing/legacy/v043/store.go +++ /dev/null @@ -1,20 +0,0 @@ -package v043 - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - v043distribution "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v043" - v040slashing "github.com/cosmos/cosmos-sdk/x/slashing/legacy/v040" -) - -// MigrateStore performs in-place store migrations from v0.40 to v0.43. The -// migration includes: -// -// - Change addresses to be length-prefixed. -func MigrateStore(ctx sdk.Context, storeKey sdk.StoreKey) error { - store := ctx.KVStore(storeKey) - v043distribution.MigratePrefixAddress(store, v040slashing.ValidatorSigningInfoKeyPrefix) - v043distribution.MigratePrefixAddressBytes(store, v040slashing.ValidatorMissedBlockBitArrayKeyPrefix) - v043distribution.MigratePrefixAddress(store, v040slashing.AddrPubkeyRelationKeyPrefix) - - return nil -} diff --git a/x/slashing/legacy/v043/store_test.go b/x/slashing/legacy/v043/store_test.go deleted file mode 100644 index a31b1cd65b3e..000000000000 --- a/x/slashing/legacy/v043/store_test.go +++ /dev/null @@ -1,68 +0,0 @@ -package v043_test - -import ( - "bytes" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/cosmos/cosmos-sdk/testutil" - "github.com/cosmos/cosmos-sdk/testutil/testdata" - sdk "github.com/cosmos/cosmos-sdk/types" - v040slashing "github.com/cosmos/cosmos-sdk/x/slashing/legacy/v040" - v043slashing "github.com/cosmos/cosmos-sdk/x/slashing/legacy/v043" - "github.com/cosmos/cosmos-sdk/x/slashing/types" -) - -func TestStoreMigration(t *testing.T) { - slashingKey := sdk.NewKVStoreKey("slashing") - ctx := testutil.DefaultContext(slashingKey, sdk.NewTransientStoreKey("transient_test")) - store := ctx.KVStore(slashingKey) - - _, _, addr1 := testdata.KeyTestPubAddr() - consAddr := sdk.ConsAddress(addr1) - // Use dummy value for all keys. - value := []byte("foo") - - testCases := []struct { - name string - oldKey []byte - newKey []byte - }{ - { - "ValidatorSigningInfoKey", - v040slashing.ValidatorSigningInfoKey(consAddr), - types.ValidatorSigningInfoKey(consAddr), - }, - { - "ValidatorMissedBlockBitArrayKey", - v040slashing.ValidatorMissedBlockBitArrayKey(consAddr, 2), - types.ValidatorMissedBlockBitArrayKey(consAddr, 2), - }, - { - "AddrPubkeyRelationKey", - v040slashing.AddrPubkeyRelationKey(consAddr), - types.AddrPubkeyRelationKey(consAddr), - }, - } - - // Set all the old keys to the store - for _, tc := range testCases { - store.Set(tc.oldKey, value) - } - - // Run migrations. - err := v043slashing.MigrateStore(ctx, slashingKey) - require.NoError(t, err) - - // Make sure the new keys are set and old keys are deleted. - for _, tc := range testCases { - tc := tc - t.Run(tc.name, func(t *testing.T) { - if !bytes.Equal(tc.oldKey, tc.newKey) { - require.Nil(t, store.Get(tc.oldKey)) - } - require.Equal(t, value, store.Get(tc.newKey)) - }) - } -} diff --git a/x/slashing/module.go b/x/slashing/module.go index 22c57dddf39c..7c238b9bb73e 100644 --- a/x/slashing/module.go +++ b/x/slashing/module.go @@ -6,11 +6,9 @@ import ( "fmt" "math/rand" - "github.com/grpc-ecosystem/grpc-gateway/runtime" - "github.com/gorilla/mux" + "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/spf13/cobra" - abci "github.com/tendermint/tendermint/abci/types" "github.com/cosmos/cosmos-sdk/client" @@ -20,10 +18,10 @@ import ( "github.com/cosmos/cosmos-sdk/types/module" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" "github.com/cosmos/cosmos-sdk/x/slashing/client/cli" - "github.com/cosmos/cosmos-sdk/x/slashing/client/rest" "github.com/cosmos/cosmos-sdk/x/slashing/keeper" "github.com/cosmos/cosmos-sdk/x/slashing/simulation" "github.com/cosmos/cosmos-sdk/x/slashing/types" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" ) var ( @@ -61,7 +59,7 @@ func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { } // ValidateGenesis performs genesis state validation for the slashing module. -func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, config client.TxEncodingConfig, bz json.RawMessage) error { +func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, _ client.TxEncodingConfig, bz json.RawMessage) error { var data types.GenesisState if err := cdc.UnmarshalJSON(bz, &data); err != nil { return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err) @@ -71,13 +69,15 @@ func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, config client.TxEncod } // RegisterRESTRoutes registers the REST routes for the slashing module. -func (AppModuleBasic) RegisterRESTRoutes(clientCtx client.Context, rtr *mux.Router) { - rest.RegisterHandlers(clientCtx, rtr) -} +// Deprecated: RegisterRESTRoutes is deprecated. `x/slashing` legacy REST implementation +// has been removed from the SDK. +func (AppModuleBasic) RegisterRESTRoutes(_ client.Context, _ *mux.Router) {} // RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the slashig module. func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { - types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)) + if err := types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)); err != nil { + panic(err) + } } // GetTxCmd returns the root tx command for the slashing module. @@ -97,11 +97,11 @@ type AppModule struct { keeper keeper.Keeper accountKeeper types.AccountKeeper bankKeeper types.BankKeeper - stakingKeeper types.StakingKeeper + stakingKeeper stakingkeeper.Keeper } // NewAppModule creates a new AppModule object -func NewAppModule(cdc codec.Codec, keeper keeper.Keeper, ak types.AccountKeeper, bk types.BankKeeper, sk types.StakingKeeper) AppModule { +func NewAppModule(cdc codec.Codec, keeper keeper.Keeper, ak types.AccountKeeper, bk types.BankKeeper, sk stakingkeeper.Keeper) AppModule { return AppModule{ AppModuleBasic: AppModuleBasic{cdc: cdc}, keeper: keeper, @@ -119,9 +119,9 @@ func (AppModule) Name() string { // RegisterInvariants registers the slashing module invariants. func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} -// Route returns the message routing key for the slashing module. +// Deprecated: Route returns the message routing key for the slashing module. func (am AppModule) Route() sdk.Route { - return sdk.NewRoute(types.RouterKey, NewHandler(am.keeper)) + return sdk.Route{} } // QuerierRoute returns the slashing module's querier route name. @@ -140,7 +140,10 @@ func (am AppModule) RegisterServices(cfg module.Configurator) { types.RegisterQueryServer(cfg.QueryServer(), am.keeper) m := keeper.NewMigrator(am.keeper) - cfg.RegisterMigration(types.ModuleName, 1, m.Migrate1to2) + err := cfg.RegisterMigration(types.ModuleName, 1, m.Migrate1to2) + if err != nil { + panic(err) + } } // InitGenesis performs genesis initialization for the slashing module. It returns @@ -148,14 +151,14 @@ func (am AppModule) RegisterServices(cfg module.Configurator) { func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json.RawMessage) []abci.ValidatorUpdate { var genesisState types.GenesisState cdc.MustUnmarshalJSON(data, &genesisState) - InitGenesis(ctx, am.keeper, am.stakingKeeper, &genesisState) + am.keeper.InitGenesis(ctx, am.stakingKeeper, &genesisState) return []abci.ValidatorUpdate{} } // ExportGenesis returns the exported genesis state as raw bytes for the slashing // module. func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage { - gs := ExportGenesis(ctx, am.keeper) + gs := am.keeper.ExportGenesis(ctx) return cdc.MustMarshalJSON(gs) } @@ -167,6 +170,12 @@ func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { BeginBlocker(ctx, req, am.keeper) } +// EndBlock returns the end blocker for the slashing module. It returns no validator +// updates. +func (AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { + return []abci.ValidatorUpdate{} +} + // AppModuleSimulation functions // GenerateGenesisState creates a randomized GenState of the slashing module. @@ -175,7 +184,7 @@ func (AppModule) GenerateGenesisState(simState *module.SimulationState) { } // ProposalContents doesn't return any content functions for governance proposals. -func (AppModule) ProposalContents(simState module.SimulationState) []simtypes.WeightedProposalContent { +func (AppModule) ProposalContents(_ module.SimulationState) []simtypes.WeightedProposalContent { return nil } diff --git a/x/slashing/simulation/decoder_test.go b/x/slashing/simulation/decoder_test.go index 94b9f5a1c88c..bdc0aa28fc5a 100644 --- a/x/slashing/simulation/decoder_test.go +++ b/x/slashing/simulation/decoder_test.go @@ -9,14 +9,14 @@ import ( "github.com/stretchr/testify/require" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" - "github.com/cosmos/cosmos-sdk/simapp" + simapp "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/kv" "github.com/cosmos/cosmos-sdk/x/slashing/simulation" "github.com/cosmos/cosmos-sdk/x/slashing/types" ) -// nolint:deadcode,unused,varcheck +//nolint:deadcode,unused,varcheck var ( delPk1 = ed25519.GenPrivKey().PubKey() delAddr1 = sdk.AccAddress(delPk1.Address()) diff --git a/x/slashing/simulation/operations.go b/x/slashing/simulation/operations.go index 13ed2edd6987..1b3aa96973d6 100644 --- a/x/slashing/simulation/operations.go +++ b/x/slashing/simulation/operations.go @@ -18,7 +18,7 @@ import ( // Simulation operation weights constants const ( - OpWeightMsgUnjail = "op_weight_msg_unjail" //nolint:gosec + OpWeightMsgUnjail = "op_weight_msg_unjail" //nolint:gosec // this is just the weight for MsgUnjail, not a hard coded credential ) // WeightedOperations returns all the operations from the module with their respective weights @@ -86,10 +86,10 @@ func SimulateMsgUnjail(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Kee msg := types.NewMsgUnjail(validator.GetOperator()) - txGen := simappparams.MakeTestEncodingConfig().TxConfig + txCfg := simappparams.MakeTestEncodingConfig().TxConfig tx, err := helpers.GenSignedMockTx( r, - txGen, + txCfg, []sdk.Msg{msg}, fees, helpers.DefaultGenTxGas, @@ -102,15 +102,14 @@ func SimulateMsgUnjail(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Kee return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to generate mock tx"), nil, err } - _, res, err := app.Deliver(txGen.TxEncoder(), tx) + _, res, err := app.Deliver(txCfg.TxEncoder(), tx) // result should fail if: // - validator cannot be unjailed due to tombstone // - validator is still in jailed period // - self delegation too low if info.Tombstoned || - ctx.BlockHeader().Time.Before(info.JailedUntil) || - validator.TokensFromShares(selfDel.GetShares()).TruncateInt().LT(validator.GetMinSelfDelegation()) { + ctx.BlockHeader().Time.Before(info.JailedUntil) { if res != nil && err == nil { if info.Tombstoned { return simtypes.NewOperationMsg(msg, true, "", nil), nil, errors.New("validator should not have been unjailed if validator tombstoned") @@ -118,9 +117,6 @@ func SimulateMsgUnjail(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Kee if ctx.BlockHeader().Time.Before(info.JailedUntil) { return simtypes.NewOperationMsg(msg, true, "", nil), nil, errors.New("validator unjailed while validator still in jail period") } - if validator.TokensFromShares(selfDel.GetShares()).TruncateInt().LT(validator.GetMinSelfDelegation()) { - return simtypes.NewOperationMsg(msg, true, "", nil), nil, errors.New("validator unjailed even though self-delegation too low") - } } // msg failed as expected return simtypes.NewOperationMsg(msg, false, "", nil), nil, nil diff --git a/x/slashing/simulation/operations_test.go b/x/slashing/simulation/operations_test.go index ea58a6e67c86..fc10a296aa37 100644 --- a/x/slashing/simulation/operations_test.go +++ b/x/slashing/simulation/operations_test.go @@ -8,11 +8,17 @@ import ( "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + tmtypes "github.com/tendermint/tendermint/types" - "github.com/cosmos/cosmos-sdk/simapp" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + simapp "github.com/cosmos/cosmos-sdk/simapp" + simapp_test "github.com/cosmos/cosmos-sdk/simapp" simappparams "github.com/cosmos/cosmos-sdk/simapp/params" sdk "github.com/cosmos/cosmos-sdk/types" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" "github.com/cosmos/cosmos-sdk/x/slashing/simulation" @@ -22,16 +28,14 @@ import ( // TestWeightedOperations tests the weights of the operations. func TestWeightedOperations(t *testing.T) { - app, ctx := createTestApp(false) + s := rand.NewSource(1) + r := rand.New(s) + app, ctx, accs := createTestApp(t, false, r, 3) ctx.WithChainID("test-chain") cdc := app.AppCodec() appParams := make(simtypes.AppParams) - s := rand.NewSource(1) - r := rand.New(s) - accs := simtypes.RandomAccounts(r, 3) - expected := []struct { weight int opMsgRoute string @@ -53,20 +57,22 @@ func TestWeightedOperations(t *testing.T) { // TestSimulateMsgUnjail tests the normal scenario of a valid message of type types.MsgUnjail. // Abonormal scenarios, where the message is created by an errors, are not tested here. func TestSimulateMsgUnjail(t *testing.T) { - app, ctx := createTestApp(false) + // setup 3 accounts + s := rand.NewSource(5) + r := rand.New(s) + app, ctx, accounts := createTestApp(t, false, r, 3) blockTime := time.Now().UTC() ctx = ctx.WithBlockTime(blockTime) - // setup 3 accounts - s := rand.NewSource(1) - r := rand.New(s) - accounts := getTestingAccounts(t, r, app, ctx, 3) + // remove genesis validator account + accounts = accounts[1:] // setup accounts[0] as validator0 validator0 := getTestingValidator0(t, app, ctx, accounts) // setup validator0 by consensus address - app.StakingKeeper.SetValidatorByConsAddr(ctx, validator0) + err := app.StakingKeeper.SetValidatorByConsAddr(ctx, validator0) + require.NoError(t, err) val0ConsAddress, err := validator0.GetConsAddr() require.NoError(t, err) info := types.NewValidatorSigningInfo(val0ConsAddress, int64(4), int64(3), @@ -81,7 +87,7 @@ func TestSimulateMsgUnjail(t *testing.T) { validator0, issuedShares := validator0.AddTokensFromDel(delTokens) val0AccAddress, err := sdk.ValAddressFromBech32(validator0.OperatorAddress) require.NoError(t, err) - selfDelegation := stakingtypes.NewDelegation(val0AccAddress.Bytes(), validator0.GetOperator(), issuedShares) + selfDelegation := stakingtypes.NewDelegation(val0AccAddress.Bytes(), validator0.GetOperator(), issuedShares, false) app.StakingKeeper.SetDelegation(ctx, selfDelegation) app.DistrKeeper.SetDelegatorStartingInfo(ctx, validator0.GetOperator(), val0AccAddress.Bytes(), distrtypes.NewDelegatorStartingInfo(2, sdk.OneDec(), 200)) @@ -98,35 +104,49 @@ func TestSimulateMsgUnjail(t *testing.T) { require.True(t, operationMsg.OK) require.Equal(t, types.TypeMsgUnjail, msg.Type()) - require.Equal(t, "cosmosvaloper1tnh2q55v8wyygtt9srz5safamzdengsn9dsd7z", msg.ValidatorAddr) + require.Equal(t, "cosmosvaloper17s94pzwhsn4ah25tec27w70n65h5t2scgxzkv2", msg.ValidatorAddr) require.Len(t, futureOperations, 0) } // returns context and an app with updated mint keeper -func createTestApp(isCheckTx bool) (*simapp.SimApp, sdk.Context) { - app := simapp.Setup(isCheckTx) +func createTestApp(t *testing.T, isCheckTx bool, r *rand.Rand, n int) (*simapp.SimApp, sdk.Context, []simtypes.Account) { + accounts := simtypes.RandomAccounts(r, n) + // create validator set with single validator + account := accounts[0] + tmPk, err := cryptocodec.ToTmPubKeyInterface(account.PubKey) + require.NoError(t, err) + validator := tmtypes.NewValidator(tmPk, 1) - ctx := app.BaseApp.NewContext(isCheckTx, tmproto.Header{}) - app.MintKeeper.SetParams(ctx, minttypes.DefaultParams()) - app.MintKeeper.SetMinter(ctx, minttypes.DefaultInitialMinter()) + valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) - return app, ctx -} + // generate genesis account + senderPrivKey := secp256k1.GenPrivKey() + acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), 0, 0) + balance := banktypes.Balance{ + Address: acc.GetAddress().String(), + Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100000000000000))), + } -func getTestingAccounts(t *testing.T, r *rand.Rand, app *simapp.SimApp, ctx sdk.Context, n int) []simtypes.Account { - accounts := simtypes.RandomAccounts(r, n) + app := simapp.SetupWithGenesisValSet(t, valSet, []authtypes.GenesisAccount{acc}, balance) + ctx := app.BaseApp.NewContext(isCheckTx, tmproto.Header{}) initAmt := app.StakingKeeper.TokensFromConsensusPower(ctx, 200) initCoins := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initAmt)) + // remove genesis validator account + accs := accounts[1:] + // add coins to the accounts - for _, account := range accounts { + for _, account := range accs { acc := app.AccountKeeper.NewAccountWithAddress(ctx, account.Address) app.AccountKeeper.SetAccount(ctx, acc) - require.NoError(t, simapp.FundAccount(app.BankKeeper, ctx, account.Address, initCoins)) + require.NoError(t, simapp_test.FundAccount(app.BankKeeper, ctx, account.Address, initCoins)) } - return accounts + app.MintKeeper.SetParams(ctx, minttypes.DefaultParams()) + app.MintKeeper.SetMinter(ctx, minttypes.DefaultInitialMinter()) + + return app, ctx, accounts } func getTestingValidator0(t *testing.T, app *simapp.SimApp, ctx sdk.Context, accounts []simtypes.Account) stakingtypes.Validator { diff --git a/x/slashing/simulation/params.go b/x/slashing/simulation/params.go index e7c8fcf491b0..95df577abdc7 100644 --- a/x/slashing/simulation/params.go +++ b/x/slashing/simulation/params.go @@ -19,7 +19,7 @@ const ( // ParamChanges defines the parameters that can be modified by param change proposals // on the simulation -func ParamChanges(r *rand.Rand) []simtypes.ParamChange { +func ParamChanges(_ *rand.Rand) []simtypes.ParamChange { return []simtypes.ParamChange{ simulation.NewSimParamChange(types.ModuleName, keySignedBlocksWindow, func(r *rand.Rand) string { diff --git a/x/slashing/spec/02_state.md b/x/slashing/spec/02_state.md index b296105feccd..a0298b57499e 100644 --- a/x/slashing/spec/02_state.md +++ b/x/slashing/spec/02_state.md @@ -14,7 +14,7 @@ Proposers are incentivized to include precommits from all validators in the Tend by receiving additional fees proportional to the difference between the voting power included in the `LastCommitInfo` and +2/3 (see [fee distribution](x/distribution/spec/03_begin_block.md)). -``` +```go type LastCommitInfo struct { Round int32 Votes []VoteInfo @@ -27,8 +27,8 @@ number of blocks by being automatically jailed, potentially slashed, and unbonde Information about validator's liveness activity is tracked through `ValidatorSigningInfo`. It is indexed in the store as follows: -- ValidatorSigningInfo: `0x01 | ConsAddrLen (1 byte) | ConsAddress -> ProtocolBuffer(ValSigningInfo)` -- MissedBlocksBitArray: `0x02 | ConsAddrLen (1 byte) | ConsAddress | LittleEndianUint64(signArrayIndex) -> VarInt(didMiss)` (varint is a number encoding format) +* ValidatorSigningInfo: `0x01 | ConsAddrLen (1 byte) | ConsAddress -> ProtocolBuffer(ValSigningInfo)` +* MissedBlocksBitArray: `0x02 | ConsAddrLen (1 byte) | ConsAddress | LittleEndianUint64(signArrayIndex) -> VarInt(didMiss)` (varint is a number encoding format) The first mapping allows us to easily lookup the recent signing info for a validator based on the validator's consensus address. diff --git a/x/slashing/spec/03_messages.md b/x/slashing/spec/03_messages.md index d680b83cdfee..8749315794d9 100644 --- a/x/slashing/spec/03_messages.md +++ b/x/slashing/spec/03_messages.md @@ -22,7 +22,7 @@ message MsgUnjail { Below is a pseudocode of the `MsgSrv/Unjail` RPC: -``` +```go unjail(tx MsgUnjail) validator = getValidator(tx.ValidatorAddr) if validator == nil diff --git a/x/slashing/spec/04_begin_block.md b/x/slashing/spec/04_begin_block.md index 452c27fbc767..99572c419f20 100644 --- a/x/slashing/spec/04_begin_block.md +++ b/x/slashing/spec/04_begin_block.md @@ -81,7 +81,7 @@ for vote in block.LastCommitInfo.Votes { // That's fine since this is just used to filter unbonding delegations & redelegations. distributionHeight := height - sdk.ValidatorUpdateDelay - 1 - Slash(vote.Validator.Address, distributionHeight, vote.Validator.Power, SlashFractionDowntime(), stakingtypes.Downtime) + Slash(vote.Validator.Address, distributionHeight, vote.Validator.Power, SlashFractionDowntime()) Jail(vote.Validator.Address) signInfo.JailedUntil = block.Time.Add(DowntimeJailDuration()) diff --git a/x/slashing/spec/05_hooks.md b/x/slashing/spec/05_hooks.md index c280d34ebdc6..d1234e58ee05 100644 --- a/x/slashing/spec/05_hooks.md +++ b/x/slashing/spec/05_hooks.md @@ -12,16 +12,16 @@ The slashing module implements the `StakingHooks` defined in `x/staking` and are The following hooks impact the slashing state: -+ `AfterValidatorBonded` creates a `ValidatorSigningInfo` instance as described in the following section. -+ `AfterValidatorCreated` stores a validator's consensus key. -+ `AfterValidatorRemoved` removes a validator's consensus key. +* `AfterValidatorBonded` creates a `ValidatorSigningInfo` instance as described in the following section. +* `AfterValidatorCreated` stores a validator's consensus key. +* `AfterValidatorRemoved` removes a validator's consensus key. ## Validator Bonded Upon successful first-time bonding of a new validator, we create a new `ValidatorSigningInfo` structure for the now-bonded validator, which `StartHeight` of the current block. -``` +```go onValidatorBonded(address sdk.ValAddress) signingInfo, found = GetValidatorSigningInfo(address) diff --git a/x/slashing/spec/06_events.md b/x/slashing/spec/06_events.md index 8f259ae46c62..c7dbf5723efa 100644 --- a/x/slashing/spec/06_events.md +++ b/x/slashing/spec/06_events.md @@ -2,17 +2,17 @@ order: 6 --> -# Tags +# Events -The slashing module emits the following events/tags: +The slashing module emits the following events: ## MsgServer ### MsgUnjail -| Type | Attribute Key | Attribute Value | -| ------- | ------------- | --------------- | -| message | module | slashing | +| Type | Attribute Key | Attribute Value | +| ------- | ------------- | ------------------ | +| message | module | slashing | | message | sender | {validatorAddress} | ## Keeper @@ -25,8 +25,9 @@ The slashing module emits the following events/tags: | slash | power | {validatorPower} | | slash | reason | {slashReason} | | slash | jailed [0] | {validatorConsensusAddress} | +| slash | burned coins | {sdk.Int} | -- [0] Only included if the validator is jailed. +* [0] Only included if the validator is jailed. | Type | Attribute Key | Attribute Value | | -------- | ------------- | --------------------------- | @@ -36,7 +37,7 @@ The slashing module emits the following events/tags: ### Slash -+ same as `"slash"` event from `HandleValidatorSignature`, but without the `jailed` attribute. +* same as `"slash"` event from `HandleValidatorSignature`, but without the `jailed` attribute. ### Jail diff --git a/x/slashing/spec/07_tombstone.md b/x/slashing/spec/07_tombstone.md index 216d1836f5a3..ab06c94563ac 100644 --- a/x/slashing/spec/07_tombstone.md +++ b/x/slashing/spec/07_tombstone.md @@ -113,11 +113,11 @@ comparing potential future ones to find the max. Currently the only Tendermint ABCI fault is: -- Unjustified precommits (double signs) +* Unjustified precommits (double signs) It is currently planned to include the following fault in the near future: -- Signing a precommit when you're in unbonding phase (needed to make light client bisection safe) +* Signing a precommit when you're in unbonding phase (needed to make light client bisection safe) Given that these faults are both attributable byzantine faults, we will likely want to slash them equally, and thus we can enact the above change. diff --git a/x/slashing/spec/09_client.md b/x/slashing/spec/09_client.md index fd5b2030fe43..11d82961e622 100644 --- a/x/slashing/spec/09_client.md +++ b/x/slashing/spec/09_client.md @@ -6,31 +6,31 @@ order: 9 A user can query and interact with the `slashing` module using the CLI. -### Query +## Query The `query` commands allow users to query `slashing` state. -```bash +```sh simd query slashing --help ``` -#### params +### params The `params` command allows users to query genesis parameters for the slashing module. -```bash +```sh simd query slashing params [flags] ``` Example: -```bash +```sh simd query slashing params ``` Example Output: -```bash +```yml downtime_jail_duration: 600s min_signed_per_window: "0.500000000000000000" signed_blocks_window: "100" @@ -38,24 +38,24 @@ slash_fraction_double_sign: "0.050000000000000000" slash_fraction_downtime: "0.010000000000000000" ``` -#### signing-info +### signing-info The `signing-info` command allows users to query signing-info of the validator using consensus public key. -```bash +```sh simd query slashing signing-infos [flags] ``` Example: -```bash +```sh simd query slashing signing-info '{"@type":"/cosmos.crypto.ed25519.PubKey","key":"Auxs3865HpB/EfssYOzfqNhEJjzys6jD5B6tPgC8="}' ``` Example Output: -```bash +```yml address: cosmosvalcons1nrqsld3aw6lh6t082frdqc84uwxn0t958c index_offset: "2068" jailed_until: "1970-01-01T00:00:00Z" @@ -64,23 +64,23 @@ start_height: "0" tombstoned: false ``` -#### signing-infos +### signing-infos The `signing-infos` command allows users to query signing infos of all validators. -```bash +```sh simd query slashing signing-infos [flags] ``` Example: -```bash +```sh simd query slashing signing-infos ``` Example Output: -```bash +```yml info: - address: cosmosvalcons1nrqsld3aw6lh6t082frdqc84uwxn0t958c index_offset: "2075" @@ -93,7 +93,7 @@ pagination: total: "0" ``` -### Transactions +## Transactions The `tx` commands allow users to interact with the `slashing` module. @@ -101,7 +101,7 @@ The `tx` commands allow users to interact with the `slashing` module. simd tx slashing --help ``` -#### unjail +### unjail The `unjail` command allows users to unjail a validator previously jailed for downtime. @@ -123,19 +123,19 @@ A user can query the `slashing` module using gRPC endpoints. The `Params` endpoint allows users to query the parameters of slashing module. -```bash +```sh cosmos.slashing.v1beta1.Query/Params ``` Example: -```bash +```sh grpcurl -plaintext localhost:9090 cosmos.slashing.v1beta1.Query/Params ``` Example Output: -```bash +```json { "params": { "signedBlocksWindow": "100", @@ -151,19 +151,19 @@ Example Output: The SigningInfo queries the signing info of given cons address. -```bash +```sh cosmos.slashing.v1beta1.Query/SigningInfo ``` Example: -```bash +```sh grpcurl -plaintext -d '{"cons_address":"cosmosvalcons1nrqsld3aw6lh6t082frdqc84uwxn0t958c"}' localhost:9090 cosmos.slashing.v1beta1.Query/SigningInfo ``` Example Output: -```bash +```json { "valSigningInfo": { "address": "cosmosvalcons1nrqsld3aw6lh6t082frdqc84uwxn0t958c", @@ -177,19 +177,19 @@ Example Output: The SigningInfos queries signing info of all validators. -```bash +```sh cosmos.slashing.v1beta1.Query/SigningInfos ``` Example: -```bash +```sh grpcurl -plaintext localhost:9090 cosmos.slashing.v1beta1.Query/SigningInfos ``` Example Output: -```bash +```json { "info": [ { @@ -210,19 +210,19 @@ A user can query the `slashing` module using REST endpoints. ### Params -```bash +```sh /cosmos/slashing/v1beta1/params ``` Example: -```bash +```sh curl "localhost:1317/cosmos/slashing/v1beta1/params" ``` Example Output: -```bash +```json { "params": { "signed_blocks_window": "100", @@ -235,19 +235,19 @@ Example Output: ### signing_info -```bash +```sh /cosmos/slashing/v1beta1/signing_infos/%s ``` Example: -```bash +```sh curl "localhost:1317/cosmos/slashing/v1beta1/signing_infos/cosmosvalcons1nrqslkwd3pz096lh6t082frdqc84uwxn0t958c" ``` Example Output: -```bash +```json { "val_signing_info": { "address": "cosmosvalcons1nrqslkwd3pz096lh6t082frdqc84uwxn0t958c", @@ -262,19 +262,19 @@ Example Output: ### signing_infos -```bash +```sh /cosmos/slashing/v1beta1/signing_infos ``` Example: -```bash +```sh curl "localhost:1317/cosmos/slashing/v1beta1/signing_infos ``` Example Output: -```bash +```json { "info": [ { diff --git a/x/slashing/spec/README.md b/x/slashing/spec/README.md index b67c6f7c7dcf..d1988cb964d0 100644 --- a/x/slashing/spec/README.md +++ b/x/slashing/spec/README.md @@ -1,5 +1,5 @@