Skip to content

Commit

Permalink
x/capability: simulations (cosmos#6062)
Browse files Browse the repository at this point in the history
* x/capability: simulations

* update logs

* validate genesis state

* InitGenesis and ExportGenesis functions

* update validation func

* fix import-export sim

* remove nondeterminism from capability genesis

* Update x/capability/types/genesis.go

* Update x/capability/types/genesis.go

* fix tests

* fix merge

* consistency updates

* try fix nondeterminism

* fix conditional

* Fix random index logic

* lint

* lint

Co-authored-by: Aditya Sripal <[email protected]>
Co-authored-by: Aleksandr Bezobchuk <[email protected]>
Co-authored-by: Alexander Bezobchuk <[email protected]>
  • Loading branch information
4 people authored Apr 30, 2020
1 parent 7143d09 commit 930802e
Show file tree
Hide file tree
Showing 13 changed files with 477 additions and 41 deletions.
3 changes: 2 additions & 1 deletion simapp/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ func NewSimApp(
genutil.NewAppModule(app.AccountKeeper, app.StakingKeeper, app.BaseApp.DeliverTx),
auth.NewAppModule(appCodec, app.AccountKeeper),
bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper),
capability.NewAppModule(*app.CapabilityKeeper),
capability.NewAppModule(appCodec, *app.CapabilityKeeper),
crisis.NewAppModule(&app.CrisisKeeper),
gov.NewAppModule(appCodec, app.GovKeeper, app.AccountKeeper, app.BankKeeper),
mint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper),
Expand Down Expand Up @@ -307,6 +307,7 @@ func NewSimApp(
app.sm = module.NewSimulationManager(
auth.NewAppModule(appCodec, app.AccountKeeper),
bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper),
capability.NewAppModule(appCodec, *app.CapabilityKeeper),
gov.NewAppModule(appCodec, app.GovKeeper, app.AccountKeeper, app.BankKeeper),
mint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper),
staking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper),
Expand Down
5 changes: 3 additions & 2 deletions simapp/sim_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/capability"
distr "github.com/cosmos/cosmos-sdk/x/distribution"
"github.com/cosmos/cosmos-sdk/x/evidence"
"github.com/cosmos/cosmos-sdk/x/gov"
Expand Down Expand Up @@ -158,6 +159,7 @@ func TestAppImportExport(t *testing.T) {
{app.keys[paramtypes.StoreKey], newApp.keys[paramtypes.StoreKey], [][]byte{}},
{app.keys[gov.StoreKey], newApp.keys[gov.StoreKey], [][]byte{}},
{app.keys[evidence.StoreKey], newApp.keys[evidence.StoreKey], [][]byte{}},
{app.keys[capability.StoreKey], newApp.keys[capability.StoreKey], [][]byte{}},
}

for _, skp := range storeKeysPrefixes {
Expand Down Expand Up @@ -268,7 +270,6 @@ func TestAppStateDeterminism(t *testing.T) {
}

db := dbm.NewMemDB()

app := NewSimApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, interBlockCacheOpt())

