x/capability: simulations (#6062)

* 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 <adityasripal@gmail.com>
Co-authored-by: Aleksandr Bezobchuk <aleks.bezobchuk@gmail.com>
Co-authored-by: Alexander Bezobchuk <alexanderbez@users.noreply.github.com>
This commit is contained in:
Federico Kunze 2020-04-30 11:07:43 -04:00 committed by GitHub
parent 7143d092c4
commit 930802e7a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 477 additions and 41 deletions

View File

@ -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),
@ -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),

View File

@ -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"
@ -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 {
@ -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(
@ -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,
)
}

View File

@ -37,4 +37,6 @@ type (
ScopedKeeper = keeper.ScopedKeeper
Capability = types.Capability
CapabilityOwners = types.CapabilityOwners
GenesisState = types.GenesisState
GenesisOwners = types.GenesisOwners
)

41
x/capability/genesis.go Normal file
View File

@ -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,
}
}

View File

@ -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
@ -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.

View File

@ -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"
@ -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{}
)
// ----------------------------------------------------------------------------
@ -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.
@ -50,7 +53,13 @@ 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) {}
@ -58,7 +67,7 @@ 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 }
// ----------------------------------------------------------------------------
@ -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,
}
}
@ -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.
@ -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
}

View File

@ -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)))
}
}
}

View File

@ -0,0 +1,59 @@
package simulation_test
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
tmkv "github.com/tendermint/tendermint/libs/kv"
"github.com/cosmos/cosmos-sdk/simapp"
codecstd "github.com/cosmos/cosmos-sdk/std"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/capability/simulation"
"github.com/cosmos/cosmos-sdk/x/capability/types"
)
func TestDecodeStore(t *testing.T) {
cdc := codecstd.NewAppCodec(codecstd.MakeCodec(simapp.ModuleBasics))
dec := simulation.NewDecodeStore(cdc)
capOwners := types.CapabilityOwners{
Owners: []types.Owner{{Module: "transfer", Name: "ports/transfer"}},
}
kvPairs := tmkv.Pairs{
tmkv.Pair{
Key: types.KeyIndex,
Value: sdk.Uint64ToBigEndian(10),
},
tmkv.Pair{
Key: types.KeyPrefixIndexCapability,
Value: cdc.MustMarshalBinaryBare(&capOwners),
},
tmkv.Pair{
Key: []byte{0x99},
Value: []byte{0x99},
},
}
tests := []struct {
name string
expectedLog string
}{
{"Index", "Index A: 10\nIndex B: 10\n"},
{"CapabilityOwners", fmt.Sprintf("CapabilityOwners A: %v\nCapabilityOwners B: %v\n", capOwners, capOwners)},
{"other", ""},
}
for i, tt := range tests {
i, tt := i, tt
t.Run(tt.name, func(t *testing.T) {
switch i {
case len(tests) - 1:
require.Panics(t, func() { dec(kvPairs[i], kvPairs[i]) }, tt.name)
default:
require.Equal(t, tt.expectedLog, dec(kvPairs[i], kvPairs[i]), tt.name)
}
})
}
}

View File

@ -0,0 +1,36 @@
package simulation
// DONTCOVER
import (
"fmt"
"math/rand"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/types/module"
"github.com/cosmos/cosmos-sdk/x/capability/types"
)
// Simulation parameter constants
const index = "index"
// GenIndex returns a random global index between 1-1000
func GenIndex(r *rand.Rand) uint64 {
return uint64(r.Int63n(1000)) + 1
}
// RandomizedGenState generates a random GenesisState for capability
func RandomizedGenState(simState *module.SimulationState) {
var idx uint64
simState.AppParams.GetOrGenerate(
simState.Cdc, index, &idx, simState.Rand,
func(r *rand.Rand) { idx = GenIndex(r) },
)
capabilityGenesis := types.GenesisState{Index: idx}
fmt.Printf("Selected randomly generated %s parameters:\n%s\n", types.ModuleName, codec.MustMarshalJSONIndent(simState.Cdc, capabilityGenesis))
simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(capabilityGenesis)
}

View File

@ -1,11 +1,65 @@
package types
import (
"fmt"
"strings"
)
// DefaultIndex is the default capability global index
const DefaultIndex uint64 = 1
// GenesisOwners defines the capability owners with their corresponding index.
type GenesisOwners struct {
Index uint64 `json:"index" yaml:"index"`
Owners CapabilityOwners `json:"index_owners" yaml:"index_owners"`
}
// GenesisState represents the Capability module genesis state
type GenesisState struct {
// capability global index
Index uint64 `json:"index" yaml:"index"`
// map from index to owners of the capability index
// index key is string to allow amino marshalling
Owners []GenesisOwners `json:"owners" yaml:"owners"`
}
// DefaultGenesis returns the default Capability genesis state
func DefaultGenesis() GenesisState {
return GenesisState{Index: 1}
return GenesisState{
Index: DefaultIndex,
Owners: []GenesisOwners{},
}
}
// Validate performs basic genesis state validation returning an error upon any
// failure.
func (gs GenesisState) Validate() error {
// NOTE: index must be greater than 0
if gs.Index == 0 {
return fmt.Errorf("capability index must be non-zero")
}
for _, genOwner := range gs.Owners {
if len(genOwner.Owners.Owners) == 0 {
return fmt.Errorf("empty owners in genesis")
}
// all exported existing indices must be between [1, gs.Index)
if genOwner.Index == 0 || genOwner.Index >= gs.Index {
return fmt.Errorf("owners exist for index %d outside of valid range: %d-%d", genOwner.Index, 1, gs.Index-1)
}
for _, owner := range genOwner.Owners.Owners {
if strings.TrimSpace(owner.Module) == "" {
return fmt.Errorf("owner's module cannot be blank: %s", owner)
}
if strings.TrimSpace(owner.Name) == "" {
return fmt.Errorf("owner's name cannot be blank: %s", owner)
}
}
}
return nil
}

