diff --git a/CHANGELOG.md b/CHANGELOG.md index 478a1b16cf5a..012c65d293f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,9 @@ longer panics if the store to load contains substores that we didn't explicitly * (simulation) [\#4824](https://github.com/cosmos/cosmos-sdk/issues/4824) PrintAllInvariants flag will print all failed invariants * (simulation) [\#4490](https://github.com/cosmos/cosmos-sdk/issues/4490) add `InitialBlockHeight` flag to resume a simulation from a given block * Support exporting the simulation stats to a given JSON file +* (simulation) [\#4847](https://github.com/cosmos/cosmos-sdk/issues/4847) `SimApp` and simulation refactors + * Implement `SimulationManager` for executing modules' simulation functionalities in a modularized way + * Add `DecodeStore` to the `SimulationManager` for decoding each module's types * (simulation) [\#4893](https://github.com/cosmos/cosmos-sdk/issues/4893) Change SimApp keepers to be public and add getter functions for keys and codec * (store) [\#4792](https://github.com/cosmos/cosmos-sdk/issues/4792) panic on non-registered store * (types) [\#4821](https://github.com/cosmos/cosmos-sdk/issues/4821) types/errors package added with support for stacktraces. It is meant as a more feature-rich replacement for sdk.Errors in the mid-term. diff --git a/client/cmd_test.go b/client/cmd_test.go index ea71399a60bc..675cc296aeb4 100644 --- a/client/cmd_test.go +++ b/client/cmd_test.go @@ -1,4 +1,3 @@ -// nolint: misspell package client_test import ( @@ -38,10 +37,10 @@ func TestValidateCmd(t *testing.T) { args []string wantErr bool }{ - {"misspelled command", []string{"comission"}, true}, + {"misspelled command", []string{"comission"}, true}, // nolint: misspell {"no command provided", []string{}, false}, - {"help flag", []string{"comission", "--help"}, false}, - {"shorthand help flag", []string{"comission", "-h"}, false}, + {"help flag", []string{"comission", "--help"}, false}, // nolint: misspell + {"shorthand help flag", []string{"comission", "-h"}, false}, // nolint: misspell } for _, tt := range tests { diff --git a/simapp/app.go b/simapp/app.go index dcaa20c92586..b356cbef5b01 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -103,6 +103,9 @@ type SimApp struct { // the module manager mm *module.Manager + + // simulation manager + sm *module.SimulationManager } // NewSimApp returns a reference to an initialized SimApp. @@ -202,6 +205,9 @@ func NewSimApp( app.mm.RegisterInvariants(&app.CrisisKeeper) app.mm.RegisterRoutes(app.Router(), app.QueryRouter()) + app.sm = module.NewSimulationManager(app.mm.Modules) + app.sm.RegisterStoreDecoders() + // initialize stores app.MountKVStores(keys) app.MountTransientStores(tkeys) diff --git a/simapp/sim_test.go b/simapp/sim_test.go index e130191ba25e..180e05b83a6f 100644 --- a/simapp/sim_test.go +++ b/simapp/sim_test.go @@ -20,20 +20,20 @@ import ( "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" - authsim "github.com/cosmos/cosmos-sdk/x/auth/simulation" - "github.com/cosmos/cosmos-sdk/x/bank" + authsimops "github.com/cosmos/cosmos-sdk/x/auth/simulation/operations" + banksimops "github.com/cosmos/cosmos-sdk/x/bank/simulation/operations" distr "github.com/cosmos/cosmos-sdk/x/distribution" - distrsim "github.com/cosmos/cosmos-sdk/x/distribution/simulation" + distrsimops "github.com/cosmos/cosmos-sdk/x/distribution/simulation/operations" "github.com/cosmos/cosmos-sdk/x/gov" - govsim "github.com/cosmos/cosmos-sdk/x/gov/simulation" + govsimops "github.com/cosmos/cosmos-sdk/x/gov/simulation/operations" "github.com/cosmos/cosmos-sdk/x/mint" "github.com/cosmos/cosmos-sdk/x/params" - paramsim "github.com/cosmos/cosmos-sdk/x/params/simulation" + paramsimops "github.com/cosmos/cosmos-sdk/x/params/simulation/operations" "github.com/cosmos/cosmos-sdk/x/simulation" "github.com/cosmos/cosmos-sdk/x/slashing" - slashingsim "github.com/cosmos/cosmos-sdk/x/slashing/simulation" + slashingsimops "github.com/cosmos/cosmos-sdk/x/slashing/simulation/operations" "github.com/cosmos/cosmos-sdk/x/staking" - stakingsim "github.com/cosmos/cosmos-sdk/x/staking/simulation" + stakingsimops "github.com/cosmos/cosmos-sdk/x/staking/simulation/operations" "github.com/cosmos/cosmos-sdk/x/supply" ) @@ -184,7 +184,7 @@ func testAndRunTxs(app *SimApp) []simulation.WeightedOperation { }) return v }(nil), - authsim.SimulateDeductFee(app.AccountKeeper, app.SupplyKeeper), + authsimops.SimulateDeductFee(app.AccountKeeper, app.SupplyKeeper), }, { func(_ *rand.Rand) int { @@ -195,7 +195,7 @@ func testAndRunTxs(app *SimApp) []simulation.WeightedOperation { }) return v }(nil), - bank.SimulateMsgSend(app.AccountKeeper, app.BankKeeper), + banksimops.SimulateMsgSend(app.AccountKeeper, app.BankKeeper), }, { func(_ *rand.Rand) int { @@ -206,7 +206,7 @@ func testAndRunTxs(app *SimApp) []simulation.WeightedOperation { }) return v }(nil), - bank.SimulateSingleInputMsgMultiSend(app.AccountKeeper, app.BankKeeper), + banksimops.SimulateSingleInputMsgMultiSend(app.AccountKeeper, app.BankKeeper), }, { func(_ *rand.Rand) int { @@ -217,7 +217,7 @@ func testAndRunTxs(app *SimApp) []simulation.WeightedOperation { }) return v }(nil), - distrsim.SimulateMsgSetWithdrawAddress(app.AccountKeeper, app.DistrKeeper), + distrsimops.SimulateMsgSetWithdrawAddress(app.DistrKeeper), }, { func(_ *rand.Rand) int { @@ -228,7 +228,7 @@ func testAndRunTxs(app *SimApp) []simulation.WeightedOperation { }) return v }(nil), - distrsim.SimulateMsgWithdrawDelegatorReward(app.AccountKeeper, app.DistrKeeper), + distrsimops.SimulateMsgWithdrawDelegatorReward(app.DistrKeeper), }, { func(_ *rand.Rand) int { @@ -239,7 +239,7 @@ func testAndRunTxs(app *SimApp) []simulation.WeightedOperation { }) return v }(nil), - distrsim.SimulateMsgWithdrawValidatorCommission(app.AccountKeeper, app.DistrKeeper), + distrsimops.SimulateMsgWithdrawValidatorCommission(app.DistrKeeper), }, { func(_ *rand.Rand) int { @@ -250,7 +250,7 @@ func testAndRunTxs(app *SimApp) []simulation.WeightedOperation { }) return v }(nil), - govsim.SimulateSubmittingVotingAndSlashingForProposal(app.GovKeeper, govsim.SimulateTextProposalContent), + govsimops.SimulateSubmittingVotingAndSlashingForProposal(app.GovKeeper, govsimops.SimulateTextProposalContent), }, { func(_ *rand.Rand) int { @@ -261,7 +261,7 @@ func testAndRunTxs(app *SimApp) []simulation.WeightedOperation { }) return v }(nil), - govsim.SimulateSubmittingVotingAndSlashingForProposal(app.GovKeeper, distrsim.SimulateCommunityPoolSpendProposalContent(app.DistrKeeper)), + govsimops.SimulateSubmittingVotingAndSlashingForProposal(app.GovKeeper, distrsimops.SimulateCommunityPoolSpendProposalContent(app.DistrKeeper)), }, { func(_ *rand.Rand) int { @@ -272,7 +272,7 @@ func testAndRunTxs(app *SimApp) []simulation.WeightedOperation { }) return v }(nil), - govsim.SimulateSubmittingVotingAndSlashingForProposal(app.GovKeeper, paramsim.SimulateParamChangeProposalContent), + govsimops.SimulateSubmittingVotingAndSlashingForProposal(app.GovKeeper, paramsimops.SimulateParamChangeProposalContent), }, { func(_ *rand.Rand) int { @@ -283,7 +283,7 @@ func testAndRunTxs(app *SimApp) []simulation.WeightedOperation { }) return v }(nil), - govsim.SimulateMsgDeposit(app.GovKeeper), + govsimops.SimulateMsgDeposit(app.GovKeeper), }, { func(_ *rand.Rand) int { @@ -294,7 +294,7 @@ func testAndRunTxs(app *SimApp) []simulation.WeightedOperation { }) return v }(nil), - stakingsim.SimulateMsgCreateValidator(app.AccountKeeper, app.StakingKeeper), + stakingsimops.SimulateMsgCreateValidator(app.AccountKeeper, app.StakingKeeper), }, { func(_ *rand.Rand) int { @@ -305,7 +305,7 @@ func testAndRunTxs(app *SimApp) []simulation.WeightedOperation { }) return v }(nil), - stakingsim.SimulateMsgEditValidator(app.StakingKeeper), + stakingsimops.SimulateMsgEditValidator(app.StakingKeeper), }, { func(_ *rand.Rand) int { @@ -316,7 +316,7 @@ func testAndRunTxs(app *SimApp) []simulation.WeightedOperation { }) return v }(nil), - stakingsim.SimulateMsgDelegate(app.AccountKeeper, app.StakingKeeper), + stakingsimops.SimulateMsgDelegate(app.AccountKeeper, app.StakingKeeper), }, { func(_ *rand.Rand) int { @@ -327,7 +327,7 @@ func testAndRunTxs(app *SimApp) []simulation.WeightedOperation { }) return v }(nil), - stakingsim.SimulateMsgUndelegate(app.AccountKeeper, app.StakingKeeper), + stakingsimops.SimulateMsgUndelegate(app.AccountKeeper, app.StakingKeeper), }, { func(_ *rand.Rand) int { @@ -338,7 +338,7 @@ func testAndRunTxs(app *SimApp) []simulation.WeightedOperation { }) return v }(nil), - stakingsim.SimulateMsgBeginRedelegate(app.AccountKeeper, app.StakingKeeper), + stakingsimops.SimulateMsgBeginRedelegate(app.AccountKeeper, app.StakingKeeper), }, { func(_ *rand.Rand) int { @@ -349,7 +349,7 @@ func testAndRunTxs(app *SimApp) []simulation.WeightedOperation { }) return v }(nil), - slashingsim.SimulateMsgUnjail(app.SlashingKeeper), + slashingsimops.SimulateMsgUnjail(app.SlashingKeeper), }, } } @@ -597,11 +597,15 @@ func TestAppImportExport(t *testing.T) { storeKeyA := storeKeysPrefix.A storeKeyB := storeKeysPrefix.B prefixes := storeKeysPrefix.Prefixes + storeA := ctxA.KVStore(storeKeyA) storeB := ctxB.KVStore(storeKeyB) - failedKVs := sdk.DiffKVStores(storeA, storeB, prefixes) - fmt.Printf("Compared %d key/value pairs between %s and %s\n", len(failedKVs)/2, storeKeyA, storeKeyB) - require.Len(t, failedKVs, 0, GetSimulationLog(storeKeyA.Name(), app.cdc, newApp.cdc, failedKVs)) + + failedKVAs, failedKVBs := sdk.DiffKVStores(storeA, storeB, prefixes) + require.Equal(t, len(failedKVAs), len(failedKVBs), "unequal sets of key-values to compare") + + fmt.Printf("Compared %d key/value pairs between %s and %s\n", len(failedKVAs), storeKeyA, storeKeyB) + require.Len(t, failedKVAs, 0, GetSimulationLog(storeKeyA.Name(), app.sm.StoreDecoders, app.cdc, failedKVAs, failedKVBs)) } } diff --git a/simapp/utils.go b/simapp/utils.go index 2bfa859e5cd6..90bc28176e9f 100644 --- a/simapp/utils.go +++ b/simapp/utils.go @@ -2,8 +2,6 @@ package simapp import ( - "bytes" - "encoding/binary" "encoding/json" "fmt" "io" @@ -11,7 +9,6 @@ import ( "math/rand" "time" - "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/secp256k1" cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/libs/log" @@ -500,242 +497,21 @@ func GenStakingGenesisState( // GetSimulationLog unmarshals the KVPair's Value to the corresponding type based on the // each's module store key and the prefix bytes of the KVPair's key. -func GetSimulationLog(storeName string, cdcA, cdcB *codec.Codec, kvs []cmn.KVPair) (log string) { - var kvA, kvB cmn.KVPair - for i := 0; i < len(kvs); i += 2 { - kvA = kvs[i] - kvB = kvs[i+1] +func GetSimulationLog(storeName string, sdr sdk.StoreDecoderRegistry, cdc *codec.Codec, kvAs, kvBs []cmn.KVPair) (log string) { + for i := 0; i < len(kvAs); i++ { - if len(kvA.Value) == 0 && len(kvB.Value) == 0 { + if len(kvAs[i].Value) == 0 && len(kvBs[i].Value) == 0 { // skip if the value doesn't have any bytes continue } - switch storeName { - case auth.StoreKey: - log += DecodeAccountStore(cdcA, cdcB, kvA, kvB) - case mint.StoreKey: - log += DecodeMintStore(cdcA, cdcB, kvA, kvB) - case staking.StoreKey: - log += DecodeStakingStore(cdcA, cdcB, kvA, kvB) - case slashing.StoreKey: - log += DecodeSlashingStore(cdcA, cdcB, kvA, kvB) - case gov.StoreKey: - log += DecodeGovStore(cdcA, cdcB, kvA, kvB) - case distribution.StoreKey: - log += DecodeDistributionStore(cdcA, cdcB, kvA, kvB) - case supply.StoreKey: - log += DecodeSupplyStore(cdcA, cdcB, kvA, kvB) - default: - log += fmt.Sprintf("store A %X => %X\nstore B %X => %X\n", kvA.Key, kvA.Value, kvB.Key, kvB.Value) + decoder, ok := sdr[storeName] + if ok { + log += decoder(cdc, kvAs[i], kvBs[i]) + } else { + log += fmt.Sprintf("store A %X => %X\nstore B %X => %X\n", kvAs[i].Key, kvAs[i].Value, kvBs[i].Key, kvBs[i].Value) } } return } - -// DecodeAccountStore unmarshals the KVPair's Value to the corresponding auth type -func DecodeAccountStore(cdcA, cdcB *codec.Codec, kvA, kvB cmn.KVPair) string { - switch { - case bytes.Equal(kvA.Key[:1], auth.AddressStoreKeyPrefix): - var accA, accB auth.Account - cdcA.MustUnmarshalBinaryBare(kvA.Value, &accA) - cdcB.MustUnmarshalBinaryBare(kvB.Value, &accB) - return fmt.Sprintf("%v\n%v", accA, accB) - case bytes.Equal(kvA.Key, auth.GlobalAccountNumberKey): - var globalAccNumberA, globalAccNumberB uint64 - cdcA.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &globalAccNumberA) - cdcB.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &globalAccNumberB) - return fmt.Sprintf("GlobalAccNumberA: %d\nGlobalAccNumberB: %d", globalAccNumberA, globalAccNumberB) - default: - panic(fmt.Sprintf("invalid account key %X", kvA.Key)) - } -} - -// DecodeMintStore unmarshals the KVPair's Value to the corresponding mint type -func DecodeMintStore(cdcA, cdcB *codec.Codec, kvA, kvB cmn.KVPair) string { - switch { - case bytes.Equal(kvA.Key, mint.MinterKey): - var minterA, minterB mint.Minter - cdcA.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &minterA) - cdcB.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &minterB) - return fmt.Sprintf("%v\n%v", minterA, minterB) - default: - panic(fmt.Sprintf("invalid mint key %X", kvA.Key)) - } -} - -// DecodeDistributionStore unmarshals the KVPair's Value to the corresponding distribution type -func DecodeDistributionStore(cdcA, cdcB *codec.Codec, kvA, kvB cmn.KVPair) string { - switch { - case bytes.Equal(kvA.Key[:1], distribution.FeePoolKey): - var feePoolA, feePoolB distribution.FeePool - cdcA.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &feePoolA) - cdcB.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &feePoolB) - return fmt.Sprintf("%v\n%v", feePoolA, feePoolB) - - case bytes.Equal(kvA.Key[:1], distribution.ProposerKey): - return fmt.Sprintf("%v\n%v", sdk.ConsAddress(kvA.Value), sdk.ConsAddress(kvB.Value)) - - case bytes.Equal(kvA.Key[:1], distribution.ValidatorOutstandingRewardsPrefix): - var rewardsA, rewardsB distribution.ValidatorOutstandingRewards - cdcA.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &rewardsA) - cdcB.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &rewardsB) - return fmt.Sprintf("%v\n%v", rewardsA, rewardsB) - - case bytes.Equal(kvA.Key[:1], distribution.DelegatorWithdrawAddrPrefix): - return fmt.Sprintf("%v\n%v", sdk.AccAddress(kvA.Value), sdk.AccAddress(kvB.Value)) - - case bytes.Equal(kvA.Key[:1], distribution.DelegatorStartingInfoPrefix): - var infoA, infoB distribution.DelegatorStartingInfo - cdcA.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &infoA) - cdcB.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &infoB) - return fmt.Sprintf("%v\n%v", infoA, infoB) - - case bytes.Equal(kvA.Key[:1], distribution.ValidatorHistoricalRewardsPrefix): - var rewardsA, rewardsB distribution.ValidatorHistoricalRewards - cdcA.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &rewardsA) - cdcB.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &rewardsB) - return fmt.Sprintf("%v\n%v", rewardsA, rewardsB) - - case bytes.Equal(kvA.Key[:1], distribution.ValidatorCurrentRewardsPrefix): - var rewardsA, rewardsB distribution.ValidatorCurrentRewards - cdcA.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &rewardsA) - cdcB.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &rewardsB) - return fmt.Sprintf("%v\n%v", rewardsA, rewardsB) - - case bytes.Equal(kvA.Key[:1], distribution.ValidatorAccumulatedCommissionPrefix): - var commissionA, commissionB distribution.ValidatorAccumulatedCommission - cdcA.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &commissionA) - cdcB.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &commissionB) - return fmt.Sprintf("%v\n%v", commissionA, commissionB) - - case bytes.Equal(kvA.Key[:1], distribution.ValidatorSlashEventPrefix): - var eventA, eventB distribution.ValidatorSlashEvent - cdcA.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &eventA) - cdcB.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &eventB) - return fmt.Sprintf("%v\n%v", eventA, eventB) - - default: - panic(fmt.Sprintf("invalid distribution key prefix %X", kvA.Key[:1])) - } -} - -// DecodeStakingStore unmarshals the KVPair's Value to the corresponding staking type -func DecodeStakingStore(cdcA, cdcB *codec.Codec, kvA, kvB cmn.KVPair) string { - switch { - case bytes.Equal(kvA.Key[:1], staking.LastTotalPowerKey): - var powerA, powerB sdk.Int - cdcA.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &powerA) - cdcB.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &powerB) - return fmt.Sprintf("%v\n%v", powerA, powerB) - - case bytes.Equal(kvA.Key[:1], staking.ValidatorsKey): - var validatorA, validatorB staking.Validator - cdcA.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &validatorA) - cdcB.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &validatorB) - return fmt.Sprintf("%v\n%v", validatorA, validatorB) - - case bytes.Equal(kvA.Key[:1], staking.LastValidatorPowerKey), - bytes.Equal(kvA.Key[:1], staking.ValidatorsByConsAddrKey), - bytes.Equal(kvA.Key[:1], staking.ValidatorsByPowerIndexKey): - return fmt.Sprintf("%v\n%v", sdk.ValAddress(kvA.Value), sdk.ValAddress(kvB.Value)) - - case bytes.Equal(kvA.Key[:1], staking.DelegationKey): - var delegationA, delegationB staking.Delegation - cdcA.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &delegationA) - cdcB.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &delegationB) - return fmt.Sprintf("%v\n%v", delegationA, delegationB) - - case bytes.Equal(kvA.Key[:1], staking.UnbondingDelegationKey), - bytes.Equal(kvA.Key[:1], staking.UnbondingDelegationByValIndexKey): - var ubdA, ubdB staking.UnbondingDelegation - cdcA.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &ubdA) - cdcB.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &ubdB) - return fmt.Sprintf("%v\n%v", ubdA, ubdB) - - case bytes.Equal(kvA.Key[:1], staking.RedelegationKey), - bytes.Equal(kvA.Key[:1], staking.RedelegationByValSrcIndexKey): - var redA, redB staking.Redelegation - cdcA.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &redA) - cdcB.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &redB) - return fmt.Sprintf("%v\n%v", redA, redB) - - default: - panic(fmt.Sprintf("invalid staking key prefix %X", kvA.Key[:1])) - } -} - -// DecodeSlashingStore unmarshals the KVPair's Value to the corresponding slashing type -func DecodeSlashingStore(cdcA, cdcB *codec.Codec, kvA, kvB cmn.KVPair) string { - switch { - case bytes.Equal(kvA.Key[:1], slashing.ValidatorSigningInfoKey): - var infoA, infoB slashing.ValidatorSigningInfo - cdcA.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &infoA) - cdcB.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &infoB) - return fmt.Sprintf("%v\n%v", infoA, infoB) - - case bytes.Equal(kvA.Key[:1], slashing.ValidatorMissedBlockBitArrayKey): - var missedA, missedB bool - cdcA.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &missedA) - cdcB.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &missedB) - return fmt.Sprintf("missedA: %v\nmissedB: %v", missedA, missedB) - - case bytes.Equal(kvA.Key[:1], slashing.AddrPubkeyRelationKey): - var pubKeyA, pubKeyB crypto.PubKey - cdcA.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &pubKeyA) - cdcB.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &pubKeyB) - bechPKA := sdk.MustBech32ifyAccPub(pubKeyA) - bechPKB := sdk.MustBech32ifyAccPub(pubKeyB) - return fmt.Sprintf("PubKeyA: %s\nPubKeyB: %s", bechPKA, bechPKB) - - default: - panic(fmt.Sprintf("invalid slashing key prefix %X", kvA.Key[:1])) - } -} - -// DecodeGovStore unmarshals the KVPair's Value to the corresponding gov type -func DecodeGovStore(cdcA, cdcB *codec.Codec, kvA, kvB cmn.KVPair) string { - switch { - case bytes.Equal(kvA.Key[:1], gov.ProposalsKeyPrefix): - var proposalA, proposalB gov.Proposal - cdcA.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &proposalA) - cdcB.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &proposalB) - return fmt.Sprintf("%v\n%v", proposalA, proposalB) - - case bytes.Equal(kvA.Key[:1], gov.ActiveProposalQueuePrefix), - bytes.Equal(kvA.Key[:1], gov.InactiveProposalQueuePrefix), - bytes.Equal(kvA.Key[:1], gov.ProposalIDKey): - proposalIDA := binary.LittleEndian.Uint64(kvA.Value) - proposalIDB := binary.LittleEndian.Uint64(kvB.Value) - return fmt.Sprintf("proposalIDA: %d\nProposalIDB: %d", proposalIDA, proposalIDB) - - case bytes.Equal(kvA.Key[:1], gov.DepositsKeyPrefix): - var depositA, depositB gov.Deposit - cdcA.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &depositA) - cdcB.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &depositB) - return fmt.Sprintf("%v\n%v", depositA, depositB) - - case bytes.Equal(kvA.Key[:1], gov.VotesKeyPrefix): - var voteA, voteB gov.Vote - cdcA.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &voteA) - cdcB.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &voteB) - return fmt.Sprintf("%v\n%v", voteA, voteB) - - default: - panic(fmt.Sprintf("invalid governance key prefix %X", kvA.Key[:1])) - } -} - -// DecodeSupplyStore unmarshals the KVPair's Value to the corresponding supply type -func DecodeSupplyStore(cdcA, cdcB *codec.Codec, kvA, kvB cmn.KVPair) string { - switch { - case bytes.Equal(kvA.Key[:1], supply.SupplyKey): - var supplyA, supplyB supply.Supply - cdcA.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &supplyA) - cdcB.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &supplyB) - return fmt.Sprintf("%v\n%v", supplyB, supplyB) - default: - panic(fmt.Sprintf("invalid supply key %X", kvA.Key)) - } -} diff --git a/simapp/utils_test.go b/simapp/utils_test.go index ce51e686d460..d64ea8999dea 100644 --- a/simapp/utils_test.go +++ b/simapp/utils_test.go @@ -1,10 +1,7 @@ package simapp import ( - "encoding/binary" "fmt" - "time" - "testing" "github.com/stretchr/testify/require" @@ -15,13 +12,6 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/distribution" - distr "github.com/cosmos/cosmos-sdk/x/distribution" - "github.com/cosmos/cosmos-sdk/x/gov" - "github.com/cosmos/cosmos-sdk/x/mint" - "github.com/cosmos/cosmos-sdk/x/slashing" - "github.com/cosmos/cosmos-sdk/x/staking" - "github.com/cosmos/cosmos-sdk/x/supply" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -38,322 +28,40 @@ func makeTestCodec() (cdc *codec.Codec) { sdk.RegisterCodec(cdc) codec.RegisterCrypto(cdc) auth.RegisterCodec(cdc) - distr.RegisterCodec(cdc) - gov.RegisterCodec(cdc) - staking.RegisterCodec(cdc) - supply.RegisterCodec(cdc) return } func TestGetSimulationLog(t *testing.T) { cdc := makeTestCodec() - tests := []struct { - store string - kvPairs []cmn.KVPair - }{ - {auth.StoreKey, []cmn.KVPair{ - {Key: auth.AddressStoreKey(delAddr1), Value: cdc.MustMarshalBinaryBare(auth.BaseAccount{})}, - {Key: auth.AddressStoreKey(delAddr1), Value: cdc.MustMarshalBinaryBare(auth.BaseAccount{})}, - }}, - {mint.StoreKey, []cmn.KVPair{ - {Key: mint.MinterKey, Value: cdc.MustMarshalBinaryLengthPrefixed(mint.Minter{})}, - {Key: mint.MinterKey, Value: cdc.MustMarshalBinaryLengthPrefixed(mint.Minter{})}, - }}, - {staking.StoreKey, []cmn.KVPair{ - {Key: staking.LastValidatorPowerKey, Value: valAddr1.Bytes()}, - {Key: staking.LastValidatorPowerKey, Value: valAddr1.Bytes()}, - }}, - {gov.StoreKey, []cmn.KVPair{ - {Key: gov.VoteKey(1, delAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(gov.Vote{})}, - {Key: gov.VoteKey(1, delAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(gov.Vote{})}, - }}, - {distribution.StoreKey, []cmn.KVPair{ - {Key: distr.ProposerKey, Value: consAddr1.Bytes()}, - {Key: distr.ProposerKey, Value: consAddr1.Bytes()}, - }}, - {slashing.StoreKey, []cmn.KVPair{ - {Key: slashing.GetValidatorMissedBlockBitArrayKey(consAddr1, 6), Value: cdc.MustMarshalBinaryLengthPrefixed(true)}, - {Key: slashing.GetValidatorMissedBlockBitArrayKey(consAddr1, 6), Value: cdc.MustMarshalBinaryLengthPrefixed(true)}, - }}, - {supply.StoreKey, []cmn.KVPair{ - {Key: supply.SupplyKey, Value: cdc.MustMarshalBinaryLengthPrefixed(supply.NewSupply(sdk.Coins{}))}, - {Key: supply.SupplyKey, Value: cdc.MustMarshalBinaryLengthPrefixed(supply.NewSupply(sdk.Coins{}))}, - }}, - {"Empty", []cmn.KVPair{{}, {}}}, - {"OtherStore", []cmn.KVPair{ - {Key: []byte("key"), Value: []byte("value")}, - {Key: []byte("key"), Value: []byte("other_value")}, - }}, - } - - for _, tt := range tests { - t.Run(tt.store, func(t *testing.T) { - require.NotPanics(t, func() { GetSimulationLog(tt.store, cdc, cdc, tt.kvPairs) }, tt.store) - }) - } -} - -func TestDecodeAccountStore(t *testing.T) { - cdc := makeTestCodec() - acc := auth.NewBaseAccountWithAddress(delAddr1) - globalAccNumber := uint64(10) - - kvPairs := cmn.KVPairs{ - cmn.KVPair{Key: auth.AddressStoreKey(delAddr1), Value: cdc.MustMarshalBinaryBare(acc)}, - cmn.KVPair{Key: auth.GlobalAccountNumberKey, Value: cdc.MustMarshalBinaryLengthPrefixed(globalAccNumber)}, - cmn.KVPair{Key: []byte{0x99}, Value: []byte{0x99}}, - } - tests := []struct { - name string - expectedLog string - }{ - {"Minter", fmt.Sprintf("%v\n%v", acc, acc)}, - {"GlobalAccNumber", fmt.Sprintf("GlobalAccNumberA: %d\nGlobalAccNumberB: %d", globalAccNumber, globalAccNumber)}, - {"other", ""}, - } - - for i, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - switch i { - case len(tests) - 1: - require.Panics(t, func() { DecodeAccountStore(cdc, cdc, kvPairs[i], kvPairs[i]) }, tt.name) - default: - require.Equal(t, tt.expectedLog, DecodeAccountStore(cdc, cdc, kvPairs[i], kvPairs[i]), tt.name) - } - }) - } -} - -func TestDecodeMintStore(t *testing.T) { - cdc := makeTestCodec() - minter := mint.NewMinter(sdk.OneDec(), sdk.NewDec(15)) - - kvPairs := cmn.KVPairs{ - cmn.KVPair{Key: mint.MinterKey, Value: cdc.MustMarshalBinaryLengthPrefixed(minter)}, - cmn.KVPair{Key: []byte{0x99}, Value: []byte{0x99}}, - } - tests := []struct { - name string - expectedLog string - }{ - {"Minter", fmt.Sprintf("%v\n%v", minter, minter)}, - {"other", ""}, - } - - for i, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - switch i { - case len(tests) - 1: - require.Panics(t, func() { DecodeMintStore(cdc, cdc, kvPairs[i], kvPairs[i]) }, tt.name) - default: - require.Equal(t, tt.expectedLog, DecodeMintStore(cdc, cdc, kvPairs[i], kvPairs[i]), tt.name) - } - }) - } -} - -func TestDecodeDistributionStore(t *testing.T) { - cdc := makeTestCodec() - - decCoins := sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, sdk.OneDec())} - feePool := distr.InitialFeePool() - feePool.CommunityPool = decCoins - info := distr.NewDelegatorStartingInfo(2, sdk.OneDec(), 200) - outstanding := distr.ValidatorOutstandingRewards{decCoins[0]} - commission := distr.ValidatorAccumulatedCommission{decCoins[0]} - historicalRewards := distr.NewValidatorHistoricalRewards(decCoins, 100) - currentRewards := distr.NewValidatorCurrentRewards(decCoins, 5) - slashEvent := distr.NewValidatorSlashEvent(10, sdk.OneDec()) - - kvPairs := cmn.KVPairs{ - cmn.KVPair{Key: distr.FeePoolKey, Value: cdc.MustMarshalBinaryLengthPrefixed(feePool)}, - cmn.KVPair{Key: distr.ProposerKey, Value: consAddr1.Bytes()}, - cmn.KVPair{Key: distr.GetValidatorOutstandingRewardsKey(valAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(outstanding)}, - cmn.KVPair{Key: distr.GetDelegatorWithdrawAddrKey(delAddr1), Value: delAddr1.Bytes()}, - cmn.KVPair{Key: distr.GetDelegatorStartingInfoKey(valAddr1, delAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(info)}, - cmn.KVPair{Key: distr.GetValidatorHistoricalRewardsKey(valAddr1, 100), Value: cdc.MustMarshalBinaryLengthPrefixed(historicalRewards)}, - cmn.KVPair{Key: distr.GetValidatorCurrentRewardsKey(valAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(currentRewards)}, - cmn.KVPair{Key: distr.GetValidatorAccumulatedCommissionKey(valAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(commission)}, - cmn.KVPair{Key: distr.GetValidatorSlashEventKeyPrefix(valAddr1, 13), Value: cdc.MustMarshalBinaryLengthPrefixed(slashEvent)}, - cmn.KVPair{Key: []byte{0x99}, Value: []byte{0x99}}, - } - - tests := []struct { - name string - expectedLog string - }{ - {"FeePool", fmt.Sprintf("%v\n%v", feePool, feePool)}, - {"Proposer", fmt.Sprintf("%v\n%v", consAddr1, consAddr1)}, - {"ValidatorOutstandingRewards", fmt.Sprintf("%v\n%v", outstanding, outstanding)}, - {"DelegatorWithdrawAddr", fmt.Sprintf("%v\n%v", delAddr1, delAddr1)}, - {"DelegatorStartingInfo", fmt.Sprintf("%v\n%v", info, info)}, - {"ValidatorHistoricalRewards", fmt.Sprintf("%v\n%v", historicalRewards, historicalRewards)}, - {"ValidatorCurrentRewards", fmt.Sprintf("%v\n%v", currentRewards, currentRewards)}, - {"ValidatorAccumulatedCommission", fmt.Sprintf("%v\n%v", commission, commission)}, - {"ValidatorSlashEvent", fmt.Sprintf("%v\n%v", slashEvent, slashEvent)}, - {"other", ""}, - } - for i, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - switch i { - case len(tests) - 1: - require.Panics(t, func() { DecodeDistributionStore(cdc, cdc, kvPairs[i], kvPairs[i]) }, tt.name) - default: - require.Equal(t, tt.expectedLog, DecodeDistributionStore(cdc, cdc, kvPairs[i], kvPairs[i]), tt.name) - } - }) - } -} - -func TestDecodeStakingStore(t *testing.T) { - cdc := makeTestCodec() - - bondTime := time.Now().UTC() - - val := staking.NewValidator(valAddr1, delPk1, staking.NewDescription("test", "test", "test", "test", "test")) - del := staking.NewDelegation(delAddr1, valAddr1, sdk.OneDec()) - ubd := staking.NewUnbondingDelegation(delAddr1, valAddr1, 15, bondTime, sdk.OneInt()) - red := staking.NewRedelegation(delAddr1, valAddr1, valAddr1, 12, bondTime, sdk.OneInt(), sdk.OneDec()) - - kvPairs := cmn.KVPairs{ - cmn.KVPair{Key: staking.LastTotalPowerKey, Value: cdc.MustMarshalBinaryLengthPrefixed(sdk.OneInt())}, - cmn.KVPair{Key: staking.GetValidatorKey(valAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(val)}, - cmn.KVPair{Key: staking.LastValidatorPowerKey, Value: valAddr1.Bytes()}, - cmn.KVPair{Key: staking.GetDelegationKey(delAddr1, valAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(del)}, - cmn.KVPair{Key: staking.GetUBDKey(delAddr1, valAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(ubd)}, - cmn.KVPair{Key: staking.GetREDKey(delAddr1, valAddr1, valAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(red)}, - cmn.KVPair{Key: []byte{0x99}, Value: []byte{0x99}}, - } - - tests := []struct { - name string - expectedLog string - }{ - {"LastTotalPower", fmt.Sprintf("%v\n%v", sdk.OneInt(), sdk.OneInt())}, - {"Validator", fmt.Sprintf("%v\n%v", val, val)}, - {"LastValidatorPower/ValidatorsByConsAddr/ValidatorsByPowerIndex", fmt.Sprintf("%v\n%v", valAddr1, valAddr1)}, - {"Delegation", fmt.Sprintf("%v\n%v", del, del)}, - {"UnbondingDelegation", fmt.Sprintf("%v\n%v", ubd, ubd)}, - {"Redelegation", fmt.Sprintf("%v\n%v", red, red)}, - {"other", ""}, - } - for i, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - switch i { - case len(tests) - 1: - require.Panics(t, func() { DecodeStakingStore(cdc, cdc, kvPairs[i], kvPairs[i]) }, tt.name) - default: - require.Equal(t, tt.expectedLog, DecodeStakingStore(cdc, cdc, kvPairs[i], kvPairs[i]), tt.name) - } - }) - } -} - -func TestDecodeSlashingStore(t *testing.T) { - cdc := makeTestCodec() - - info := slashing.NewValidatorSigningInfo(consAddr1, 0, 1, time.Now().UTC(), false, 0) - bechPK := sdk.MustBech32ifyAccPub(delPk1) - missed := true - - kvPairs := cmn.KVPairs{ - cmn.KVPair{Key: slashing.GetValidatorSigningInfoKey(consAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(info)}, - cmn.KVPair{Key: slashing.GetValidatorMissedBlockBitArrayKey(consAddr1, 6), Value: cdc.MustMarshalBinaryLengthPrefixed(missed)}, - cmn.KVPair{Key: slashing.GetAddrPubkeyRelationKey(delAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(delPk1)}, - cmn.KVPair{Key: []byte{0x99}, Value: []byte{0x99}}, - } - - tests := []struct { - name string - expectedLog string - }{ - {"ValidatorSigningInfo", fmt.Sprintf("%v\n%v", info, info)}, - {"ValidatorMissedBlockBitArray", fmt.Sprintf("missedA: %v\nmissedB: %v", missed, missed)}, - {"AddrPubkeyRelation", fmt.Sprintf("PubKeyA: %s\nPubKeyB: %s", bechPK, bechPK)}, - {"other", ""}, - } - for i, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - switch i { - case len(tests) - 1: - require.Panics(t, func() { DecodeSlashingStore(cdc, cdc, kvPairs[i], kvPairs[i]) }, tt.name) - default: - require.Equal(t, tt.expectedLog, DecodeSlashingStore(cdc, cdc, kvPairs[i], kvPairs[i]), tt.name) - } - }) - } -} - -func TestDecodeGovStore(t *testing.T) { - cdc := makeTestCodec() - - endTime := time.Now().UTC() - - content := gov.ContentFromProposalType("test", "test", gov.ProposalTypeText) - proposal := gov.NewProposal(content, 1, endTime, endTime.Add(24*time.Hour)) - proposalIDBz := make([]byte, 8) - binary.LittleEndian.PutUint64(proposalIDBz, 1) - deposit := gov.NewDeposit(1, delAddr1, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.OneInt()))) - vote := gov.NewVote(1, delAddr1, gov.OptionYes) - - kvPairs := cmn.KVPairs{ - cmn.KVPair{Key: gov.ProposalKey(1), Value: cdc.MustMarshalBinaryLengthPrefixed(proposal)}, - cmn.KVPair{Key: gov.InactiveProposalQueueKey(1, endTime), Value: proposalIDBz}, - cmn.KVPair{Key: gov.DepositKey(1, delAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(deposit)}, - cmn.KVPair{Key: gov.VoteKey(1, delAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(vote)}, - cmn.KVPair{Key: []byte{0x99}, Value: []byte{0x99}}, - } - - tests := []struct { - name string - expectedLog string - }{ - {"proposals", fmt.Sprintf("%v\n%v", proposal, proposal)}, - {"proposal IDs", "proposalIDA: 1\nProposalIDB: 1"}, - {"deposits", fmt.Sprintf("%v\n%v", deposit, deposit)}, - {"votes", fmt.Sprintf("%v\n%v", vote, vote)}, - {"other", ""}, - } - - for i, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - switch i { - case len(tests) - 1: - require.Panics(t, func() { DecodeGovStore(cdc, cdc, kvPairs[i], kvPairs[i]) }, tt.name) - default: - require.Equal(t, tt.expectedLog, DecodeGovStore(cdc, cdc, kvPairs[i], kvPairs[i]), tt.name) - } - }) - } -} - -func TestDecodeSupplyStore(t *testing.T) { - cdc := makeTestCodec() - - totalSupply := supply.NewSupply(sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 1000))) - - kvPairs := cmn.KVPairs{ - cmn.KVPair{Key: supply.SupplyKey, Value: cdc.MustMarshalBinaryLengthPrefixed(totalSupply)}, - cmn.KVPair{Key: []byte{0x99}, Value: []byte{0x99}}, - } + decoders := make(sdk.StoreDecoderRegistry) + decoders[auth.StoreKey] = func(cdc *codec.Codec, kvAs, kvBs cmn.KVPair) string { return "10" } tests := []struct { - name string + store string + kvPairs []cmn.KVPair expectedLog string }{ - {"Supply", fmt.Sprintf("%v\n%v", totalSupply, totalSupply)}, - {"other", ""}, + { + "Empty", + []cmn.KVPair{{}}, + "", + }, + { + auth.StoreKey, + []cmn.KVPair{{Key: auth.GlobalAccountNumberKey, Value: cdc.MustMarshalBinaryLengthPrefixed(uint64(10))}}, + "10", + }, + { + "OtherStore", + []cmn.KVPair{{Key: []byte("key"), Value: []byte("value")}}, + fmt.Sprintf("store A %X => %X\nstore B %X => %X\n", []byte("key"), []byte("value"), []byte("key"), []byte("value")), + }, } - for i, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - switch i { - case len(tests) - 1: - require.Panics(t, func() { DecodeSupplyStore(cdc, cdc, kvPairs[i], kvPairs[i]) }, tt.name) - default: - require.Equal(t, tt.expectedLog, DecodeSupplyStore(cdc, cdc, kvPairs[i], kvPairs[i]), tt.name) - } + for _, tt := range tests { + t.Run(tt.store, func(t *testing.T) { + require.Equal(t, tt.expectedLog, GetSimulationLog(tt.store, decoders, cdc, tt.kvPairs, tt.kvPairs), tt.store) }) } } diff --git a/store/types/utils.go b/store/types/utils.go index 39bc7da66500..9efcd02f1646 100644 --- a/store/types/utils.go +++ b/store/types/utils.go @@ -18,7 +18,7 @@ func KVStoreReversePrefixIterator(kvs KVStore, prefix []byte) Iterator { // DiffKVStores compares two KVstores and returns all the key/value pairs // that differ from one another. It also skips value comparison for a set of provided prefixes -func DiffKVStores(a KVStore, b KVStore, prefixesToSkip [][]byte) (kvs []cmn.KVPair) { +func DiffKVStores(a KVStore, b KVStore, prefixesToSkip [][]byte) (kvAs, kvBs []cmn.KVPair) { iterA := a.Iterator(nil, nil) iterB := b.Iterator(nil, nil) @@ -36,7 +36,8 @@ func DiffKVStores(a KVStore, b KVStore, prefixesToSkip [][]byte) (kvs []cmn.KVPa iterB.Next() } if !bytes.Equal(kvA.Key, kvB.Key) { - kvs = append(kvs, []cmn.KVPair{kvA, kvB}...) + kvAs = append(kvAs, kvA) + kvBs = append(kvBs, kvB) } compareValue := true for _, prefix := range prefixesToSkip { @@ -46,7 +47,8 @@ func DiffKVStores(a KVStore, b KVStore, prefixesToSkip [][]byte) (kvs []cmn.KVPa } } if compareValue && !bytes.Equal(kvA.Value, kvB.Value) { - kvs = append(kvs, []cmn.KVPair{kvA, kvB}...) + kvAs = append(kvAs, kvA) + kvBs = append(kvBs, kvB) } } return diff --git a/types/module/module.go b/types/module/module.go index f6083882d979..1501d6a394c0 100644 --- a/types/module/module.go +++ b/types/module/module.go @@ -3,6 +3,7 @@ Package module contains application module patterns and associated "manager" fun The module pattern has been broken down by: - independent module functionality (AppModuleBasic) - inter-dependent module genesis functionality (AppModuleGenesis) + - inter-dependent module simulation functionality (AppModuleSimulation) - inter-dependent module full functionality (AppModule) inter-dependent module functionality is module functionality which somehow @@ -41,6 +42,7 @@ import ( ) //__________________________________________________________________________________________ + // AppModuleBasic is the standard form for basic non-dependant elements of an application module. type AppModuleBasic interface { Name() string @@ -56,9 +58,10 @@ type AppModuleBasic interface { GetQueryCmd(*codec.Codec) *cobra.Command } -// collections of AppModuleBasic +// BasicManager is a collection of AppModuleBasic type BasicManager map[string]AppModuleBasic +// NewBasicManager creates a new BasicManager object func NewBasicManager(modules ...AppModuleBasic) BasicManager { moduleMap := make(map[string]AppModuleBasic) for _, module := range modules { @@ -67,14 +70,14 @@ func NewBasicManager(modules ...AppModuleBasic) BasicManager { return moduleMap } -// RegisterCodecs registers all module codecs +// RegisterCodec registers all module codecs func (bm BasicManager) RegisterCodec(cdc *codec.Codec) { for _, b := range bm { b.RegisterCodec(cdc) } } -// Provided default genesis information for all modules +// DefaultGenesis provides default genesis information for all modules func (bm BasicManager) DefaultGenesis() map[string]json.RawMessage { genesis := make(map[string]json.RawMessage) for _, b := range bm { @@ -83,7 +86,7 @@ func (bm BasicManager) DefaultGenesis() map[string]json.RawMessage { return genesis } -// Provided default genesis information for all modules +// ValidateGenesis performs genesis state validation for all modules func (bm BasicManager) ValidateGenesis(genesis map[string]json.RawMessage) error { for _, b := range bm { if err := b.ValidateGenesis(genesis[b.Name()]); err != nil { @@ -93,14 +96,14 @@ func (bm BasicManager) ValidateGenesis(genesis map[string]json.RawMessage) error return nil } -// RegisterRestRoutes registers all module rest routes +// RegisterRESTRoutes registers all module rest routes func (bm BasicManager) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { for _, b := range bm { b.RegisterRESTRoutes(ctx, rtr) } } -// add all tx commands to the rootTxCmd +// AddTxCommands adds all tx commands to the rootTxCmd func (bm BasicManager) AddTxCommands(rootTxCmd *cobra.Command, cdc *codec.Codec) { for _, b := range bm { if cmd := b.GetTxCmd(cdc); cmd != nil { @@ -109,7 +112,7 @@ func (bm BasicManager) AddTxCommands(rootTxCmd *cobra.Command, cdc *codec.Codec) } } -// add all query commands to the rootQueryCmd +// AddQueryCommands adds all query commands to the rootQueryCmd func (bm BasicManager) AddQueryCommands(rootQueryCmd *cobra.Command, cdc *codec.Codec) { for _, b := range bm { if cmd := b.GetQueryCmd(cdc); cmd != nil { @@ -119,6 +122,7 @@ func (bm BasicManager) AddQueryCommands(rootQueryCmd *cobra.Command, cdc *codec. } //_________________________________________________________ + // AppModuleGenesis is the standard form for an application module genesis functions type AppModuleGenesis interface { AppModuleBasic @@ -126,9 +130,17 @@ type AppModuleGenesis interface { ExportGenesis(sdk.Context) json.RawMessage } +// AppModuleSimulation defines the standard functions that every module should expose +// for the SDK blockchain simulator +type AppModuleSimulation interface { + // register a func to decode the each module's defined types from their corresponding store key + RegisterStoreDecoder(sdk.StoreDecoderRegistry) +} + // AppModule is the standard form for an application module type AppModule interface { AppModuleGenesis + AppModuleSimulation // registers RegisterInvariants(sdk.InvariantRegistry) @@ -139,12 +151,14 @@ type AppModule interface { QuerierRoute() string NewQuerierHandler() sdk.Querier + // ABCI BeginBlock(sdk.Context, abci.RequestBeginBlock) EndBlock(sdk.Context, abci.RequestEndBlock) []abci.ValidatorUpdate } //___________________________ -// app module + +// GenesisOnlyAppModule is an AppModule that only has import/export functionality type GenesisOnlyAppModule struct { AppModuleGenesis } @@ -156,31 +170,35 @@ func NewGenesisOnlyAppModule(amg AppModuleGenesis) AppModule { } } -// register invariants +// RegisterInvariants is a placeholder function register no invariants func (GenesisOnlyAppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} -// module message route ngame +// RegisterStoreDecoder empty store decoder registry +func (GenesisOnlyAppModule) RegisterStoreDecoder(_ sdk.StoreDecoderRegistry) {} + +// Route empty module message route func (GenesisOnlyAppModule) Route() string { return "" } -// module handler +// NewHandler returns an empty module handler func (GenesisOnlyAppModule) NewHandler() sdk.Handler { return nil } -// module querier route ngame +// QuerierRoute returns an empty module querier route func (GenesisOnlyAppModule) QuerierRoute() string { return "" } -// module querier +// NewQuerierHandler returns an empty module querier func (gam GenesisOnlyAppModule) NewQuerierHandler() sdk.Querier { return nil } -// module begin-block +// BeginBlock returns an empty module begin-block func (gam GenesisOnlyAppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) {} -// module end-block +// EndBlock returns an empty module end-block func (GenesisOnlyAppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { return []abci.ValidatorUpdate{} } //____________________________________________________________________________ -// module manager provides the high level utility for managing and executing + +// Manager defines a module manager that provides the high level utility for managing and executing // operations for a group of modules type Manager struct { Modules map[string]AppModule @@ -190,7 +208,7 @@ type Manager struct { OrderEndBlockers []string } -// NewModuleManager creates a new Manager object +// NewManager creates a new Manager object func NewManager(modules ...AppModule) *Manager { moduleMap := make(map[string]AppModule) @@ -209,34 +227,34 @@ func NewManager(modules ...AppModule) *Manager { } } -// set the order of init genesis calls +// SetOrderInitGenesis sets the order of init genesis calls func (m *Manager) SetOrderInitGenesis(moduleNames ...string) { m.OrderInitGenesis = moduleNames } -// set the order of export genesis calls +// SetOrderExportGenesis sets the order of export genesis calls func (m *Manager) SetOrderExportGenesis(moduleNames ...string) { m.OrderExportGenesis = moduleNames } -// set the order of set begin-blocker calls +// SetOrderBeginBlockers sets the order of set begin-blocker calls func (m *Manager) SetOrderBeginBlockers(moduleNames ...string) { m.OrderBeginBlockers = moduleNames } -// set the order of set end-blocker calls +// SetOrderEndBlockers sets the order of set end-blocker calls func (m *Manager) SetOrderEndBlockers(moduleNames ...string) { m.OrderEndBlockers = moduleNames } -// register all module routes and module querier routes +// RegisterInvariants registers all module routes and module querier routes func (m *Manager) RegisterInvariants(ir sdk.InvariantRegistry) { for _, module := range m.Modules { module.RegisterInvariants(ir) } } -// register all module routes and module querier routes +// RegisterRoutes registers all module routes and module querier routes func (m *Manager) RegisterRoutes(router sdk.Router, queryRouter sdk.QueryRouter) { for _, module := range m.Modules { if module.Route() != "" { @@ -248,7 +266,7 @@ func (m *Manager) RegisterRoutes(router sdk.Router, queryRouter sdk.QueryRouter) } } -// perform init genesis functionality for modules +// InitGenesis performs init genesis functionality for modules func (m *Manager) InitGenesis(ctx sdk.Context, genesisData map[string]json.RawMessage) abci.ResponseInitChain { var validatorUpdates []abci.ValidatorUpdate for _, moduleName := range m.OrderInitGenesis { @@ -271,7 +289,7 @@ func (m *Manager) InitGenesis(ctx sdk.Context, genesisData map[string]json.RawMe } } -// perform export genesis functionality for modules +// ExportGenesis performs export genesis functionality for modules func (m *Manager) ExportGenesis(ctx sdk.Context) map[string]json.RawMessage { genesisData := make(map[string]json.RawMessage) for _, moduleName := range m.OrderExportGenesis { @@ -320,6 +338,4 @@ func (m *Manager) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) abci.Respo ValidatorUpdates: validatorUpdates, Events: ctx.EventManager().ABCIEvents(), } -} - -// DONTCOVER +} \ No newline at end of file diff --git a/types/module/simulation.go b/types/module/simulation.go new file mode 100644 index 000000000000..c6901677dc2e --- /dev/null +++ b/types/module/simulation.go @@ -0,0 +1,27 @@ +package module + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// SimulationManager defines a simulation manager that provides the high level utility +// for managing and executing simulation functionalities for a group of modules +type SimulationManager struct { + Modules map[string]AppModule + StoreDecoders sdk.StoreDecoderRegistry +} + +// NewSimulationManager creates a new SimulationManager object +func NewSimulationManager(moduleMap map[string]AppModule) *SimulationManager { + return &SimulationManager{ + Modules: moduleMap, + StoreDecoders: make(sdk.StoreDecoderRegistry), + } +} + +// RegisterStoreDecoders registers each of the modules' store decoders into a map +func (sm *SimulationManager) RegisterStoreDecoders() { + for _, module := range sm.Modules { + module.RegisterStoreDecoder(sm.StoreDecoders) + } +} diff --git a/types/store.go b/types/store.go index 1ebeb62aed21..a8772117e0bb 100644 --- a/types/store.go +++ b/types/store.go @@ -3,6 +3,7 @@ package types import ( cmn "github.com/tendermint/tendermint/libs/common" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store/types" ) @@ -24,6 +25,10 @@ type ( Iterator = types.Iterator ) +// StoreDecoderRegistry defines each of the modules store decoders. Used for ImportExport +// simulation. +type StoreDecoderRegistry map[string]func(cdc *codec.Codec, kvA, kvB cmn.KVPair) string + // Iterator over all the keys with a certain prefix in ascending order func KVStorePrefixIterator(kvs KVStore, prefix []byte) Iterator { return types.KVStorePrefixIterator(kvs, prefix) @@ -36,7 +41,7 @@ func KVStoreReversePrefixIterator(kvs KVStore, prefix []byte) Iterator { // DiffKVStores compares two KVstores and returns all the key/value pairs // that differ from one another. It also skips value comparison for a set of provided prefixes -func DiffKVStores(a KVStore, b KVStore, prefixesToSkip [][]byte) []cmn.KVPair { +func DiffKVStores(a KVStore, b KVStore, prefixesToSkip [][]byte) (kvAs, kvBs []cmn.KVPair) { return types.DiffKVStores(a, b, prefixesToSkip) } diff --git a/x/auth/module.go b/x/auth/module.go index ef978e020082..cb45b4913dc0 100644 --- a/x/auth/module.go +++ b/x/auth/module.go @@ -13,33 +13,36 @@ import ( "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/x/auth/client/cli" "github.com/cosmos/cosmos-sdk/x/auth/client/rest" + "github.com/cosmos/cosmos-sdk/x/auth/simulation" "github.com/cosmos/cosmos-sdk/x/auth/types" ) var ( - _ module.AppModule = AppModule{} - _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModuleSimulation = AppModuleSimulation{} ) -// app module basics object +// AppModuleBasic defines the basic application module used by the auth module. type AppModuleBasic struct{} -// module name +// Name returns the auth module's name. func (AppModuleBasic) Name() string { return types.ModuleName } -// register module codec +// RegisterCodec registers the auth module's types for the given codec. func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { types.RegisterCodec(cdc) } -// default genesis state +// DefaultGenesis returns default genesis state as raw bytes for the auth +// module. func (AppModuleBasic) DefaultGenesis() json.RawMessage { return types.ModuleCdc.MustMarshalJSON(types.DefaultGenesisState()) } -// module validate genesis +// ValidateGenesis performs genesis state validation for the auth module. func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { var data types.GenesisState err := types.ModuleCdc.UnmarshalJSON(bz, &data) @@ -49,61 +52,76 @@ func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { return types.ValidateGenesis(data) } -// register rest routes +// RegisterRESTRoutes registers the REST routes for the auth module. func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { rest.RegisterRoutes(ctx, rtr, types.StoreKey) } -// get the root tx command of this module +// GetTxCmd returns the root tx command for the auth module. func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { return cli.GetTxCmd(cdc) } -// get the root query command of this module +// GetQueryCmd returns the root query command for the auth module. func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { return cli.GetQueryCmd(cdc) } -//___________________________ -// app module object +//____________________________________________________________________________ + +// AppModuleSimulation defines the module simulation functions used by the auth module. +type AppModuleSimulation struct{} + +// RegisterStoreDecoder registers a decoder for auth module's types +func (AppModuleSimulation) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { + sdr[StoreKey] = simulation.DecodeStore +} + +//____________________________________________________________________________ + +// AppModule implements an application module for the auth module. type AppModule struct { AppModuleBasic + AppModuleSimulation + accountKeeper AccountKeeper } // NewAppModule creates a new AppModule object func NewAppModule(accountKeeper AccountKeeper) AppModule { return AppModule{ - AppModuleBasic: AppModuleBasic{}, - accountKeeper: accountKeeper, + AppModuleBasic: AppModuleBasic{}, + AppModuleSimulation: AppModuleSimulation{}, + accountKeeper: accountKeeper, } } -// module name +// Name returns the auth module's name. func (AppModule) Name() string { return types.ModuleName } -// register invariants +// RegisterInvariants performs a no-op. func (AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} -// module message route name +// Route returns the message routing key for the auth module. func (AppModule) Route() string { return "" } -// module handler +// NewHandler returns an sdk.Handler for the auth module. func (AppModule) NewHandler() sdk.Handler { return nil } -// module querier route name +// QuerierRoute returns the auth module's querier route name. func (AppModule) QuerierRoute() string { return types.QuerierRoute } -// module querier +// NewQuerierHandler returns the auth module sdk.Querier. func (am AppModule) NewQuerierHandler() sdk.Querier { return NewQuerier(am.accountKeeper) } -// module init-genesis +// InitGenesis performs genesis initialization for the auth module. It returns +// no validator updates. func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { var genesisState GenesisState types.ModuleCdc.MustUnmarshalJSON(data, &genesisState) @@ -111,16 +129,18 @@ func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.Va return []abci.ValidatorUpdate{} } -// module export genesis +// ExportGenesis returns the exported genesis state as raw bytes for the auth +// module. func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { gs := ExportGenesis(ctx, am.accountKeeper) return types.ModuleCdc.MustMarshalJSON(gs) } -// module begin-block +// BeginBlock returns the begin blocker for the auth module. func (AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} -// module end-block +// EndBlock returns the end blocker for the auth module. It returns no validator +// updates. func (AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { return []abci.ValidatorUpdate{} } diff --git a/x/auth/simulation/decoder.go b/x/auth/simulation/decoder.go new file mode 100644 index 000000000000..933421630bb9 --- /dev/null +++ b/x/auth/simulation/decoder.go @@ -0,0 +1,30 @@ +package simulation + +import ( + "bytes" + "fmt" + + cmn "github.com/tendermint/tendermint/libs/common" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/x/auth/exported" + "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +// DecodeStore unmarshals the KVPair's Value to the corresponding auth type +func DecodeStore(cdc *codec.Codec, kvA, kvB cmn.KVPair) string { + switch { + case bytes.Equal(kvA.Key[:1], types.AddressStoreKeyPrefix): + var accA, accB exported.Account + cdc.MustUnmarshalBinaryBare(kvA.Value, &accA) + cdc.MustUnmarshalBinaryBare(kvB.Value, &accB) + return fmt.Sprintf("%v\n%v", accA, accB) + case bytes.Equal(kvA.Key, types.GlobalAccountNumberKey): + var globalAccNumberA, globalAccNumberB uint64 + cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &globalAccNumberA) + cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &globalAccNumberB) + return fmt.Sprintf("GlobalAccNumberA: %d\nGlobalAccNumberB: %d", globalAccNumberA, globalAccNumberB) + default: + panic(fmt.Sprintf("invalid account key %X", kvA.Key)) + } +} diff --git a/x/auth/simulation/decoder_test.go b/x/auth/simulation/decoder_test.go new file mode 100644 index 000000000000..7b2cbe710a6a --- /dev/null +++ b/x/auth/simulation/decoder_test.go @@ -0,0 +1,61 @@ +package simulation + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/tendermint/tendermint/crypto/ed25519" + cmn "github.com/tendermint/tendermint/libs/common" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +var ( + delPk1 = ed25519.GenPrivKey().PubKey() + delAddr1 = sdk.AccAddress(delPk1.Address()) + valAddr1 = sdk.ValAddress(delPk1.Address()) + consAddr1 = sdk.ConsAddress(delPk1.Address().Bytes()) +) + +func makeTestCodec() (cdc *codec.Codec) { + cdc = codec.New() + sdk.RegisterCodec(cdc) + codec.RegisterCrypto(cdc) + types.RegisterCodec(cdc) + return +} + +func TestDecodeStore(t *testing.T) { + cdc := makeTestCodec() + acc := types.NewBaseAccountWithAddress(delAddr1) + globalAccNumber := uint64(10) + + kvPairs := cmn.KVPairs{ + cmn.KVPair{Key: types.AddressStoreKey(delAddr1), Value: cdc.MustMarshalBinaryBare(acc)}, + cmn.KVPair{Key: types.GlobalAccountNumberKey, Value: cdc.MustMarshalBinaryLengthPrefixed(globalAccNumber)}, + cmn.KVPair{Key: []byte{0x99}, Value: []byte{0x99}}, + } + tests := []struct { + name string + expectedLog string + }{ + {"Account", fmt.Sprintf("%v\n%v", acc, acc)}, + {"GlobalAccNumber", fmt.Sprintf("GlobalAccNumberA: %d\nGlobalAccNumberB: %d", globalAccNumber, globalAccNumber)}, + {"other", ""}, + } + + for i, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + switch i { + case len(tests) - 1: + require.Panics(t, func() { DecodeStore(cdc, kvPairs[i], kvPairs[i]) }, tt.name) + default: + require.Equal(t, tt.expectedLog, DecodeStore(cdc, kvPairs[i], kvPairs[i]), tt.name) + } + }) + } +} diff --git a/x/auth/simulation/fake.go b/x/auth/simulation/operations/fake.go similarity index 99% rename from x/auth/simulation/fake.go rename to x/auth/simulation/operations/fake.go index 18e528f7bcb0..9a516785cb5d 100644 --- a/x/auth/simulation/fake.go +++ b/x/auth/simulation/operations/fake.go @@ -1,4 +1,4 @@ -package simulation +package operations import ( "errors" diff --git a/x/bank/module.go b/x/bank/module.go index bd0a8d54dbc6..c2545036a758 100644 --- a/x/bank/module.go +++ b/x/bank/module.go @@ -19,25 +19,27 @@ import ( ) var ( - _ module.AppModule = AppModule{} - _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModuleSimulation = AppModuleSimulation{} ) -// app module basics object +// AppModuleBasic defines the basic application module used by the bank module. type AppModuleBasic struct{} -// module name +// Name returns the bank module's name. func (AppModuleBasic) Name() string { return ModuleName } -// register module codec +// RegisterCodec registers the bank module's types for the given codec. func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { RegisterCodec(cdc) } -// default genesis state +// DefaultGenesis returns default genesis state as raw bytes for the bank +// module. func (AppModuleBasic) DefaultGenesis() json.RawMessage { return ModuleCdc.MustMarshalJSON(DefaultGenesisState()) } -// module validate genesis +// ValidateGenesis performs genesis state validation for the bank module. func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { var data GenesisState err := ModuleCdc.UnmarshalJSON(bz, &data) @@ -47,23 +49,34 @@ func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { return ValidateGenesis(data) } -// register rest routes +// RegisterRESTRoutes registers the REST routes for the bank module. func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { rest.RegisterRoutes(ctx, rtr) } -// get the root tx command of this module +// GetTxCmd returns the root tx command for the bank module. func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { return cli.GetTxCmd(cdc) } -// get the root query command of this module +// GetQueryCmd returns no root query command for the bank module. func (AppModuleBasic) GetQueryCmd(_ *codec.Codec) *cobra.Command { return nil } -//___________________________ -// app module +//____________________________________________________________________________ + +// AppModuleSimulation defines the module simulation functions used by the bank module. +type AppModuleSimulation struct{} + +// RegisterStoreDecoder performs a no-op. +func (AppModuleSimulation) RegisterStoreDecoder(_ sdk.StoreDecoderRegistry) {} + +//____________________________________________________________________________ + +// AppModule implements an application module for the bank module. type AppModule struct { AppModuleBasic + AppModuleSimulation + keeper Keeper accountKeeper types.AccountKeeper } @@ -71,35 +84,37 @@ type AppModule struct { // NewAppModule creates a new AppModule object func NewAppModule(keeper Keeper, accountKeeper types.AccountKeeper) AppModule { return AppModule{ - AppModuleBasic: AppModuleBasic{}, - keeper: keeper, - accountKeeper: accountKeeper, + AppModuleBasic: AppModuleBasic{}, + AppModuleSimulation: AppModuleSimulation{}, + keeper: keeper, + accountKeeper: accountKeeper, } } -// module name +// Name returns the bank module's name. func (AppModule) Name() string { return ModuleName } -// register invariants +// RegisterInvariants registers the bank module invariants. func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { keeper.RegisterInvariants(ir, am.accountKeeper) } -// module message route name +// Route returns the message routing key for the bank module. func (AppModule) Route() string { return RouterKey } -// module handler +// NewHandler returns an sdk.Handler for the bank module. func (am AppModule) NewHandler() sdk.Handler { return NewHandler(am.keeper) } -// module querier route name +// QuerierRoute returns the bank module's querier route name. func (AppModule) QuerierRoute() string { return RouterKey } -// module querier +// NewQuerierHandler returns the bank module sdk.Querier. func (am AppModule) NewQuerierHandler() sdk.Querier { return keeper.NewQuerier(am.keeper) } -// module init-genesis +// InitGenesis performs genesis initialization for the bank module. It returns +// no validator updates. func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { var genesisState GenesisState ModuleCdc.MustUnmarshalJSON(data, &genesisState) @@ -107,16 +122,18 @@ func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.Va return []abci.ValidatorUpdate{} } -// module export genesis +// ExportGenesis returns the exported genesis state as raw bytes for the bank +// module. func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { gs := ExportGenesis(ctx, am.keeper) return ModuleCdc.MustMarshalJSON(gs) } -// module begin-block +// BeginBlock performs a no-op. func (AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} -// module end-block +// EndBlock returns the end blocker for the bank module. It returns no validator +// updates. func (AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { return []abci.ValidatorUpdate{} } diff --git a/x/bank/simulation.go b/x/bank/simulation/operations/msgs.go similarity index 94% rename from x/bank/simulation.go rename to x/bank/simulation/operations/msgs.go index 85532502eaab..32ff0ad6eb01 100644 --- a/x/bank/simulation.go +++ b/x/bank/simulation/operations/msgs.go @@ -1,4 +1,4 @@ -package bank +package operations import ( "fmt" @@ -8,16 +8,16 @@ import ( "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/bank/internal/keeper" + "github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/bank/internal/types" "github.com/cosmos/cosmos-sdk/x/mock" "github.com/cosmos/cosmos-sdk/x/simulation" ) -// SendTx tests and runs a single msg send where both +// SimulateMsgSend tests and runs a single msg send where both // accounts already exist. -func SimulateMsgSend(mapper types.AccountKeeper, bk keeper.Keeper) simulation.Operation { - handler := NewHandler(bk) +func SimulateMsgSend(mapper types.AccountKeeper, bk bank.Keeper) simulation.Operation { + handler := bank.NewHandler(bk) return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account) ( opMsg simulation.OperationMsg, fOps []simulation.FutureOperation, err error) { @@ -108,10 +108,10 @@ func sendAndVerifyMsgSend(app *baseapp.BaseApp, mapper types.AccountKeeper, msg return nil } -// SingleInputSendMsg tests and runs a single msg multisend, with one input and one output, where both +// SimulateSingleInputMsgMultiSend tests and runs a single msg multisend, with one input and one output, where both // accounts already exist. -func SimulateSingleInputMsgMultiSend(mapper types.AccountKeeper, bk keeper.Keeper) simulation.Operation { - handler := NewHandler(bk) +func SimulateSingleInputMsgMultiSend(mapper types.AccountKeeper, bk bank.Keeper) simulation.Operation { + handler := bank.NewHandler(bk) return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account) ( opMsg simulation.OperationMsg, fOps []simulation.FutureOperation, err error) { diff --git a/x/crisis/module.go b/x/crisis/module.go index 456e5cc7b906..ec84c736d3c8 100644 --- a/x/crisis/module.go +++ b/x/crisis/module.go @@ -18,8 +18,9 @@ import ( ) var ( - _ module.AppModule = AppModule{} - _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModuleSimulation = AppModuleSimulation{} ) // AppModuleBasic defines the basic application module used by the crisis module. @@ -61,9 +62,20 @@ func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { // GetQueryCmd returns no root query command for the crisis module. func (AppModuleBasic) GetQueryCmd(_ *codec.Codec) *cobra.Command { return nil } +//____________________________________________________________________________ + +// AppModuleSimulation defines the module simulation functions used by the crisis module. +type AppModuleSimulation struct{} + +// RegisterStoreDecoder performs a no-op. +func (AppModuleSimulation) RegisterStoreDecoder(_ sdk.StoreDecoderRegistry) {} + +//____________________________________________________________________________ + // AppModule implements an application module for the crisis module. type AppModule struct { AppModuleBasic + AppModuleSimulation // NOTE: We store a reference to the keeper here so that after a module // manager is created, the invariants can be properly registered and @@ -74,8 +86,9 @@ type AppModule struct { // NewAppModule creates a new AppModule object func NewAppModule(keeper *keeper.Keeper) AppModule { return AppModule{ - AppModuleBasic: AppModuleBasic{}, - keeper: keeper, + AppModuleBasic: AppModuleBasic{}, + AppModuleSimulation: AppModuleSimulation{}, + keeper: keeper, } } @@ -121,7 +134,7 @@ func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { return types.ModuleCdc.MustMarshalJSON(gs) } -// BeginBlock returns the begin blocker for the crisis module. +// BeginBlock performs a no-op. func (AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} // EndBlock returns the end blocker for the crisis module. It returns no validator diff --git a/x/distribution/module.go b/x/distribution/module.go index 6fe513ad613e..c156e67aad49 100644 --- a/x/distribution/module.go +++ b/x/distribution/module.go @@ -14,33 +14,36 @@ import ( "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/x/distribution/client/cli" "github.com/cosmos/cosmos-sdk/x/distribution/client/rest" + "github.com/cosmos/cosmos-sdk/x/distribution/simulation" "github.com/cosmos/cosmos-sdk/x/distribution/types" ) var ( - _ module.AppModule = AppModule{} - _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModuleSimulation = AppModuleSimulation{} ) -// app module basics object +// AppModuleBasic defines the basic application module used by the distribution module. type AppModuleBasic struct{} -// module name +// Name returns the distribution module's name. func (AppModuleBasic) Name() string { return ModuleName } -// register module codec +// RegisterCodec registers the distribution module's types for the given codec. func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { RegisterCodec(cdc) } -// default genesis state +// DefaultGenesis returns default genesis state as raw bytes for the distribution +// module. func (AppModuleBasic) DefaultGenesis() json.RawMessage { return ModuleCdc.MustMarshalJSON(DefaultGenesisState()) } -// module validate genesis +// ValidateGenesis performs genesis state validation for the distribution module. func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { var data GenesisState err := ModuleCdc.UnmarshalJSON(bz, &data) @@ -50,24 +53,38 @@ func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { return ValidateGenesis(data) } -// register rest routes +// RegisterRESTRoutes registers the REST routes for the distribution module. func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { rest.RegisterRoutes(ctx, rtr, StoreKey) } -// get the root tx command of this module +// GetTxCmd returns the root tx command for the distribution module. func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { return cli.GetTxCmd(StoreKey, cdc) } -// get the root query command of this module +// GetQueryCmd returns the root query command for the distribution module. func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { return cli.GetQueryCmd(StoreKey, cdc) } -// app module +//____________________________________________________________________________ + +// AppModuleSimulation defines the module simulation functions used by the distribution module. +type AppModuleSimulation struct{} + +// RegisterStoreDecoder registers a decoder for distribution module's types +func (AppModuleSimulation) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { + sdr[StoreKey] = simulation.DecodeStore +} + +//____________________________________________________________________________ + +// AppModule implements an application module for the distribution module. type AppModule struct { AppModuleBasic + AppModuleSimulation + keeper Keeper supplyKeeper types.SupplyKeeper } @@ -75,43 +92,45 @@ type AppModule struct { // NewAppModule creates a new AppModule object func NewAppModule(keeper Keeper, supplyKeeper types.SupplyKeeper) AppModule { return AppModule{ - AppModuleBasic: AppModuleBasic{}, - keeper: keeper, - supplyKeeper: supplyKeeper, + AppModuleBasic: AppModuleBasic{}, + AppModuleSimulation: AppModuleSimulation{}, + keeper: keeper, + supplyKeeper: supplyKeeper, } } -// module name +// Name returns the distribution module's name. func (AppModule) Name() string { return ModuleName } -// register invariants +// RegisterInvariants registers the distribution module invariants. func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { RegisterInvariants(ir, am.keeper) } -// module message route name +// Route returns the message routing key for the distribution module. func (AppModule) Route() string { return RouterKey } -// module handler +// NewHandler returns an sdk.Handler for the distribution module. func (am AppModule) NewHandler() sdk.Handler { return NewHandler(am.keeper) } -// module querier route name +// QuerierRoute returns the distribution module's querier route name. func (AppModule) QuerierRoute() string { return QuerierRoute } -// module querier +// NewQuerierHandler returns the distribution module sdk.Querier. func (am AppModule) NewQuerierHandler() sdk.Querier { return NewQuerier(am.keeper) } -// module init-genesis +// InitGenesis performs genesis initialization for the distribution module. It returns +// no validator updates. func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { var genesisState GenesisState ModuleCdc.MustUnmarshalJSON(data, &genesisState) @@ -119,18 +138,20 @@ func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.Va return []abci.ValidatorUpdate{} } -// module export genesis +// ExportGenesis returns the exported genesis state as raw bytes for the distribution +// module. func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { gs := ExportGenesis(ctx, am.keeper) return ModuleCdc.MustMarshalJSON(gs) } -// module begin-block +// BeginBlock returns the begin blocker for the distribution module. func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { BeginBlocker(ctx, req, am.keeper) } -// module end-block +// EndBlock returns the end blocker for the distribution module. It returns no validator +// updates. func (AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { return []abci.ValidatorUpdate{} } diff --git a/x/distribution/simulation/decoder.go b/x/distribution/simulation/decoder.go new file mode 100644 index 000000000000..c1b8dce42821 --- /dev/null +++ b/x/distribution/simulation/decoder.go @@ -0,0 +1,70 @@ +package simulation + +import ( + "bytes" + "fmt" + + cmn "github.com/tendermint/tendermint/libs/common" + + "github.com/cosmos/cosmos-sdk/codec" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/distribution/keeper" + "github.com/cosmos/cosmos-sdk/x/distribution/types" +) + +// DecodeStore unmarshals the KVPair's Value to the corresponding distribution type +func DecodeStore(cdc *codec.Codec, kvA, kvB cmn.KVPair) string { + switch { + case bytes.Equal(kvA.Key[:1], keeper.FeePoolKey): + var feePoolA, feePoolB types.FeePool + cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &feePoolA) + cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &feePoolB) + return fmt.Sprintf("%v\n%v", feePoolA, feePoolB) + + case bytes.Equal(kvA.Key[:1], keeper.ProposerKey): + return fmt.Sprintf("%v\n%v", sdk.ConsAddress(kvA.Value), sdk.ConsAddress(kvB.Value)) + + case bytes.Equal(kvA.Key[:1], keeper.ValidatorOutstandingRewardsPrefix): + var rewardsA, rewardsB types.ValidatorOutstandingRewards + cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &rewardsA) + cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &rewardsB) + return fmt.Sprintf("%v\n%v", rewardsA, rewardsB) + + case bytes.Equal(kvA.Key[:1], keeper.DelegatorWithdrawAddrPrefix): + return fmt.Sprintf("%v\n%v", sdk.AccAddress(kvA.Value), sdk.AccAddress(kvB.Value)) + + case bytes.Equal(kvA.Key[:1], keeper.DelegatorStartingInfoPrefix): + var infoA, infoB types.DelegatorStartingInfo + cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &infoA) + cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &infoB) + return fmt.Sprintf("%v\n%v", infoA, infoB) + + case bytes.Equal(kvA.Key[:1], keeper.ValidatorHistoricalRewardsPrefix): + var rewardsA, rewardsB types.ValidatorHistoricalRewards + cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &rewardsA) + cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &rewardsB) + return fmt.Sprintf("%v\n%v", rewardsA, rewardsB) + + case bytes.Equal(kvA.Key[:1], keeper.ValidatorCurrentRewardsPrefix): + var rewardsA, rewardsB types.ValidatorCurrentRewards + cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &rewardsA) + cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &rewardsB) + return fmt.Sprintf("%v\n%v", rewardsA, rewardsB) + + case bytes.Equal(kvA.Key[:1], keeper.ValidatorAccumulatedCommissionPrefix): + var commissionA, commissionB types.ValidatorAccumulatedCommission + cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &commissionA) + cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &commissionB) + return fmt.Sprintf("%v\n%v", commissionA, commissionB) + + case bytes.Equal(kvA.Key[:1], keeper.ValidatorSlashEventPrefix): + var eventA, eventB types.ValidatorSlashEvent + cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &eventA) + cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &eventB) + return fmt.Sprintf("%v\n%v", eventA, eventB) + + default: + panic(fmt.Sprintf("invalid distribution key prefix %X", kvA.Key[:1])) + } +} diff --git a/x/distribution/simulation/decoder_test.go b/x/distribution/simulation/decoder_test.go new file mode 100644 index 000000000000..25d846e252d9 --- /dev/null +++ b/x/distribution/simulation/decoder_test.go @@ -0,0 +1,84 @@ +package simulation + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/tendermint/tendermint/crypto/ed25519" + cmn "github.com/tendermint/tendermint/libs/common" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/distribution/keeper" + "github.com/cosmos/cosmos-sdk/x/distribution/types" +) + +var ( + delPk1 = ed25519.GenPrivKey().PubKey() + delAddr1 = sdk.AccAddress(delPk1.Address()) + valAddr1 = sdk.ValAddress(delPk1.Address()) + consAddr1 = sdk.ConsAddress(delPk1.Address().Bytes()) +) + +func makeTestCodec() (cdc *codec.Codec) { + cdc = codec.New() + sdk.RegisterCodec(cdc) + codec.RegisterCrypto(cdc) + types.RegisterCodec(cdc) + return +} + +func TestDecodeDistributionStore(t *testing.T) { + cdc := makeTestCodec() + + decCoins := sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, sdk.OneDec())} + feePool := types.InitialFeePool() + feePool.CommunityPool = decCoins + info := types.NewDelegatorStartingInfo(2, sdk.OneDec(), 200) + outstanding := types.ValidatorOutstandingRewards{decCoins[0]} + commission := types.ValidatorAccumulatedCommission{decCoins[0]} + historicalRewards := types.NewValidatorHistoricalRewards(decCoins, 100) + currentRewards := types.NewValidatorCurrentRewards(decCoins, 5) + slashEvent := types.NewValidatorSlashEvent(10, sdk.OneDec()) + + kvPairs := cmn.KVPairs{ + cmn.KVPair{Key: keeper.FeePoolKey, Value: cdc.MustMarshalBinaryLengthPrefixed(feePool)}, + cmn.KVPair{Key: keeper.ProposerKey, Value: consAddr1.Bytes()}, + cmn.KVPair{Key: keeper.GetValidatorOutstandingRewardsKey(valAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(outstanding)}, + cmn.KVPair{Key: keeper.GetDelegatorWithdrawAddrKey(delAddr1), Value: delAddr1.Bytes()}, + cmn.KVPair{Key: keeper.GetDelegatorStartingInfoKey(valAddr1, delAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(info)}, + cmn.KVPair{Key: keeper.GetValidatorHistoricalRewardsKey(valAddr1, 100), Value: cdc.MustMarshalBinaryLengthPrefixed(historicalRewards)}, + cmn.KVPair{Key: keeper.GetValidatorCurrentRewardsKey(valAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(currentRewards)}, + cmn.KVPair{Key: keeper.GetValidatorAccumulatedCommissionKey(valAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(commission)}, + cmn.KVPair{Key: keeper.GetValidatorSlashEventKeyPrefix(valAddr1, 13), Value: cdc.MustMarshalBinaryLengthPrefixed(slashEvent)}, + cmn.KVPair{Key: []byte{0x99}, Value: []byte{0x99}}, + } + + tests := []struct { + name string + expectedLog string + }{ + {"FeePool", fmt.Sprintf("%v\n%v", feePool, feePool)}, + {"Proposer", fmt.Sprintf("%v\n%v", consAddr1, consAddr1)}, + {"ValidatorOutstandingRewards", fmt.Sprintf("%v\n%v", outstanding, outstanding)}, + {"DelegatorWithdrawAddr", fmt.Sprintf("%v\n%v", delAddr1, delAddr1)}, + {"DelegatorStartingInfo", fmt.Sprintf("%v\n%v", info, info)}, + {"ValidatorHistoricalRewards", fmt.Sprintf("%v\n%v", historicalRewards, historicalRewards)}, + {"ValidatorCurrentRewards", fmt.Sprintf("%v\n%v", currentRewards, currentRewards)}, + {"ValidatorAccumulatedCommission", fmt.Sprintf("%v\n%v", commission, commission)}, + {"ValidatorSlashEvent", fmt.Sprintf("%v\n%v", slashEvent, slashEvent)}, + {"other", ""}, + } + for i, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + switch i { + case len(tests) - 1: + require.Panics(t, func() { DecodeStore(cdc, kvPairs[i], kvPairs[i]) }, tt.name) + default: + require.Equal(t, tt.expectedLog, DecodeStore(cdc, kvPairs[i], kvPairs[i]), tt.name) + } + }) + } +} diff --git a/x/distribution/simulation/msgs.go b/x/distribution/simulation/operations/msgs.go similarity index 88% rename from x/distribution/simulation/msgs.go rename to x/distribution/simulation/operations/msgs.go index ae002010c17a..6d30aac278c3 100644 --- a/x/distribution/simulation/msgs.go +++ b/x/distribution/simulation/operations/msgs.go @@ -1,4 +1,4 @@ -package simulation +package operations import ( "fmt" @@ -6,15 +6,14 @@ import ( "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/distribution" "github.com/cosmos/cosmos-sdk/x/gov" - govsim "github.com/cosmos/cosmos-sdk/x/gov/simulation" + govsimops "github.com/cosmos/cosmos-sdk/x/gov/simulation/operations" "github.com/cosmos/cosmos-sdk/x/simulation" ) // SimulateMsgSetWithdrawAddress generates a MsgSetWithdrawAddress with random values. -func SimulateMsgSetWithdrawAddress(m auth.AccountKeeper, k distribution.Keeper) simulation.Operation { +func SimulateMsgSetWithdrawAddress(k distribution.Keeper) simulation.Operation { handler := distribution.NewHandler(k) return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account) (opMsg simulation.OperationMsg, fOps []simulation.FutureOperation, err error) { @@ -39,7 +38,7 @@ func SimulateMsgSetWithdrawAddress(m auth.AccountKeeper, k distribution.Keeper) } // SimulateMsgWithdrawDelegatorReward generates a MsgWithdrawDelegatorReward with random values. -func SimulateMsgWithdrawDelegatorReward(m auth.AccountKeeper, k distribution.Keeper) simulation.Operation { +func SimulateMsgWithdrawDelegatorReward(k distribution.Keeper) simulation.Operation { handler := distribution.NewHandler(k) return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account) (opMsg simulation.OperationMsg, fOps []simulation.FutureOperation, err error) { @@ -64,7 +63,7 @@ func SimulateMsgWithdrawDelegatorReward(m auth.AccountKeeper, k distribution.Kee } // SimulateMsgWithdrawValidatorCommission generates a MsgWithdrawValidatorCommission with random values. -func SimulateMsgWithdrawValidatorCommission(m auth.AccountKeeper, k distribution.Keeper) simulation.Operation { +func SimulateMsgWithdrawValidatorCommission(k distribution.Keeper) simulation.Operation { handler := distribution.NewHandler(k) return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account) (opMsg simulation.OperationMsg, fOps []simulation.FutureOperation, err error) { @@ -88,7 +87,7 @@ func SimulateMsgWithdrawValidatorCommission(m auth.AccountKeeper, k distribution } // SimulateCommunityPoolSpendProposalContent generates random community-pool-spend proposal content -func SimulateCommunityPoolSpendProposalContent(k distribution.Keeper) govsim.ContentSimulator { +func SimulateCommunityPoolSpendProposalContent(k distribution.Keeper) govsimops.ContentSimulator { return func(r *rand.Rand, _ *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account) gov.Content { recipientAcc := simulation.RandomAcc(r, accs) coins := sdk.Coins{} diff --git a/x/genaccounts/module.go b/x/genaccounts/module.go index 90c01de8b15e..9a1f625aa96b 100644 --- a/x/genaccounts/module.go +++ b/x/genaccounts/module.go @@ -17,27 +17,29 @@ import ( ) var ( - _ module.AppModuleGenesis = AppModule{} - _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModuleGenesis = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModuleSimulation = AppModuleSimulation{} ) -// app module basics object +// AppModuleBasic defines the basic application module used by the genesis accounts module. type AppModuleBasic struct{} -// module name +// Name returns the genesis accounts module's name. func (AppModuleBasic) Name() string { return ModuleName } -// register module codec +// RegisterCodec performs a no-op. func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) {} -// default genesis state +// DefaultGenesis returns default genesis state as raw bytes for the genesis accounts +// module. func (AppModuleBasic) DefaultGenesis() json.RawMessage { return ModuleCdc.MustMarshalJSON(GenesisState{}) } -// module validate genesis +// ValidateGenesis performs genesis state validation for the genesis accounts module. func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { var data GenesisState err := ModuleCdc.UnmarshalJSON(bz, &data) @@ -47,17 +49,18 @@ func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { return ValidateGenesis(data) } -// register rest routes +// RegisterRESTRoutes registers no REST routes for the genesis accounts module. func (AppModuleBasic) RegisterRESTRoutes(_ context.CLIContext, _ *mux.Router) {} -// get the root tx command of this module +// GetTxCmd returns no root tx command for the genesis accounts module. func (AppModuleBasic) GetTxCmd(_ *codec.Codec) *cobra.Command { return nil } -// get the root query command of this module +// GetQueryCmd returns no root query command for the genesis accounts module. func (AppModuleBasic) GetQueryCmd(_ *codec.Codec) *cobra.Command { return nil } // extra function from sdk.AppModuleBasic -// iterate the genesis accounts and perform an operation at each of them + +// IterateGenesisAccounts iterates over the genesis accounts and perform an operation at each of them // - to used by other modules func (AppModuleBasic) IterateGenesisAccounts(cdc *codec.Codec, appGenesis map[string]json.RawMessage, iterateFn func(exported.Account) (stop bool)) { @@ -71,10 +74,21 @@ func (AppModuleBasic) IterateGenesisAccounts(cdc *codec.Codec, appGenesis map[st } } -//___________________________ -// app module +//____________________________________________________________________________ + +// AppModuleSimulation defines the module simulation functions used by the genesis accounts module. +type AppModuleSimulation struct{} + +// RegisterStoreDecoder performs a no-op. +func (AppModuleSimulation) RegisterStoreDecoder(_ sdk.StoreDecoderRegistry) {} + +//____________________________________________________________________________ + +// AppModule implements an application module for the genesis accounts module. type AppModule struct { AppModuleBasic + AppModuleSimulation + accountKeeper types.AccountKeeper } @@ -82,12 +96,14 @@ type AppModule struct { func NewAppModule(accountKeeper types.AccountKeeper) module.AppModule { return module.NewGenesisOnlyAppModule(AppModule{ - AppModuleBasic: AppModuleBasic{}, - accountKeeper: accountKeeper, + AppModuleBasic: AppModuleBasic{}, + AppModuleSimulation: AppModuleSimulation{}, + accountKeeper: accountKeeper, }) } -// module init-genesis +// InitGenesis performs genesis initialization for the genesis accounts module. It returns +// no validator updates. func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { var genesisState GenesisState ModuleCdc.MustUnmarshalJSON(data, &genesisState) @@ -95,7 +111,8 @@ func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.Va return []abci.ValidatorUpdate{} } -// module export genesis +// ExportGenesis returns the exported genesis state as raw bytes for the genesis accounts +// module. func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { gs := ExportGenesis(ctx, am.accountKeeper) return ModuleCdc.MustMarshalJSON(gs) diff --git a/x/genutil/module.go b/x/genutil/module.go index f6051fccb549..63e0a93a4acc 100644 --- a/x/genutil/module.go +++ b/x/genutil/module.go @@ -16,27 +16,29 @@ import ( ) var ( - _ module.AppModuleGenesis = AppModule{} - _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModuleGenesis = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModuleSimulation = AppModuleSimulation{} ) -// app module basics object +// AppModuleBasic defines the basic application module used by the genutil module. type AppModuleBasic struct{} -// module name +// Name returns the genutil module's name. func (AppModuleBasic) Name() string { return ModuleName } -// register module codec +// RegisterCodec registers the genutil module's types for the given codec. func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) {} -// default genesis state +// DefaultGenesis returns default genesis state as raw bytes for the genutil +// module. func (AppModuleBasic) DefaultGenesis() json.RawMessage { return ModuleCdc.MustMarshalJSON(GenesisState{}) } -// module validate genesis +// ValidateGenesis performs genesis state validation for the genutil module. func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { var data GenesisState err := ModuleCdc.UnmarshalJSON(bz, &data) @@ -46,19 +48,30 @@ func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { return ValidateGenesis(data) } -// register rest routes +// RegisterRESTRoutes registers the REST routes for the genutil module. func (AppModuleBasic) RegisterRESTRoutes(_ context.CLIContext, _ *mux.Router) {} -// get the root tx command of this module +// GetTxCmd returns no root tx command for the genutil module. func (AppModuleBasic) GetTxCmd(_ *codec.Codec) *cobra.Command { return nil } -// get the root query command of this module +// GetQueryCmd returns no root query command for the genutil module. func (AppModuleBasic) GetQueryCmd(_ *codec.Codec) *cobra.Command { return nil } -//___________________________ -// app module +//____________________________________________________________________________ + +// AppModuleSimulation defines the module simulation functions used by the auth module. +type AppModuleSimulation struct{} + +// RegisterStoreDecoder registers a decoder for genutil module's types +func (AppModuleSimulation) RegisterStoreDecoder(_ sdk.StoreDecoderRegistry) {} + +//____________________________________________________________________________ + +// AppModule implements an application module for the genutil module. type AppModule struct { AppModuleBasic + AppModuleSimulation + accountKeeper types.AccountKeeper stakingKeeper types.StakingKeeper deliverTx deliverTxfn @@ -69,21 +82,24 @@ func NewAppModule(accountKeeper types.AccountKeeper, stakingKeeper types.StakingKeeper, deliverTx deliverTxfn) module.AppModule { return module.NewGenesisOnlyAppModule(AppModule{ - AppModuleBasic: AppModuleBasic{}, - accountKeeper: accountKeeper, - stakingKeeper: stakingKeeper, - deliverTx: deliverTx, + AppModuleBasic: AppModuleBasic{}, + AppModuleSimulation: AppModuleSimulation{}, + accountKeeper: accountKeeper, + stakingKeeper: stakingKeeper, + deliverTx: deliverTx, }) } -// module init-genesis +// InitGenesis performs genesis initialization for the genutil module. It returns +// no validator updates. func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { var genesisState GenesisState ModuleCdc.MustUnmarshalJSON(data, &genesisState) return InitGenesis(ctx, ModuleCdc, am.stakingKeeper, am.deliverTx, genesisState) } -// module export genesis +// ExportGenesis returns the exported genesis state as raw bytes for the genutil +// module. func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { return nil } diff --git a/x/gov/module.go b/x/gov/module.go index 16e304313032..81100e0ffae9 100644 --- a/x/gov/module.go +++ b/x/gov/module.go @@ -17,15 +17,17 @@ import ( "github.com/cosmos/cosmos-sdk/x/gov/client" "github.com/cosmos/cosmos-sdk/x/gov/client/cli" "github.com/cosmos/cosmos-sdk/x/gov/client/rest" + "github.com/cosmos/cosmos-sdk/x/gov/simulation" "github.com/cosmos/cosmos-sdk/x/gov/types" ) var ( - _ module.AppModule = AppModule{} - _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModuleSimulation = AppModuleSimulation{} ) -// AppModuleBasic - app module basics object +// AppModuleBasic defines the basic application module used by the gov module. type AppModuleBasic struct { proposalHandlers []client.ProposalHandler // proposal handlers which live in governance cli and rest } @@ -37,24 +39,23 @@ func NewAppModuleBasic(proposalHandlers ...client.ProposalHandler) AppModuleBasi } } -var _ module.AppModuleBasic = AppModuleBasic{} - -// Name - module name +// Name returns the gov module's name. func (AppModuleBasic) Name() string { return types.ModuleName } -// RegisterCodec -register module codec +// RegisterCodec registers the gov module's types for the given codec. func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { RegisterCodec(cdc) } -// DefaultGenesis - default genesis state +// DefaultGenesis returns default genesis state as raw bytes for the gov +// module. func (AppModuleBasic) DefaultGenesis() json.RawMessage { return ModuleCdc.MustMarshalJSON(DefaultGenesisState()) } -// ValidateGenesis - module validate genesis +// ValidateGenesis performs genesis state validation for the gov module. func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { var data GenesisState err := ModuleCdc.UnmarshalJSON(bz, &data) @@ -64,7 +65,7 @@ func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { return ValidateGenesis(data) } -// RegisterRESTRoutes - register rest routes +// RegisterRESTRoutes registers the REST routes for the gov module. func (a AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { var proposalRESTHandlers []rest.ProposalRESTHandler for _, proposalHandler := range a.proposalHandlers { @@ -74,7 +75,7 @@ func (a AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Rout rest.RegisterRoutes(ctx, rtr, proposalRESTHandlers) } -// GetTxCmd gets the root tx command of this module +// GetTxCmd returns the root tx command for the gov module. func (a AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { var proposalCLIHandlers []*cobra.Command @@ -85,16 +86,28 @@ func (a AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { return cli.GetTxCmd(StoreKey, cdc, proposalCLIHandlers) } -// GetQueryCmd gets the root query command of this module +// GetQueryCmd returns the root query command for the gov module. func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { return cli.GetQueryCmd(StoreKey, cdc) } -//___________________________ +//____________________________________________________________________________ + +// AppModuleSimulation defines the module simulation functions used by the gov module. +type AppModuleSimulation struct{} + +// RegisterStoreDecoder performs a no-op. +func (AppModuleSimulation) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { + sdr[StoreKey] = simulation.DecodeStore +} + +//____________________________________________________________________________ -// AppModule - app module object +// AppModule implements an application module for the gov module. type AppModule struct { AppModuleBasic + AppModuleSimulation + keeper Keeper supplyKeeper types.SupplyKeeper } @@ -102,13 +115,14 @@ type AppModule struct { // NewAppModule creates a new AppModule object func NewAppModule(keeper Keeper, supplyKeeper types.SupplyKeeper) AppModule { return AppModule{ - AppModuleBasic: AppModuleBasic{}, - keeper: keeper, - supplyKeeper: supplyKeeper, + AppModuleBasic: AppModuleBasic{}, + AppModuleSimulation: AppModuleSimulation{}, + keeper: keeper, + supplyKeeper: supplyKeeper, } } -// Name - module name +// Name returns the gov module's name. func (AppModule) Name() string { return ModuleName } @@ -118,27 +132,28 @@ func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { RegisterInvariants(ir, am.keeper) } -// Route - module message route name +// Route returns the message routing key for the gov module. func (AppModule) Route() string { return RouterKey } -// NewHandler - module handler +// NewHandler returns an sdk.Handler for the gov module. func (am AppModule) NewHandler() sdk.Handler { return NewHandler(am.keeper) } -// QuerierRoute - module querier route name +// QuerierRoute returns the gov module's querier route name. func (AppModule) QuerierRoute() string { return QuerierRoute } -// NewQuerierHandler - module querier +// NewQuerierHandler returns no sdk.Querier. func (am AppModule) NewQuerierHandler() sdk.Querier { return NewQuerier(am.keeper) } -// InitGenesis - module init-genesis +// InitGenesis performs genesis initialization for the gov module. It returns +// no validator updates. func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { var genesisState GenesisState ModuleCdc.MustUnmarshalJSON(data, &genesisState) @@ -146,16 +161,18 @@ func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.Va return []abci.ValidatorUpdate{} } -// ExportGenesis - module export genesis +// ExportGenesis returns the exported genesis state as raw bytes for the gov +// module. func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { gs := ExportGenesis(ctx, am.keeper) return ModuleCdc.MustMarshalJSON(gs) } -// BeginBlock - module begin-block +// BeginBlock performs a no-op. func (AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} -// EndBlock - module end-block +// EndBlock returns the end blocker for the gov module. It returns no validator +// updates. func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { EndBlocker(ctx, am.keeper) return []abci.ValidatorUpdate{} diff --git a/x/gov/simulation/decoder.go b/x/gov/simulation/decoder.go new file mode 100644 index 000000000000..475721e8769e --- /dev/null +++ b/x/gov/simulation/decoder.go @@ -0,0 +1,45 @@ +package simulation + +import ( + "bytes" + "encoding/binary" + "fmt" + + cmn "github.com/tendermint/tendermint/libs/common" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/x/gov/types" +) + +// DecodeStore unmarshals the KVPair's Value to the corresponding gov type +func DecodeStore(cdc *codec.Codec, kvA, kvB cmn.KVPair) string { + switch { + case bytes.Equal(kvA.Key[:1], types.ProposalsKeyPrefix): + var proposalA, proposalB types.Proposal + cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &proposalA) + cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &proposalB) + return fmt.Sprintf("%v\n%v", proposalA, proposalB) + + case bytes.Equal(kvA.Key[:1], types.ActiveProposalQueuePrefix), + bytes.Equal(kvA.Key[:1], types.InactiveProposalQueuePrefix), + bytes.Equal(kvA.Key[:1], types.ProposalIDKey): + proposalIDA := binary.LittleEndian.Uint64(kvA.Value) + proposalIDB := binary.LittleEndian.Uint64(kvB.Value) + return fmt.Sprintf("proposalIDA: %d\nProposalIDB: %d", proposalIDA, proposalIDB) + + case bytes.Equal(kvA.Key[:1], types.DepositsKeyPrefix): + var depositA, depositB types.Deposit + cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &depositA) + cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &depositB) + return fmt.Sprintf("%v\n%v", depositA, depositB) + + case bytes.Equal(kvA.Key[:1], types.VotesKeyPrefix): + var voteA, voteB types.Vote + cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &voteA) + cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &voteB) + return fmt.Sprintf("%v\n%v", voteA, voteB) + + default: + panic(fmt.Sprintf("invalid governance key prefix %X", kvA.Key[:1])) + } +} diff --git a/x/gov/simulation/decoder_test.go b/x/gov/simulation/decoder_test.go new file mode 100644 index 000000000000..05d980023c2e --- /dev/null +++ b/x/gov/simulation/decoder_test.go @@ -0,0 +1,75 @@ +package simulation + +import ( + "encoding/binary" + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/tendermint/tendermint/crypto/ed25519" + cmn "github.com/tendermint/tendermint/libs/common" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/gov/types" +) + +var ( + delPk1 = ed25519.GenPrivKey().PubKey() + delAddr1 = sdk.AccAddress(delPk1.Address()) + valAddr1 = sdk.ValAddress(delPk1.Address()) + consAddr1 = sdk.ConsAddress(delPk1.Address().Bytes()) +) + +func makeTestCodec() (cdc *codec.Codec) { + cdc = codec.New() + sdk.RegisterCodec(cdc) + codec.RegisterCrypto(cdc) + types.RegisterCodec(cdc) + return +} + +func TestDecodeStore(t *testing.T) { + cdc := makeTestCodec() + + endTime := time.Now().UTC() + + content := types.ContentFromProposalType("test", "test", types.ProposalTypeText) + proposal := types.NewProposal(content, 1, endTime, endTime.Add(24*time.Hour)) + proposalIDBz := make([]byte, 8) + binary.LittleEndian.PutUint64(proposalIDBz, 1) + deposit := types.NewDeposit(1, delAddr1, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.OneInt()))) + vote := types.NewVote(1, delAddr1, types.OptionYes) + + kvPairs := cmn.KVPairs{ + cmn.KVPair{Key: types.ProposalKey(1), Value: cdc.MustMarshalBinaryLengthPrefixed(proposal)}, + cmn.KVPair{Key: types.InactiveProposalQueueKey(1, endTime), Value: proposalIDBz}, + cmn.KVPair{Key: types.DepositKey(1, delAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(deposit)}, + cmn.KVPair{Key: types.VoteKey(1, delAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(vote)}, + cmn.KVPair{Key: []byte{0x99}, Value: []byte{0x99}}, + } + + tests := []struct { + name string + expectedLog string + }{ + {"proposals", fmt.Sprintf("%v\n%v", proposal, proposal)}, + {"proposal IDs", "proposalIDA: 1\nProposalIDB: 1"}, + {"deposits", fmt.Sprintf("%v\n%v", deposit, deposit)}, + {"votes", fmt.Sprintf("%v\n%v", vote, vote)}, + {"other", ""}, + } + + for i, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + switch i { + case len(tests) - 1: + require.Panics(t, func() { DecodeStore(cdc, cdc, kvPairs[i], kvPairs[i]) }, tt.name) + default: + require.Equal(t, tt.expectedLog, DecodeStore(cdc, cdc, kvPairs[i], kvPairs[i]), tt.name) + } + }) + } +} diff --git a/x/gov/simulation/msgs.go b/x/gov/simulation/operations/msgs.go similarity index 99% rename from x/gov/simulation/msgs.go rename to x/gov/simulation/operations/msgs.go index 0477e8273b88..f7dcba519de0 100644 --- a/x/gov/simulation/msgs.go +++ b/x/gov/simulation/operations/msgs.go @@ -1,4 +1,4 @@ -package simulation +package operations import ( "fmt" diff --git a/x/mint/module.go b/x/mint/module.go index e8e07d94efd8..33b93ab54e81 100644 --- a/x/mint/module.go +++ b/x/mint/module.go @@ -14,32 +14,35 @@ import ( "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/x/mint/client/cli" "github.com/cosmos/cosmos-sdk/x/mint/client/rest" + "github.com/cosmos/cosmos-sdk/x/mint/simulation" ) var ( - _ module.AppModule = AppModule{} - _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModuleSimulation = AppModuleSimulation{} ) -// app module basics object +// AppModuleBasic defines the basic application module used by the mint module. type AppModuleBasic struct{} var _ module.AppModuleBasic = AppModuleBasic{} -// module name +// Name returns the mint module's name. func (AppModuleBasic) Name() string { return ModuleName } -// register module codec +// RegisterCodec registers the mint module's types for the given codec. func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) {} -// default genesis state +// DefaultGenesis returns default genesis state as raw bytes for the mint +// module. func (AppModuleBasic) DefaultGenesis() json.RawMessage { return ModuleCdc.MustMarshalJSON(DefaultGenesisState()) } -// module validate genesis +// ValidateGenesis performs genesis state validation for the mint module. func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { var data GenesisState err := ModuleCdc.UnmarshalJSON(bz, &data) @@ -49,59 +52,74 @@ func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { return ValidateGenesis(data) } -// register rest routes +// RegisterRESTRoutes registers the REST routes for the mint module. func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { rest.RegisterRoutes(ctx, rtr) } -// get the root tx command of this module +// GetTxCmd returns no root tx command for the mint module. func (AppModuleBasic) GetTxCmd(_ *codec.Codec) *cobra.Command { return nil } -// get the root query command of this module +// GetQueryCmd returns the root query command for the mint module. func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { return cli.GetQueryCmd(cdc) } -//___________________________ -// app module +//____________________________________________________________________________ + +// AppModuleSimulation defines the module simulation functions used by the mint module. +type AppModuleSimulation struct{} + +// RegisterStoreDecoder registers a decoder for mint module's types. +func (AppModuleSimulation) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { + sdr[StoreKey] = simulation.DecodeStore +} + +//____________________________________________________________________________ + +// AppModule implements an application module for the mint module. type AppModule struct { AppModuleBasic + AppModuleSimulation + keeper Keeper } // NewAppModule creates a new AppModule object func NewAppModule(keeper Keeper) AppModule { return AppModule{ - AppModuleBasic: AppModuleBasic{}, - keeper: keeper, + AppModuleBasic: AppModuleBasic{}, + AppModuleSimulation: AppModuleSimulation{}, + keeper: keeper, } } -// module name +// Name returns the mint module's name. func (AppModule) Name() string { return ModuleName } -// register invariants +// RegisterInvariants registers the mint module invariants. func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} -// module message route name +// Route returns the message routing key for the mint module. func (AppModule) Route() string { return "" } -// module handler +// NewHandler returns an sdk.Handler for the mint module. func (am AppModule) NewHandler() sdk.Handler { return nil } -// module querier route name +// QuerierRoute returns the mint module's querier route name. func (AppModule) QuerierRoute() string { return QuerierRoute } -// module querier +// NewQuerierHandler returns the mint module sdk.Querier. func (am AppModule) NewQuerierHandler() sdk.Querier { return NewQuerier(am.keeper) } -// module init-genesis +// InitGenesis performs genesis initialization for the mint module. It returns +// no validator updates. func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { var genesisState GenesisState ModuleCdc.MustUnmarshalJSON(data, &genesisState) @@ -109,18 +127,20 @@ func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.Va return []abci.ValidatorUpdate{} } -// module export genesis +// ExportGenesis returns the exported genesis state as raw bytes for the mint +// module. func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { gs := ExportGenesis(ctx, am.keeper) return ModuleCdc.MustMarshalJSON(gs) } -// module begin-block +// BeginBlock returns the begin blocker for the mint module. func (am AppModule) BeginBlock(ctx sdk.Context, _ abci.RequestBeginBlock) { BeginBlocker(ctx, am.keeper) } -// module end-block +// EndBlock returns the end blocker for the mint module. It returns no validator +// updates. func (AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { return []abci.ValidatorUpdate{} } diff --git a/x/mint/simulation/decoder.go b/x/mint/simulation/decoder.go new file mode 100644 index 000000000000..0f1ab1960989 --- /dev/null +++ b/x/mint/simulation/decoder.go @@ -0,0 +1,24 @@ +package simulation + +import ( + "bytes" + "fmt" + + cmn "github.com/tendermint/tendermint/libs/common" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/x/mint/internal/types" +) + +// DecodeStore unmarshals the KVPair's Value to the corresponding mint type +func DecodeStore(cdc *codec.Codec, kvA, kvB cmn.KVPair) string { + switch { + case bytes.Equal(kvA.Key, types.MinterKey): + var minterA, minterB types.Minter + cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &minterA) + cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &minterB) + return fmt.Sprintf("%v\n%v", minterA, minterB) + default: + panic(fmt.Sprintf("invalid mint key %X", kvA.Key)) + } +} diff --git a/x/mint/simulation/decoder_test.go b/x/mint/simulation/decoder_test.go new file mode 100644 index 000000000000..b6501baa7f06 --- /dev/null +++ b/x/mint/simulation/decoder_test.go @@ -0,0 +1,48 @@ +package simulation + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + cmn "github.com/tendermint/tendermint/libs/common" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/mint/internal/types" +) + +func makeTestCodec() (cdc *codec.Codec) { + cdc = codec.New() + sdk.RegisterCodec(cdc) + return +} + +func TestDecodeStore(t *testing.T) { + cdc := makeTestCodec() + minter := types.NewMinter(sdk.OneDec(), sdk.NewDec(15)) + + kvPairs := cmn.KVPairs{ + cmn.KVPair{Key: types.MinterKey, Value: cdc.MustMarshalBinaryLengthPrefixed(minter)}, + cmn.KVPair{Key: []byte{0x99}, Value: []byte{0x99}}, + } + tests := []struct { + name string + expectedLog string + }{ + {"Minter", fmt.Sprintf("%v\n%v", minter, minter)}, + {"other", ""}, + } + + for i, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + switch i { + case len(tests) - 1: + require.Panics(t, func() { DecodeStore(cdc, kvPairs[i], kvPairs[i]) }, tt.name) + default: + require.Equal(t, tt.expectedLog, DecodeStore(cdc, kvPairs[i], kvPairs[i]), tt.name) + } + }) + } +} diff --git a/x/params/module.go b/x/params/module.go index e339d954990c..38b93bd5f00c 100644 --- a/x/params/module.go +++ b/x/params/module.go @@ -16,32 +16,31 @@ var ( _ module.AppModuleBasic = AppModuleBasic{} ) -const moduleName = "params" - -// app module basics object +// AppModuleBasic defines the basic application module used by the params module. type AppModuleBasic struct{} -// module name +// Name returns the params module's name. func (AppModuleBasic) Name() string { - return moduleName + return ModuleName } -// register module codec +// RegisterCodec registers the params module's types for the given codec. func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { types.RegisterCodec(cdc) } -// default genesis state +// DefaultGenesis returns default genesis state as raw bytes for the params +// module. func (AppModuleBasic) DefaultGenesis() json.RawMessage { return nil } -// module validate genesis +// ValidateGenesis performs genesis state validation for the params module. func (AppModuleBasic) ValidateGenesis(_ json.RawMessage) error { return nil } -// register rest routes +// RegisterRESTRoutes registers the REST routes for the params module. func (AppModuleBasic) RegisterRESTRoutes(_ context.CLIContext, _ *mux.Router) {} -// get the root tx command of this module +// GetTxCmd returns no root tx command for the params module. func (AppModuleBasic) GetTxCmd(_ *codec.Codec) *cobra.Command { return nil } -// get the root query command of this module +// GetQueryCmd returns no root query command for the params module. func (AppModuleBasic) GetQueryCmd(_ *codec.Codec) *cobra.Command { return nil } diff --git a/x/params/simulation/msgs.go b/x/params/simulation/operations/msgs.go similarity index 99% rename from x/params/simulation/msgs.go rename to x/params/simulation/operations/msgs.go index db7669ae09ed..b6b869dc6f23 100644 --- a/x/params/simulation/msgs.go +++ b/x/params/simulation/operations/msgs.go @@ -1,4 +1,4 @@ -package simulation +package operations import ( "encoding/json" diff --git a/x/slashing/module.go b/x/slashing/module.go index e3ee17fc2c7e..986915a11531 100644 --- a/x/slashing/module.go +++ b/x/slashing/module.go @@ -15,34 +15,37 @@ import ( "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/internal/types" + "github.com/cosmos/cosmos-sdk/x/slashing/simulation" ) var ( - _ module.AppModule = AppModule{} - _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModuleSimulation = AppModuleSimulation{} ) -// app module basics object +// AppModuleBasic defines the basic application module used by the slashing module. type AppModuleBasic struct{} var _ module.AppModuleBasic = AppModuleBasic{} -// module name +// Name returns the slashing module's name. func (AppModuleBasic) Name() string { return types.ModuleName } -// register module codec +// RegisterCodec registers the slashing module's types for the given codec. func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { RegisterCodec(cdc) } -// default genesis state +// DefaultGenesis returns default genesis state as raw bytes for the slashing +// module. func (AppModuleBasic) DefaultGenesis() json.RawMessage { return ModuleCdc.MustMarshalJSON(DefaultGenesisState()) } -// module validate genesis +// ValidateGenesis performs genesis state validation for the slashing module. func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { var data GenesisState err := ModuleCdc.UnmarshalJSON(bz, &data) @@ -52,25 +55,38 @@ func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { return ValidateGenesis(data) } -// register rest routes +// RegisterRESTRoutes registers the REST routes for the slashing module. func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { rest.RegisterRoutes(ctx, rtr) } -// get the root tx command of this module +// GetTxCmd returns the root tx command for the slashing module. func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { return cli.GetTxCmd(cdc) } -// get the root query command of this module +// GetQueryCmd returns no root query command for the slashing module. func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { return cli.GetQueryCmd(StoreKey, cdc) } -//___________________________ -// app module +//____________________________________________________________________________ + +// AppModuleSimulation defines the module simulation functions used by the slashing module. +type AppModuleSimulation struct{} + +// RegisterStoreDecoder registers a decoder for slashing module's types +func (AppModuleSimulation) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { + sdr[StoreKey] = simulation.DecodeStore +} + +//____________________________________________________________________________ + +// AppModule implements an application module for the slashing module. type AppModule struct { AppModuleBasic + AppModuleSimulation + keeper Keeper stakingKeeper types.StakingKeeper } @@ -78,41 +94,43 @@ type AppModule struct { // NewAppModule creates a new AppModule object func NewAppModule(keeper Keeper, stakingKeeper types.StakingKeeper) AppModule { return AppModule{ - AppModuleBasic: AppModuleBasic{}, - keeper: keeper, - stakingKeeper: stakingKeeper, + AppModuleBasic: AppModuleBasic{}, + AppModuleSimulation: AppModuleSimulation{}, + keeper: keeper, + stakingKeeper: stakingKeeper, } } -// module name +// Name returns the slashing module's name. func (AppModule) Name() string { return ModuleName } -// register invariants +// RegisterInvariants registers the slashing module invariants. func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} -// module message route name +// Route returns the message routing key for the slashing module. func (AppModule) Route() string { return RouterKey } -// module handler +// NewHandler returns an sdk.Handler for the slashing module. func (am AppModule) NewHandler() sdk.Handler { return NewHandler(am.keeper) } -// module querier route name +// QuerierRoute returns the slashing module's querier route name. func (AppModule) QuerierRoute() string { return QuerierRoute } -// module querier +// NewQuerierHandler returns the slashing module sdk.Querier. func (am AppModule) NewQuerierHandler() sdk.Querier { return NewQuerier(am.keeper) } -// module init-genesis +// InitGenesis performs genesis initialization for the slashing module. It returns +// no validator updates. func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { var genesisState GenesisState ModuleCdc.MustUnmarshalJSON(data, &genesisState) @@ -120,18 +138,20 @@ func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.Va return []abci.ValidatorUpdate{} } -// module export genesis +// ExportGenesis returns the exported genesis state as raw bytes for the slashing +// module. func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { gs := ExportGenesis(ctx, am.keeper) return ModuleCdc.MustMarshalJSON(gs) } -// module begin-block +// BeginBlock returns the begin blocker for the slashing module. func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { BeginBlocker(ctx, req, am.keeper) } -// module end-block +// 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{} } diff --git a/x/slashing/simulation/decoder.go b/x/slashing/simulation/decoder.go new file mode 100644 index 000000000000..99af7c0b722b --- /dev/null +++ b/x/slashing/simulation/decoder.go @@ -0,0 +1,41 @@ +package simulation + +import ( + "bytes" + "fmt" + + "github.com/tendermint/tendermint/crypto" + cmn "github.com/tendermint/tendermint/libs/common" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/slashing/internal/types" +) + +// DecodeStore unmarshals the KVPair's Value to the corresponding slashing type +func DecodeStore(cdc *codec.Codec, kvA, kvB cmn.KVPair) string { + switch { + case bytes.Equal(kvA.Key[:1], types.ValidatorSigningInfoKey): + var infoA, infoB types.ValidatorSigningInfo + cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &infoA) + cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &infoB) + return fmt.Sprintf("%v\n%v", infoA, infoB) + + case bytes.Equal(kvA.Key[:1], types.ValidatorMissedBlockBitArrayKey): + var missedA, missedB bool + cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &missedA) + cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &missedB) + return fmt.Sprintf("missedA: %v\nmissedB: %v", missedA, missedB) + + case bytes.Equal(kvA.Key[:1], types.AddrPubkeyRelationKey): + var pubKeyA, pubKeyB crypto.PubKey + cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &pubKeyA) + cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &pubKeyB) + bechPKA := sdk.MustBech32ifyAccPub(pubKeyA) + bechPKB := sdk.MustBech32ifyAccPub(pubKeyB) + return fmt.Sprintf("PubKeyA: %s\nPubKeyB: %s", bechPKA, bechPKB) + + default: + panic(fmt.Sprintf("invalid slashing key prefix %X", kvA.Key[:1])) + } +} diff --git a/x/slashing/simulation/decoder_test.go b/x/slashing/simulation/decoder_test.go new file mode 100644 index 000000000000..1c8f8f93c542 --- /dev/null +++ b/x/slashing/simulation/decoder_test.go @@ -0,0 +1,66 @@ +package simulation + +import ( + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/tendermint/tendermint/crypto/ed25519" + cmn "github.com/tendermint/tendermint/libs/common" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/slashing/internal/types" +) + +var ( + delPk1 = ed25519.GenPrivKey().PubKey() + delAddr1 = sdk.AccAddress(delPk1.Address()) + valAddr1 = sdk.ValAddress(delPk1.Address()) + consAddr1 = sdk.ConsAddress(delPk1.Address().Bytes()) +) + +func makeTestCodec() (cdc *codec.Codec) { + cdc = codec.New() + sdk.RegisterCodec(cdc) + codec.RegisterCrypto(cdc) + types.RegisterCodec(cdc) + return +} + +func TestDecodeStore(t *testing.T) { + cdc := makeTestCodec() + + info := types.NewValidatorSigningInfo(consAddr1, 0, 1, time.Now().UTC(), false, 0) + bechPK := sdk.MustBech32ifyAccPub(delPk1) + missed := true + + kvPairs := cmn.KVPairs{ + cmn.KVPair{Key: types.GetValidatorSigningInfoKey(consAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(info)}, + cmn.KVPair{Key: types.GetValidatorMissedBlockBitArrayKey(consAddr1, 6), Value: cdc.MustMarshalBinaryLengthPrefixed(missed)}, + cmn.KVPair{Key: types.GetAddrPubkeyRelationKey(delAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(delPk1)}, + cmn.KVPair{Key: []byte{0x99}, Value: []byte{0x99}}, + } + + tests := []struct { + name string + expectedLog string + }{ + {"ValidatorSigningInfo", fmt.Sprintf("%v\n%v", info, info)}, + {"ValidatorMissedBlockBitArray", fmt.Sprintf("missedA: %v\nmissedB: %v", missed, missed)}, + {"AddrPubkeyRelation", fmt.Sprintf("PubKeyA: %s\nPubKeyB: %s", bechPK, bechPK)}, + {"other", ""}, + } + for i, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + switch i { + case len(tests) - 1: + require.Panics(t, func() { DecodeStore(cdc, kvPairs[i], kvPairs[i]) }, tt.name) + default: + require.Equal(t, tt.expectedLog, DecodeStore(cdc, kvPairs[i], kvPairs[i]), tt.name) + } + }) + } +} diff --git a/x/slashing/simulation/msgs.go b/x/slashing/simulation/operations/msgs.go similarity index 98% rename from x/slashing/simulation/msgs.go rename to x/slashing/simulation/operations/msgs.go index 01acf4a81980..5772101b16b2 100644 --- a/x/slashing/simulation/msgs.go +++ b/x/slashing/simulation/operations/msgs.go @@ -1,4 +1,4 @@ -package simulation +package operations import ( "fmt" diff --git a/x/staking/module.go b/x/staking/module.go index 8dcb26c66ec6..4bfd0c70aab5 100644 --- a/x/staking/module.go +++ b/x/staking/module.go @@ -18,35 +18,38 @@ import ( authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/staking/client/cli" "github.com/cosmos/cosmos-sdk/x/staking/client/rest" + "github.com/cosmos/cosmos-sdk/x/staking/simulation" "github.com/cosmos/cosmos-sdk/x/staking/types" ) var ( - _ module.AppModule = AppModule{} - _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModuleSimulation = AppModuleSimulation{} ) -// app module basics object +// AppModuleBasic defines the basic application module used by the staking module. type AppModuleBasic struct{} var _ module.AppModuleBasic = AppModuleBasic{} -// module name +// Name returns the staking module's name. func (AppModuleBasic) Name() string { return ModuleName } -// register module codec +// RegisterCodec registers the staking module's types for the given codec. func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { RegisterCodec(cdc) } -// default genesis state +// DefaultGenesis returns default genesis state as raw bytes for the staking +// module. func (AppModuleBasic) DefaultGenesis() json.RawMessage { return ModuleCdc.MustMarshalJSON(DefaultGenesisState()) } -// module validate genesis +// ValidateGenesis performs genesis state validation for the staking module. func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { var data GenesisState err := ModuleCdc.UnmarshalJSON(bz, &data) @@ -56,17 +59,17 @@ func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { return ValidateGenesis(data) } -// register rest routes +// RegisterRESTRoutes registers the REST routes for the staking module. func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { rest.RegisterRoutes(ctx, rtr) } -// get the root tx command of this module +// GetTxCmd returns the root tx command for the staking module. func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { return cli.GetTxCmd(StoreKey, cdc) } -// get the root query command of this module +// GetQueryCmd returns no root query command for the staking module. func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { return cli.GetQueryCmd(StoreKey, cdc) } @@ -92,9 +95,23 @@ func (AppModuleBasic) BuildCreateValidatorMsg(cliCtx context.CLIContext, return cli.BuildCreateValidatorMsg(cliCtx, txBldr) } -// app module +//____________________________________________________________________________ + +// AppModuleSimulation defines the module simulation functions used by the staking module. +type AppModuleSimulation struct{} + +// RegisterStoreDecoder registers a decoder for staking module's types +func (AppModuleSimulation) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { + sdr[StoreKey] = simulation.DecodeStore +} + +//____________________________________________________________________________ + +// AppModule implements an application module for the staking module. type AppModule struct { AppModuleBasic + AppModuleSimulation + keeper Keeper distrKeeper types.DistributionKeeper accKeeper types.AccountKeeper @@ -106,61 +123,65 @@ func NewAppModule(keeper Keeper, distrKeeper types.DistributionKeeper, accKeeper supplyKeeper types.SupplyKeeper) AppModule { return AppModule{ - AppModuleBasic: AppModuleBasic{}, - keeper: keeper, - distrKeeper: distrKeeper, - accKeeper: accKeeper, - supplyKeeper: supplyKeeper, + AppModuleBasic: AppModuleBasic{}, + AppModuleSimulation: AppModuleSimulation{}, + keeper: keeper, + distrKeeper: distrKeeper, + accKeeper: accKeeper, + supplyKeeper: supplyKeeper, } } -// module name +// Name returns the staking module's name. func (AppModule) Name() string { return ModuleName } -// register invariants +// RegisterInvariants registers the staking module invariants. func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { RegisterInvariants(ir, am.keeper) } -// module message route name +// Route returns the message routing key for the staking module. func (AppModule) Route() string { return RouterKey } -// module handler +// NewHandler returns an sdk.Handler for the staking module. func (am AppModule) NewHandler() sdk.Handler { return NewHandler(am.keeper) } -// module querier route name +// QuerierRoute returns the staking module's querier route name. func (AppModule) QuerierRoute() string { return QuerierRoute } -// module querier +// NewQuerierHandler returns the staking module sdk.Querier. func (am AppModule) NewQuerierHandler() sdk.Querier { return NewQuerier(am.keeper) } -// module init-genesis +// InitGenesis performs genesis initialization for the staking module. It returns +// no validator updates. func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { var genesisState GenesisState ModuleCdc.MustUnmarshalJSON(data, &genesisState) return InitGenesis(ctx, am.keeper, am.accKeeper, am.supplyKeeper, genesisState) } -// module export genesis +// ExportGenesis returns the exported genesis state as raw bytes for the staking +// module. func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { gs := ExportGenesis(ctx, am.keeper) return ModuleCdc.MustMarshalJSON(gs) } -// module begin-block +// BeginBlock returns the begin blocker for the staking module. func (AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} -// module end-block +// EndBlock returns the end blocker for the staking module. It returns no validator +// updates. func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { return EndBlocker(ctx, am.keeper) } diff --git a/x/staking/simulation/decoder.go b/x/staking/simulation/decoder.go new file mode 100644 index 000000000000..45aba449f807 --- /dev/null +++ b/x/staking/simulation/decoder.go @@ -0,0 +1,57 @@ +package simulation + +import ( + "bytes" + "fmt" + + cmn "github.com/tendermint/tendermint/libs/common" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// DecodeStore unmarshals the KVPair's Value to the corresponding staking type +func DecodeStore(cdc *codec.Codec, kvA, kvB cmn.KVPair) string { + switch { + case bytes.Equal(kvA.Key[:1], types.LastTotalPowerKey): + var powerA, powerB sdk.Int + cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &powerA) + cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &powerB) + return fmt.Sprintf("%v\n%v", powerA, powerB) + + case bytes.Equal(kvA.Key[:1], types.ValidatorsKey): + var validatorA, validatorB types.Validator + cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &validatorA) + cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &validatorB) + return fmt.Sprintf("%v\n%v", validatorA, validatorB) + + case bytes.Equal(kvA.Key[:1], types.LastValidatorPowerKey), + bytes.Equal(kvA.Key[:1], types.ValidatorsByConsAddrKey), + bytes.Equal(kvA.Key[:1], types.ValidatorsByPowerIndexKey): + return fmt.Sprintf("%v\n%v", sdk.ValAddress(kvA.Value), sdk.ValAddress(kvB.Value)) + + case bytes.Equal(kvA.Key[:1], types.DelegationKey): + var delegationA, delegationB types.Delegation + cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &delegationA) + cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &delegationB) + return fmt.Sprintf("%v\n%v", delegationA, delegationB) + + case bytes.Equal(kvA.Key[:1], types.UnbondingDelegationKey), + bytes.Equal(kvA.Key[:1], types.UnbondingDelegationByValIndexKey): + var ubdA, ubdB types.UnbondingDelegation + cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &ubdA) + cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &ubdB) + return fmt.Sprintf("%v\n%v", ubdA, ubdB) + + case bytes.Equal(kvA.Key[:1], types.RedelegationKey), + bytes.Equal(kvA.Key[:1], types.RedelegationByValSrcIndexKey): + var redA, redB types.Redelegation + cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &redA) + cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &redB) + return fmt.Sprintf("%v\n%v", redA, redB) + + default: + panic(fmt.Sprintf("invalid staking key prefix %X", kvA.Key[:1])) + } +} diff --git a/x/staking/simulation/decoder_test.go b/x/staking/simulation/decoder_test.go new file mode 100644 index 000000000000..19950c97aad4 --- /dev/null +++ b/x/staking/simulation/decoder_test.go @@ -0,0 +1,75 @@ +package simulation + +import ( + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/tendermint/tendermint/crypto/ed25519" + cmn "github.com/tendermint/tendermint/libs/common" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +var ( + delPk1 = ed25519.GenPrivKey().PubKey() + delAddr1 = sdk.AccAddress(delPk1.Address()) + valAddr1 = sdk.ValAddress(delPk1.Address()) + consAddr1 = sdk.ConsAddress(delPk1.Address().Bytes()) +) + +func makeTestCodec() (cdc *codec.Codec) { + cdc = codec.New() + sdk.RegisterCodec(cdc) + codec.RegisterCrypto(cdc) + types.RegisterCodec(cdc) + return +} + +func TestDecodeStore(t *testing.T) { + cdc := makeTestCodec() + + bondTime := time.Now().UTC() + + val := types.NewValidator(valAddr1, delPk1, types.NewDescription("test", "test", "test", "test", "test")) + del := types.NewDelegation(delAddr1, valAddr1, sdk.OneDec()) + ubd := types.NewUnbondingDelegation(delAddr1, valAddr1, 15, bondTime, sdk.OneInt()) + red := types.NewRedelegation(delAddr1, valAddr1, valAddr1, 12, bondTime, sdk.OneInt(), sdk.OneDec()) + + kvPairs := cmn.KVPairs{ + cmn.KVPair{Key: types.LastTotalPowerKey, Value: cdc.MustMarshalBinaryLengthPrefixed(sdk.OneInt())}, + cmn.KVPair{Key: types.GetValidatorKey(valAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(val)}, + cmn.KVPair{Key: types.LastValidatorPowerKey, Value: valAddr1.Bytes()}, + cmn.KVPair{Key: types.GetDelegationKey(delAddr1, valAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(del)}, + cmn.KVPair{Key: types.GetUBDKey(delAddr1, valAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(ubd)}, + cmn.KVPair{Key: types.GetREDKey(delAddr1, valAddr1, valAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(red)}, + cmn.KVPair{Key: []byte{0x99}, Value: []byte{0x99}}, + } + + tests := []struct { + name string + expectedLog string + }{ + {"LastTotalPower", fmt.Sprintf("%v\n%v", sdk.OneInt(), sdk.OneInt())}, + {"Validator", fmt.Sprintf("%v\n%v", val, val)}, + {"LastValidatorPower/ValidatorsByConsAddr/ValidatorsByPowerIndex", fmt.Sprintf("%v\n%v", valAddr1, valAddr1)}, + {"Delegation", fmt.Sprintf("%v\n%v", del, del)}, + {"UnbondingDelegation", fmt.Sprintf("%v\n%v", ubd, ubd)}, + {"Redelegation", fmt.Sprintf("%v\n%v", red, red)}, + {"other", ""}, + } + for i, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + switch i { + case len(tests) - 1: + require.Panics(t, func() { DecodeStore(cdc, kvPairs[i], kvPairs[i]) }, tt.name) + default: + require.Equal(t, tt.expectedLog, DecodeStore(cdc, kvPairs[i], kvPairs[i]), tt.name) + } + }) + } +} diff --git a/x/staking/simulation/msgs.go b/x/staking/simulation/operations/msgs.go similarity index 99% rename from x/staking/simulation/msgs.go rename to x/staking/simulation/operations/msgs.go index f36a5e6d9802..caf6dd09c0bc 100644 --- a/x/staking/simulation/msgs.go +++ b/x/staking/simulation/operations/msgs.go @@ -1,4 +1,4 @@ -package simulation +package operations import ( "fmt" diff --git a/x/supply/module.go b/x/supply/module.go index 3a987e2fa14d..5607b4d3207d 100644 --- a/x/supply/module.go +++ b/x/supply/module.go @@ -15,32 +15,35 @@ import ( "github.com/cosmos/cosmos-sdk/x/supply/client/cli" "github.com/cosmos/cosmos-sdk/x/supply/client/rest" "github.com/cosmos/cosmos-sdk/x/supply/internal/types" + "github.com/cosmos/cosmos-sdk/x/supply/simulation" ) var ( - _ module.AppModule = AppModule{} - _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModuleSimulation = AppModuleSimulation{} ) -// app module basics object +// AppModuleBasic defines the basic application module used by the supply module. type AppModuleBasic struct{} -// module name +// Name returns the supply module's name. func (AppModuleBasic) Name() string { return ModuleName } -// register module codec +// RegisterCodec registers the supply module's types for the given codec. func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { RegisterCodec(cdc) } -// default genesis state +// DefaultGenesis returns default genesis state as raw bytes for the supply +// module. func (AppModuleBasic) DefaultGenesis() json.RawMessage { return ModuleCdc.MustMarshalJSON(DefaultGenesisState()) } -// module validate genesis +// ValidateGenesis performs genesis state validation for the supply module. func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { var data GenesisState err := ModuleCdc.UnmarshalJSON(bz, &data) @@ -50,22 +53,36 @@ func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { return ValidateGenesis(data) } -// register rest routes +// RegisterRESTRoutes registers the REST routes for the supply module. func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { rest.RegisterRoutes(ctx, rtr) } -// get the root tx command of this module +// GetTxCmd returns the root tx command for the supply module. func (AppModuleBasic) GetTxCmd(_ *codec.Codec) *cobra.Command { return nil } -// get the root query command of this module +// GetQueryCmd returns no root query command for the supply module. func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { return cli.GetQueryCmd(cdc) } -// app module +//____________________________________________________________________________ + +// AppModuleSimulation defines the module simulation functions used by the supply module. +type AppModuleSimulation struct{} + +// RegisterStoreDecoder registers a decoder for supply module's types +func (AppModuleSimulation) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { + sdr[StoreKey] = simulation.DecodeStore +} + +//____________________________________________________________________________ + +// AppModule implements an application module for the supply module. type AppModule struct { AppModuleBasic + AppModuleSimulation + keeper Keeper ak types.AccountKeeper } @@ -73,41 +90,43 @@ type AppModule struct { // NewAppModule creates a new AppModule object func NewAppModule(keeper Keeper, ak types.AccountKeeper) AppModule { return AppModule{ - AppModuleBasic: AppModuleBasic{}, - keeper: keeper, - ak: ak, + AppModuleBasic: AppModuleBasic{}, + AppModuleSimulation: AppModuleSimulation{}, + keeper: keeper, + ak: ak, } } -// module name +// Name returns the supply module's name. func (AppModule) Name() string { return ModuleName } -// register invariants +// RegisterInvariants registers the supply module invariants. func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { RegisterInvariants(ir, am.keeper) } -// module message route name +// Route returns the message routing key for the supply module. func (AppModule) Route() string { return RouterKey } -// module handler +// NewHandler returns an sdk.Handler for the supply module. func (am AppModule) NewHandler() sdk.Handler { return nil } -// module querier route name +// QuerierRoute returns the supply module's querier route name. func (AppModule) QuerierRoute() string { return QuerierRoute } -// module querier +// NewQuerierHandler returns the supply module sdk.Querier. func (am AppModule) NewQuerierHandler() sdk.Querier { return NewQuerier(am.keeper) } -// module init-genesis +// InitGenesis performs genesis initialization for the supply module. It returns +// no validator updates. func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { var genesisState GenesisState ModuleCdc.MustUnmarshalJSON(data, &genesisState) @@ -115,16 +134,18 @@ func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.Va return []abci.ValidatorUpdate{} } -// module export genesis +// ExportGenesis returns the exported genesis state as raw bytes for the supply +// module. func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { gs := ExportGenesis(ctx, am.keeper) return ModuleCdc.MustMarshalJSON(gs) } -// module begin-block +// BeginBlock returns the begin blocker for the supply module. func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} -// module end-block +// EndBlock returns the end blocker for the supply module. It returns no validator +// updates. func (AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { return []abci.ValidatorUpdate{} } diff --git a/x/supply/simulation/decoder.go b/x/supply/simulation/decoder.go new file mode 100644 index 000000000000..7e22f33a5478 --- /dev/null +++ b/x/supply/simulation/decoder.go @@ -0,0 +1,25 @@ +package simulation + +import ( + "bytes" + "fmt" + + cmn "github.com/tendermint/tendermint/libs/common" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/x/supply/internal/keeper" + "github.com/cosmos/cosmos-sdk/x/supply/internal/types" +) + +// DecodeStore unmarshals the KVPair's Value to the corresponding supply type +func DecodeStore(cdc *codec.Codec, kvA, kvB cmn.KVPair) string { + switch { + case bytes.Equal(kvA.Key[:1], keeper.SupplyKey): + var supplyA, supplyB types.Supply + cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &supplyA) + cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &supplyB) + return fmt.Sprintf("%v\n%v", supplyB, supplyB) + default: + panic(fmt.Sprintf("invalid supply key %X", kvA.Key)) + } +} diff --git a/x/supply/simulation/decoder_test.go b/x/supply/simulation/decoder_test.go new file mode 100644 index 000000000000..329b3e752747 --- /dev/null +++ b/x/supply/simulation/decoder_test.go @@ -0,0 +1,52 @@ +package simulation + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + cmn "github.com/tendermint/tendermint/libs/common" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/supply/internal/keeper" + "github.com/cosmos/cosmos-sdk/x/supply/internal/types" +) + +func makeTestCodec() (cdc *codec.Codec) { + cdc = codec.New() + sdk.RegisterCodec(cdc) + codec.RegisterCrypto(cdc) + types.RegisterCodec(cdc) + return +} +func TestDecodeStore(t *testing.T) { + cdc := makeTestCodec() + + totalSupply := types.NewSupply(sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 1000))) + + kvPairs := cmn.KVPairs{ + cmn.KVPair{Key: keeper.SupplyKey, Value: cdc.MustMarshalBinaryLengthPrefixed(totalSupply)}, + cmn.KVPair{Key: []byte{0x99}, Value: []byte{0x99}}, + } + + tests := []struct { + name string + expectedLog string + }{ + {"Supply", fmt.Sprintf("%v\n%v", totalSupply, totalSupply)}, + {"other", ""}, + } + + for i, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + switch i { + case len(tests) - 1: + require.Panics(t, func() { DecodeStore(cdc, kvPairs[i], kvPairs[i]) }, tt.name) + default: + require.Equal(t, tt.expectedLog, DecodeStore(cdc, kvPairs[i], kvPairs[i]), tt.name) + } + }) + } +}