fmt.Printf(
Expand All @@ -292,7 +293,7 @@ func TestAppStateDeterminism(t *testing.T) {

if j != 0 {
require.Equal(
t, appHashList[0], appHashList[j],
t, string(appHashList[0]), string(appHashList[j]),
"non-determinism in seed %d: %d/%d, attempt: %d/%d\n", config.Seed, i+1, numSeeds, j+1, numTimesToRunPerSeed,
)
}
Expand Down
2 changes: 2 additions & 0 deletions x/capability/alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,6 @@ type (
ScopedKeeper = keeper.ScopedKeeper
Capability = types.Capability
CapabilityOwners = types.CapabilityOwners
GenesisState = types.GenesisState
GenesisOwners = types.GenesisOwners
)
41 changes: 41 additions & 0 deletions x/capability/genesis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package capability

import (
sdk "github.com/cosmos/cosmos-sdk/types"
)

// InitGenesis initializes the capability module's state from a provided genesis
// state.
func InitGenesis(ctx sdk.Context, k Keeper, genState GenesisState) {
k.SetIndex(ctx, genState.Index)

// set owners for each index and initialize capability
for _, genOwner := range genState.Owners {
k.SetOwners(ctx, genOwner.Index, genOwner.Owners)
k.InitializeCapability(ctx, genOwner.Index, genOwner.Owners)
}
}

// ExportGenesis returns the capability module's exported genesis.
func ExportGenesis(ctx sdk.Context, k Keeper) GenesisState {
index := k.GetLatestIndex(ctx)
owners := []GenesisOwners{}

for i := uint64(1); i < index; i++ {
capabilityOwners, ok := k.GetOwners(ctx, i)
if !ok || len(capabilityOwners.Owners) == 0 {
continue
}

genOwner := GenesisOwners{
Index: i,
Owners: capabilityOwners,
}
owners = append(owners, genOwner)
}

return GenesisState{
Index: index,
Owners: owners,
}
}
65 changes: 49 additions & 16 deletions x/capability/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,25 +106,10 @@ func (k *Keeper) InitializeAndSeal(ctx sdk.Context) {
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
index := types.IndexFromKey(iterator.Key())
cap := types.NewCapability(index)

var capOwners types.CapabilityOwners
k.cdc.MustUnmarshalBinaryBare(iterator.Value(), &capOwners)

for _, owner := range capOwners.Owners {
// Set the forward mapping between the module and capability tuple and the
// capability name in the memKVStore
memStore.Set(types.FwdCapabilityKey(owner.Module, cap), []byte(owner.Name))

// Set the reverse mapping between the module and capability name and the
// index in the in-memory store. Since marshalling and unmarshalling into a store
// will change memory address of capability, we simply store index as value here
// and retrieve the in-memory pointer to the capability from our map
memStore.Set(types.RevCapabilityKey(owner.Module, owner.Name), sdk.Uint64ToBigEndian(index))

// Set the mapping from index from index to in-memory capability in the go map
k.capMap[index] = cap
}
k.InitializeCapability(ctx, index, capOwners)
}

k.sealed = true
Expand All @@ -144,6 +129,54 @@ func (k Keeper) GetLatestIndex(ctx sdk.Context) uint64 {
return types.IndexFromKey(store.Get(types.KeyIndex))
}

// SetOwners set the capability owners to the store
func (k Keeper) SetOwners(ctx sdk.Context, index uint64, owners types.CapabilityOwners) {
prefixStore := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixIndexCapability)
indexKey := types.IndexToKey(index)

// set owners in persistent store
prefixStore.Set(indexKey, k.cdc.MustMarshalBinaryBare(&owners))
}

// GetOwners returns the capability owners with a given index.
func (k Keeper) GetOwners(ctx sdk.Context, index uint64) (types.CapabilityOwners, bool) {
prefixStore := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixIndexCapability)
indexKey := types.IndexToKey(index)

// get owners for index from persistent store
ownerBytes := prefixStore.Get(indexKey)
if ownerBytes == nil {
return types.CapabilityOwners{}, false
}
var owners types.CapabilityOwners
k.cdc.MustUnmarshalBinaryBare(ownerBytes, &owners)
return owners, true
}

// InitializeCapability takes in an index and an owners array. It creates the capability in memory
// and sets the fwd and reverse keys for each owner in the memstore
func (k Keeper) InitializeCapability(ctx sdk.Context, index uint64, owners types.CapabilityOwners) {

memStore := ctx.KVStore(k.memKey)

cap := types.NewCapability(index)
for _, owner := range owners.Owners {
// Set the forward mapping between the module and capability tuple and the
// capability name in the memKVStore
memStore.Set(types.FwdCapabilityKey(owner.Module, cap), []byte(owner.Name))

// Set the reverse mapping between the module and capability name and the
// index in the in-memory store. Since marshalling and unmarshalling into a store
// will change memory address of capability, we simply store index as value here
// and retrieve the in-memory pointer to the capability from our map
memStore.Set(types.RevCapabilityKey(owner.Module, owner.Name), sdk.Uint64ToBigEndian(index))

// Set the mapping from index from index to in-memory capability in the go map
k.capMap[index] = cap
}

}

// NewCapability attempts to create a new capability with a given name. If the
// capability already exists in the in-memory store, an error will be returned.
// Otherwise, a new capability is created with the current global unique index.
Expand Down
65 changes: 49 additions & 16 deletions x/capability/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@ package capability