View File

@ -0,0 +1,131 @@
package types
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestValidateGenesis(t *testing.T) {
testCases := []struct {
name string
malleate func(*GenesisState)
expPass bool
}{
{
name: "default",
malleate: func(_ *GenesisState) {},
expPass: true,
},
{
name: "valid genesis state",
malleate: func(genState *GenesisState) {
genState.Index = 10
genOwner := GenesisOwners{
Index: 1,
Owners: CapabilityOwners{[]Owner{{Module: "ibc", Name: "port/transfer"}}},
}
genState.Owners = append(genState.Owners, genOwner)
},
expPass: true,
},
{
name: "initial index is 0",
malleate: func(genState *GenesisState) {
genState.Index = 0
genOwner := GenesisOwners{
Index: 0,
Owners: CapabilityOwners{[]Owner{{Module: "ibc", Name: "port/transfer"}}},
}
genState.Owners = append(genState.Owners, genOwner)
},
expPass: false,
},
{
name: "blank owner module",
malleate: func(genState *GenesisState) {
genState.Index = 1
genOwner := GenesisOwners{
Index: 1,
Owners: CapabilityOwners{[]Owner{{Module: "", Name: "port/transfer"}}},
}
genState.Owners = append(genState.Owners, genOwner)
},
expPass: false,
},
{
name: "blank owner name",
malleate: func(genState *GenesisState) {
genState.Index = 1
genOwner := GenesisOwners{
Index: 1,
Owners: CapabilityOwners{[]Owner{{Module: "ibc", Name: ""}}},
}
genState.Owners = append(genState.Owners, genOwner)
},
expPass: false,
},
{
name: "index above range",
malleate: func(genState *GenesisState) {
genState.Index = 10
genOwner := GenesisOwners{
Index: 12,
Owners: CapabilityOwners{[]Owner{{Module: "ibc", Name: "port/transfer"}}},
}
genState.Owners = append(genState.Owners, genOwner)
},
expPass: false,
},
{
name: "index below range",
malleate: func(genState *GenesisState) {
genState.Index = 10
genOwner := GenesisOwners{
Index: 0,
Owners: CapabilityOwners{[]Owner{{Module: "ibc", Name: "port/transfer"}}},
}
genState.Owners = append(genState.Owners, genOwner)
},
expPass: false,
},
{
name: "owners are empty",
malleate: func(genState *GenesisState) {
genState.Index = 10
genOwner := GenesisOwners{
Index: 0,
Owners: CapabilityOwners{[]Owner{}},
}
genState.Owners = append(genState.Owners, genOwner)
},
expPass: false,
},
}
for _, tc := range testCases {
tc := tc
genState := DefaultGenesis()
tc.malleate(&genState)
err := genState.Validate()
if tc.expPass {
require.NoError(t, err, tc.name)
} else {
require.Error(t, err, tc.name)
}
}
}

View File

@ -9,12 +9,17 @@ import (
// InitGenesis binds to portid from genesis state
func InitGenesis(ctx sdk.Context, keeper Keeper, state types.GenesisState) {
// transfer module binds to the transfer port on InitChain
// and claims the returned capability
err := keeper.BindPort(ctx, state.PortID)
if err != nil {
panic(fmt.Sprintf("could not claim port capability: %v", err))
// Only try to bind to port if it is not already bound, since we may already own
// port capability from capability InitGenesis
if !keeper.IsBound(ctx, state.PortID) {
// transfer module binds to the transfer port on InitChain
// and claims the returned capability
err := keeper.BindPort(ctx, state.PortID)
if err != nil {
panic(fmt.Sprintf("could not claim port capability: %v", err))
}
}
// check if the module account exists
moduleAcc := keeper.GetTransferAccount(ctx)
if moduleAcc == nil {

View File

@ -94,6 +94,12 @@ func (k Keeper) ChanCloseInit(ctx sdk.Context, portID, channelID string) error {
return k.channelKeeper.ChanCloseInit(ctx, portID, channelID, chanCap)
}
// IsBound checks if the transfer module is already bound to the desired port
func (k Keeper) IsBound(ctx sdk.Context, portID string) bool {
_, ok := k.scopedKeeper.GetCapability(ctx, porttypes.PortPath(portID))
return ok
}
// BindPort defines a wrapper function for the ort Keeper's function in
// order to expose it to module's InitGenesis function
func (k Keeper) BindPort(ctx sdk.Context, portID string) error {