import (
"encoding/json"
"fmt"
"math/rand"

"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
"github.com/cosmos/cosmos-sdk/x/capability/simulation"
"github.com/cosmos/cosmos-sdk/x/capability/types"

"github.com/gorilla/mux"
Expand All @@ -15,11 +19,9 @@ import (
)

var (
_ module.AppModule = AppModule{}
_ module.AppModuleBasic = AppModuleBasic{}

// TODO: Enable simulation once concrete types are defined.
// _ module.AppModuleSimulation = AppModuleSimulation{}
_ module.AppModule = AppModule{}
_ module.AppModuleBasic = AppModuleBasic{}
_ module.AppModuleSimulation = AppModule{}
)

// ----------------------------------------------------------------------------
Expand All @@ -28,10 +30,11 @@ var (

// AppModuleBasic implements the AppModuleBasic interface for the capability module.
type AppModuleBasic struct {
cdc codec.Marshaler
}

func NewAppModuleBasic() AppModuleBasic {
return AppModuleBasic{}
func NewAppModuleBasic(cdc codec.Marshaler) AppModuleBasic {
return AppModuleBasic{cdc: cdc}
}

// Name returns the capability module's name.
Expand All @@ -50,15 +53,21 @@ func (AppModuleBasic) DefaultGenesis(cdc codec.JSONMarshaler) json.RawMessage {
}

// ValidateGenesis performs genesis state validation for the capability module.
func (AppModuleBasic) ValidateGenesis(_ codec.JSONMarshaler, _ json.RawMessage) error { return nil }
func (AppModuleBasic) ValidateGenesis(cdc codec.JSONMarshaler, bz json.RawMessage) error {
var genState types.GenesisState
if err := cdc.UnmarshalJSON(bz, &genState); err != nil {
return fmt.Errorf("failed to unmarshal %s genesis state: %w", ModuleName, err)
}
return genState.Validate()
}

// RegisterRESTRoutes registers the capability module's REST service handlers.
func (a AppModuleBasic) RegisterRESTRoutes(_ context.CLIContext, _ *mux.Router) {}

// GetTxCmd returns the capability module's root tx command.
func (a AppModuleBasic) GetTxCmd(_ *codec.Codec) *cobra.Command { return nil }

// GetTxCmd returns the capability module's root query command.
// GetQueryCmd returns the capability module's root query command.
func (AppModuleBasic) GetQueryCmd(_ *codec.Codec) *cobra.Command { return nil }

// ----------------------------------------------------------------------------
Expand All @@ -72,9 +81,9 @@ type AppModule struct {
keeper Keeper
}

func NewAppModule(keeper Keeper) AppModule {
func NewAppModule(cdc codec.Marshaler, keeper Keeper) AppModule {
return AppModule{
AppModuleBasic: NewAppModuleBasic(),
AppModuleBasic: NewAppModuleBasic(cdc),
keeper: keeper,
}
}
Expand Down Expand Up @@ -102,19 +111,18 @@ func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {}
// InitGenesis performs the capability module's genesis initialization It returns
// no validator updates.
func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONMarshaler, gs json.RawMessage) []abci.ValidatorUpdate {
var genState types.GenesisState
var genState GenesisState
// Initialize global index to index in genesis state
cdc.MustUnmarshalJSON(gs, &genState)

am.keeper.SetIndex(ctx, genState.Index)

InitGenesis(ctx, am.keeper, genState)
return []abci.ValidatorUpdate{}
}

// ExportGenesis returns the capability module's exported genesis state as raw JSON bytes.
func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json.RawMessage {
index := am.keeper.GetLatestIndex(ctx)
return cdc.MustMarshalJSON(types.GenesisState{index})
genState := ExportGenesis(ctx, am.keeper)
return cdc.MustMarshalJSON(genState)
}

// BeginBlock executes all ABCI BeginBlock logic respective to the capability module.
Expand All @@ -125,3 +133,28 @@ func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {}
func (am AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate {
return []abci.ValidatorUpdate{}
}

// GenerateGenesisState creates a randomized GenState of the capability module.
func (AppModule) GenerateGenesisState(simState *module.SimulationState) {
simulation.RandomizedGenState(simState)
}

// ProposalContents performs a no-op
func (am AppModule) ProposalContents(simState module.SimulationState) []simtypes.WeightedProposalContent {
return nil
}

// RandomizedParams creates randomized capability param changes for the simulator.
func (AppModule) RandomizedParams(r *rand.Rand) []simtypes.ParamChange {
return nil
}

// RegisterStoreDecoder registers a decoder for capability module's types
func (am AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) {
sdr[StoreKey] = simulation.NewDecodeStore(am.cdc)
}

// WeightedOperations returns the all the gov module operations with their respective weights.
func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation {
return nil
}
34 changes: 34 additions & 0 deletions x/capability/simulation/decoder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package simulation

import (
"bytes"
"fmt"

tmkv "github.com/tendermint/tendermint/libs/kv"

"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/capability/types"
)

// NewDecodeStore returns a decoder function closure that unmarshals the KVPair's
// Value to the corresponding capaility type.
func NewDecodeStore(cdc codec.Marshaler) func(kvA, kvB tmkv.Pair) string {
return func(kvA, kvB tmkv.Pair) string {
switch {
case bytes.Equal(kvA.Key, types.KeyIndex):
idxA := sdk.BigEndianToUint64(kvA.Value)
idxB := sdk.BigEndianToUint64(kvB.Value)
return fmt.Sprintf("Index A: %d\nIndex B: %d\n", idxA, idxB)

case bytes.HasPrefix(kvA.Key, types.KeyPrefixIndexCapability):
var capOwnersA, capOwnersB types.CapabilityOwners
cdc.MustUnmarshalBinaryBare(kvA.Value, &capOwnersA)
cdc.MustUnmarshalBinaryBare(kvB.Value, &capOwnersB)
return fmt.Sprintf("CapabilityOwners A: %v\nCapabilityOwners B: %v\n", capOwnersA, capOwnersB)

default:
panic(fmt.Sprintf("invalid %s key prefix %X (%s)", types.ModuleName, kvA.Key, string(kvA.Key)))
}
}
}
Loading

0 comments on commit 930802e

Please sign in to comment.