From af10e050d23e2b6f4c50e7a7ffc273192d32a0a2 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 18 Mar 2020 13:40:32 -0400 Subject: [PATCH 01/29] Implement in-memory KVStore --- baseapp/baseapp.go | 8 ++++++++ simapp/app.go | 6 ++++++ store/mem/mem_test.go | 39 +++++++++++++++++++++++++++++++++++++++ store/mem/store.go | 39 +++++++++++++++++++++++++++++++++++++++ store/rootmulti/store.go | 8 ++++++++ store/types/store.go | 20 ++++++++++++++++++++ types/store.go | 15 +++++++++++++++ 7 files changed, 135 insertions(+) create mode 100644 store/mem/mem_test.go create mode 100644 store/mem/store.go diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 3837a34ccc9a..c8aab9a85a89 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -199,6 +199,14 @@ func (app *BaseApp) MountTransientStores(keys map[string]*sdk.TransientStoreKey) } } +// MountMemoryStores mounts all in-memory KVStores with the BaseApp's internal +// commit multi-store. +func (app *BaseApp) MountMemoryStores(keys map[string]*sdk.MemoryStoreKey) { + for _, memKey := range keys { + app.MountStore(memKey, sdk.StoreTypeMemory) + } +} + // MountStoreWithDB mounts a store to the provided key in the BaseApp // multistore, using a specified DB. func (app *BaseApp) MountStoreWithDB(key sdk.StoreKey, typ sdk.StoreType, db dbm.DB) { diff --git a/simapp/app.go b/simapp/app.go index 4ed3eee2464b..663f23a7415e 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -157,6 +157,9 @@ func NewSimApp( ) tkeys := sdk.NewTransientStoreKeys(params.TStoreKey) + // TODO: + // memKeys := sdk.NewMemoryStoreKeys(...) + app := &SimApp{ BaseApp: bApp, cdc: cdc, @@ -302,6 +305,9 @@ func NewSimApp( app.MountKVStores(keys) app.MountTransientStores(tkeys) + // TODO: + // app.MountMemoryStores(memKeys) + // initialize BaseApp app.SetInitChainer(app.InitChainer) app.SetBeginBlocker(app.BeginBlocker) diff --git a/store/mem/mem_test.go b/store/mem/mem_test.go new file mode 100644 index 000000000000..cff4c37da7dc --- /dev/null +++ b/store/mem/mem_test.go @@ -0,0 +1,39 @@ +package mem_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/store/mem" + "github.com/cosmos/cosmos-sdk/store/types" +) + +func TestStore(t *testing.T) { + db := mem.NewStore() + key, value := []byte("key"), []byte("value") + + require.Equal(t, types.StoreTypeMemory, db.GetStoreType()) + + require.Nil(t, db.Get(key)) + db.Set(key, value) + require.Equal(t, value, db.Get(key)) + + newValue := []byte("newValue") + db.Set(key, newValue) + require.Equal(t, newValue, db.Get(key)) + + db.Delete(key) + require.Nil(t, db.Get(key)) +} + +func TestCommit(t *testing.T) { + db := mem.NewStore() + key, value := []byte("key"), []byte("value") + + db.Set(key, value) + id := db.Commit() + require.True(t, id.IsZero()) + require.True(t, db.LastCommitID().IsZero()) + require.Equal(t, value, db.Get(key)) +} diff --git a/store/mem/store.go b/store/mem/store.go new file mode 100644 index 000000000000..b66eb8414817 --- /dev/null +++ b/store/mem/store.go @@ -0,0 +1,39 @@ +package mem + +import ( + dbm "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/store/dbadapter" + "github.com/cosmos/cosmos-sdk/store/types" +) + +var ( + _ types.KVStore = (*Store)(nil) + _ types.Committer = (*Store)(nil) +) + +// Store implements an in-memory only KVStore. Entries are peresistent between +// commits and thus between blocks. +type Store struct { + dbadapter.Store +} + +func NewStore() *Store { + return NewStoreWithDB(dbm.NewMemDB()) +} + +func NewStoreWithDB(db *dbm.MemDB) *Store { + return &Store{Store: dbadapter.Store{DB: db}} +} + +// GetStoreType returns the Store's type. +func (ts *Store) GetStoreType() types.StoreType { + return types.StoreTypeMemory +} + +// Commit performs a no-op as entries are persistent between commitments. +func (ts *Store) Commit() (id types.CommitID) { return } + +// nolint +func (ts *Store) SetPruning(pruning types.PruningOptions) {} +func (ts *Store) LastCommitID() (id types.CommitID) { return } diff --git a/store/rootmulti/store.go b/store/rootmulti/store.go index 5239ab8a4e15..0edbff7fee8d 100644 --- a/store/rootmulti/store.go +++ b/store/rootmulti/store.go @@ -13,6 +13,7 @@ import ( "github.com/cosmos/cosmos-sdk/store/cachemulti" "github.com/cosmos/cosmos-sdk/store/dbadapter" "github.com/cosmos/cosmos-sdk/store/iavl" + "github.com/cosmos/cosmos-sdk/store/mem" "github.com/cosmos/cosmos-sdk/store/tracekv" "github.com/cosmos/cosmos-sdk/store/transient" "github.com/cosmos/cosmos-sdk/store/types" @@ -517,6 +518,13 @@ func (rs *Store) loadCommitStoreFromParams(key types.StoreKey, id types.CommitID return transient.NewStore(), nil + case types.StoreTypeMemory: + if _, ok := key.(*types.MemoryStoreKey); !ok { + return nil, fmt.Errorf("unexpected key type for a MemoryStoreKey; got: %s", key.String()) + } + + return mem.NewStore(), nil + default: panic(fmt.Sprintf("unrecognized store type %v", params.typ)) } diff --git a/store/types/store.go b/store/types/store.go index d48585f8fce2..5de396651dd6 100644 --- a/store/types/store.go +++ b/store/types/store.go @@ -266,6 +266,7 @@ const ( StoreTypeDB StoreTypeIAVL StoreTypeTransient + StoreTypeMemory ) //---------------------------------------- @@ -326,6 +327,25 @@ func (key *TransientStoreKey) String() string { return fmt.Sprintf("TransientStoreKey{%p, %s}", key, key.name) } +// MemoryStoreKey defines a typed key to be used with an in-memory KVStore. +type MemoryStoreKey struct { + name string +} + +func NewMemoryStoreKey(name string) *MemoryStoreKey { + return &MemoryStoreKey{name: name} +} + +// Name returns the name of the MemoryStoreKey. +func (key *MemoryStoreKey) Name() string { + return key.name +} + +// String returns a stringified representation of the MemoryStoreKey. +func (key *MemoryStoreKey) String() string { + return fmt.Sprintf("MemoryStoreKey{%p, %s}", key, key.name) +} + //---------------------------------------- // key-value result for iterator queries diff --git a/types/store.go b/types/store.go index 6057526c80e9..884556cc6b26 100644 --- a/types/store.go +++ b/types/store.go @@ -76,6 +76,7 @@ const ( StoreTypeDB = types.StoreTypeDB StoreTypeIAVL = types.StoreTypeIAVL StoreTypeTransient = types.StoreTypeTransient + StoreTypeMemory = types.StoreTypeMemory ) // nolint - reexport @@ -84,6 +85,7 @@ type ( CapabilityKey = types.CapabilityKey KVStoreKey = types.KVStoreKey TransientStoreKey = types.TransientStoreKey + MemoryStoreKey = types.MemoryStoreKey ) // NewKVStoreKey returns a new pointer to a KVStoreKey. @@ -99,6 +101,7 @@ func NewKVStoreKeys(names ...string) map[string]*KVStoreKey { for _, name := range names { keys[name] = NewKVStoreKey(name) } + return keys } @@ -115,6 +118,18 @@ func NewTransientStoreKeys(names ...string) map[string]*TransientStoreKey { for _, name := range names { keys[name] = NewTransientStoreKey(name) } + + return keys +} + +// NewMemoryStoreKeys constructs a new map matching store key names to their +// respective MemoryStoreKey references. +func NewMemoryStoreKeys(names ...string) map[string]*MemoryStoreKey { + keys := make(map[string]*MemoryStoreKey) + for _, name := range names { + keys[name] = types.NewMemoryStoreKey(name) + } + return keys } From 29d68cf621042b5f8030bfcbbd7fbad4ebfb9d3a Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 18 Mar 2020 16:15:16 -0400 Subject: [PATCH 02/29] Start keeper and types --- x/capability/alias.go | 18 +++++++++ x/capability/keeper/keeper.go | 73 +++++++++++++++++++++++++++++++++++ x/capability/types/types.go | 32 +++++++++++++++ 3 files changed, 123 insertions(+) create mode 100644 x/capability/alias.go create mode 100644 x/capability/keeper/keeper.go create mode 100644 x/capability/types/types.go diff --git a/x/capability/alias.go b/x/capability/alias.go new file mode 100644 index 000000000000..8e4a2faf0715 --- /dev/null +++ b/x/capability/alias.go @@ -0,0 +1,18 @@ +package capability + +import ( + "github.com/cosmos/cosmos-sdk/x/capability/keeper" + "github.com/cosmos/cosmos-sdk/x/capability/types" +) + +// nolint + +var ( + NewCapabilityKey = types.NewCapabilityKey + NewKeeper = keeper.NewKeeper +) + +type ( + Capability = types.Capability + CapabilityKey = types.CapabilityKey +) diff --git a/x/capability/keeper/keeper.go b/x/capability/keeper/keeper.go new file mode 100644 index 000000000000..533f5d6e7337 --- /dev/null +++ b/x/capability/keeper/keeper.go @@ -0,0 +1,73 @@ +package keeper + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +type ( + // Keeper defines the capability module's keeper. It is responsible for provisioning, + // tracking, and authenticating capabilities at runtime. During application + // initialization, the keeper can be hooked up to modules through unique function + // references so that it can identify the calling module when later invoked. + // + // When the initial state is loaded from disk, the keeper allows the ability to + // create new capability keys for all previously allocated capability identifiers + // (allocated during execution of past transactions and assigned to particular modes), + // and keep them in a memory-only store while the chain is running. + // + // The keeper allows the ability to create scoped sub-keepers which are tied to + // a single specific module. + Keeper struct { + cdc *codec.Codec + storeKey sdk.StoreKey + memKey sdk.MemoryStoreKey + scopedModules map[string]struct{} + sealed bool + } + + // ScopedKeeper defines a scoped sub-keeper which is tied to a single specific + // module provisioned by the capability keeper. Scoped keepers must be created + // at application initialization and passed to modules, which can then use them + // to claim capabilities they receive and retrieve capabilities which they own + // by name, in addition to creating new capabilities & authenticating capabilities + // passed by other modules. + ScopedKeeper struct { + storeKey sdk.StoreKey + memKey sdk.MemoryStoreKey + module string + } +) + +func NewKeeper(cdc *codec.Codec, storeKey sdk.StoreKey, memKey sdk.MemoryStoreKey) Keeper { + return Keeper{ + cdc: cdc, + storeKey: storeKey, + memKey: memKey, + scopedModules: make(map[string]struct{}), + sealed: false, + } +} + +// ScopeToModule attempts to create and return a ScopedKeeper for a given module +// by name. It will panic if the keeper is already sealed or if the module name +// already has a ScopedKeeper. +func (k Keeper) ScopeToModule(moduleName string) ScopedKeeper { + if k.sealed { + panic("cannot scope to module via a sealed capability keeper") + } + + if _, ok := k.scopedModules[moduleName]; ok { + panic(fmt.Sprintf("cannot create multiple scoped keepers for the same module name: %s", moduleName)) + } + + k.scopedModules[moduleName] = struct{}{} + + return ScopedKeeper{ + storeKey: k.storeKey, + memKey: k.memKey, + module: moduleName, + } +} diff --git a/x/capability/types/types.go b/x/capability/types/types.go new file mode 100644 index 000000000000..51fd0bd6040e --- /dev/null +++ b/x/capability/types/types.go @@ -0,0 +1,32 @@ +package types + +import "fmt" + +var _ Capability = (*CapabilityKey)(nil) + +// Capability defines the interface a capability must implement. The given +// capability must provide a GUID. +type Capability interface { + GetIndex() uint64 + String() string +} + +// CapabilityKey defines an implementation of a Capability. The index provided to +// a CapabilityKey must be globally unique. +type CapabilityKey struct { + Name string + Index uint64 +} + +func NewCapabilityKey(name string, index uint64) Capability { + return &CapabilityKey{Name: name, Index: index} +} + +// GetIndex returns the capability key's index. +func (ck *CapabilityKey) GetIndex() uint64 { + return ck.Index +} + +func (ck *CapabilityKey) String() string { + return fmt.Sprintf("CapabilityKey{%p, %d, %s}", ck, ck.Index, ck.Name) +} From 191df595d2145f33ea3718826e8343f96f043c34 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 19 Mar 2020 15:41:12 -0400 Subject: [PATCH 03/29] Add codec --- x/capability/types/codec.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 x/capability/types/codec.go diff --git a/x/capability/types/codec.go b/x/capability/types/codec.go new file mode 100644 index 000000000000..7b7cea97dcf4 --- /dev/null +++ b/x/capability/types/codec.go @@ -0,0 +1,25 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" +) + +// ModuleCdc defines the capability module's codec. The codec is not sealed as to +// allow other modules to register their concrete Capability types. +var ModuleCdc = codec.New() + +// RegisterCodec registers all the necessary types and interfaces for the +// capability module. +func RegisterCodec(cdc *codec.Codec) { + cdc.RegisterInterface((*Capability)(nil), nil) +} + +// RegisterCapabilityTypeCodec registers an external concrete Capability type +// defined in another module for the internal ModuleCdc. +func RegisterCapabilityTypeCodec(o interface{}, name string) { + ModuleCdc.RegisterConcrete(o, name, nil) +} + +func init() { + RegisterCodec(ModuleCdc) +} From cb6fc182b7bcde4ec147155dace3d6d27c52bdd9 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 19 Mar 2020 15:41:20 -0400 Subject: [PATCH 04/29] Add keys logic --- x/capability/types/keys.go | 52 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 x/capability/types/keys.go diff --git a/x/capability/types/keys.go b/x/capability/types/keys.go new file mode 100644 index 000000000000..6c1a6ddf2fae --- /dev/null +++ b/x/capability/types/keys.go @@ -0,0 +1,52 @@ +package types + +import ( + "encoding/binary" + "fmt" +) + +const ( + // ModuleName defines the module name + ModuleName = "capability" + + // StoreKey defines the primary module store key + StoreKey = ModuleName + + // MemStoreKey defines the in-memory store key + MemStoreKey = "mem_capability" +) + +var ( + // KeyIndex defines the key that stores the current globally unique capability + // index. + KeyIndex = []byte("index") + + // KeyPrefixIndexCapability defines a key prefix that stores index to capability + // name mappings. + KeyPrefixIndexCapability = []byte("capability_index") +) + +// RevCapabilityKey returns a reverse lookup key for a given module and capability +// name. +func RevCapabilityKey(module, name string) []byte { + return []byte(fmt.Sprintf("%s/rev/%s", module, name)) +} + +// FwdCapabilityKey returns a forward lookup key for a given module and capability +// reference. +func FwdCapabilityKey(module string, cap Capability) []byte { + return []byte(fmt.Sprintf("%s/fwd/%s", module, cap)) +} + +// IndexToKey returns bytes to be used as a key for a given capability index. +func IndexToKey(index uint64) []byte { + buf := make([]byte, 8) + binary.LittleEndian.PutUint64(buf, index) + return buf +} + +// IndexFromKey returns an index from a call to IndexToKey for a given capability +// index. +func IndexFromKey(key []byte) uint64 { + return binary.LittleEndian.Uint64(key) +} From 447221b8f8b0df60a303f5475fe25f774e644ed4 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 19 Mar 2020 15:41:48 -0400 Subject: [PATCH 05/29] Update types --- x/capability/types/keys_test.go | 29 ++++++++++++++ x/capability/types/types.go | 66 +++++++++++++++++++++++++++++--- x/capability/types/types_test.go | 34 ++++++++++++++++ 3 files changed, 123 insertions(+), 6 deletions(-) create mode 100644 x/capability/types/keys_test.go create mode 100644 x/capability/types/types_test.go diff --git a/x/capability/types/keys_test.go b/x/capability/types/keys_test.go new file mode 100644 index 000000000000..b4a0168912b2 --- /dev/null +++ b/x/capability/types/keys_test.go @@ -0,0 +1,29 @@ +package types_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/x/capability/types" +) + +func TestRevCapabilityKey(t *testing.T) { + expected := []byte("bank/rev/send") + require.Equal(t, expected, types.RevCapabilityKey("bank", "send")) +} + +func TestFwdCapabilityKey(t *testing.T) { + cap := types.NewCapabilityKey(23) + expected := []byte(fmt.Sprintf("bank/fwd/%s", cap)) + require.Equal(t, expected, types.FwdCapabilityKey("bank", cap)) +} + +func TestIndexToKey(t *testing.T) { + require.Equal(t, []byte{0x5a, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, types.IndexToKey(3162)) +} + +func TestIndexFromKey(t *testing.T) { + require.Equal(t, uint64(3162), types.IndexFromKey([]byte{0x5a, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0})) +} diff --git a/x/capability/types/types.go b/x/capability/types/types.go index 51fd0bd6040e..e6da5535d0d9 100644 --- a/x/capability/types/types.go +++ b/x/capability/types/types.go @@ -1,6 +1,12 @@ package types -import "fmt" +import ( + "fmt" + + "gopkg.in/yaml.v2" + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) var _ Capability = (*CapabilityKey)(nil) @@ -14,12 +20,13 @@ type Capability interface { // CapabilityKey defines an implementation of a Capability. The index provided to // a CapabilityKey must be globally unique. type CapabilityKey struct { - Name string - Index uint64 + Index uint64 `json:"index" yaml:"index"` } -func NewCapabilityKey(name string, index uint64) Capability { - return &CapabilityKey{Name: name, Index: index} +// NewCapabilityKey returns a reference to a new CapabilityKey to be used as an +// actual capability. +func NewCapabilityKey(index uint64) Capability { + return &CapabilityKey{Index: index} } // GetIndex returns the capability key's index. @@ -27,6 +34,53 @@ func (ck *CapabilityKey) GetIndex() uint64 { return ck.Index } +// String returns the string representation of a CapabilityKey. The string contains +// the CapabilityKey's memory reference as the string is to be used in a composite +// key and to authenticate capabilities. func (ck *CapabilityKey) String() string { - return fmt.Sprintf("CapabilityKey{%p, %d, %s}", ck, ck.Index, ck.Name) + return fmt.Sprintf("CapabilityKey{%p, %d}", ck, ck.Index) +} + +// Owner defines a single capability owner. An owner is defined by the name of +// capability and the module name. +type Owner struct { + Module string `json:"module" yaml:"module"` + Name string `json:"name" yaml:"name"` +} + +func NewOwner(module, name string) Owner { + return Owner{Module: module, Name: name} +} + +// Key returns a composite key for an Owner. +func (o Owner) Key() string { + return fmt.Sprintf("%s/%s", o.Module, o.Name) +} + +func (o Owner) String() string { + bz, _ := yaml.Marshal(o) + return string(bz) +} + +// CapabilityOwners defines a set of owners of a single Capability. The set of +// owners must be unique. +type CapabilityOwners struct { + Owners []Owner `json:"owners" yaml:"owners"` +} + +func NewCapabilityOwners() *CapabilityOwners { + return &CapabilityOwners{Owners: make([]Owner, 0)} +} + +// Set attempts to add a given owner to the CapabilityOwners. If the owner already +// exists, an error will be returned. +func (co *CapabilityOwners) Set(owner Owner) error { + for _, o := range co.Owners { + if o.Key() == owner.Key() { + return sdkerrors.Wrapf(ErrOwnerClaimed, owner.String()) + } + } + + co.Owners = append(co.Owners, owner) + return nil } diff --git a/x/capability/types/types_test.go b/x/capability/types/types_test.go new file mode 100644 index 000000000000..74da351d7ac3 --- /dev/null +++ b/x/capability/types/types_test.go @@ -0,0 +1,34 @@ +package types_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/x/capability/types" +) + +func TestCapabilityKey(t *testing.T) { + idx := uint64(3162) + cap := types.NewCapabilityKey(idx) + require.Equal(t, idx, cap.GetIndex()) + require.Equal(t, fmt.Sprintf("CapabilityKey{%p, %d}", cap, idx), cap.String()) +} + +func TestOwner(t *testing.T) { + o := types.NewOwner("bank", "send") + require.Equal(t, "bank/send", o.Key()) + require.Equal(t, "module: bank\nname: send\n", o.String()) +} + +func TestCapabilityOwners(t *testing.T) { + co := types.NewCapabilityOwners() + o1 := types.NewOwner("bank", "send") + o2 := types.NewOwner("slashing", "slash") + + require.NoError(t, co.Set(o1)) + require.Error(t, co.Set(o1)) + require.NoError(t, co.Set(o2)) + require.Equal(t, []types.Owner{o1, o2}, co.Owners) +} From 970e7864250df06b792af88cd9bb2ce950010ab8 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 19 Mar 2020 15:42:40 -0400 Subject: [PATCH 06/29] Update keeper --- x/capability/alias.go | 27 +++++++++++++--- x/capability/keeper/keeper.go | 61 +++++++++++++++++++++++++++++++++-- x/capability/types/errors.go | 13 ++++++++ 3 files changed, 94 insertions(+), 7 deletions(-) create mode 100644 x/capability/types/errors.go diff --git a/x/capability/alias.go b/x/capability/alias.go index 8e4a2faf0715..4bdd24ca80ac 100644 --- a/x/capability/alias.go +++ b/x/capability/alias.go @@ -6,13 +6,32 @@ import ( ) // nolint +// DONTCOVER + +const ( + ModuleName = types.ModuleName + StoreKey = types.StoreKey + MemStoreKey = types.MemStoreKey +) var ( - NewCapabilityKey = types.NewCapabilityKey - NewKeeper = keeper.NewKeeper + NewKeeper = keeper.NewKeeper + NewCapabilityKey = types.NewCapabilityKey + RevCapabilityKey = types.RevCapabilityKey + FwdCapabilityKey = types.FwdCapabilityKey + KeyIndex = types.KeyIndex + KeyPrefixIndexCapability = types.KeyPrefixIndexCapability + ErrCapabilityTaken = types.ErrCapabilityTaken + ErrOwnerClaimed = types.ErrOwnerClaimed + RegisterCodec = types.RegisterCodec + RegisterCapabilityTypeCodec = types.RegisterCapabilityTypeCodec + ModuleCdc = types.ModuleCdc + NewOwner = types.NewOwner + NewCapabilityOwners = types.NewCapabilityOwners ) type ( - Capability = types.Capability - CapabilityKey = types.CapabilityKey + Capability = types.Capability + CapabilityKey = types.CapabilityKey + CapabilityOwners = types.CapabilityOwners ) diff --git a/x/capability/keeper/keeper.go b/x/capability/keeper/keeper.go index 533f5d6e7337..d0caad92f358 100644 --- a/x/capability/keeper/keeper.go +++ b/x/capability/keeper/keeper.go @@ -4,7 +4,9 @@ import ( "fmt" "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/capability/types" ) type ( @@ -23,7 +25,7 @@ type ( Keeper struct { cdc *codec.Codec storeKey sdk.StoreKey - memKey sdk.MemoryStoreKey + memKey sdk.StoreKey scopedModules map[string]struct{} sealed bool } @@ -35,13 +37,14 @@ type ( // by name, in addition to creating new capabilities & authenticating capabilities // passed by other modules. ScopedKeeper struct { + cdc *codec.Codec storeKey sdk.StoreKey - memKey sdk.MemoryStoreKey + memKey sdk.StoreKey module string } ) -func NewKeeper(cdc *codec.Codec, storeKey sdk.StoreKey, memKey sdk.MemoryStoreKey) Keeper { +func NewKeeper(cdc *codec.Codec, storeKey, memKey sdk.StoreKey) Keeper { return Keeper{ cdc: cdc, storeKey: storeKey, @@ -71,3 +74,55 @@ func (k Keeper) ScopeToModule(moduleName string) ScopedKeeper { module: moduleName, } } + +// AuthenticateCapability attempts to authenticate a given capability and name +// from a caller. It allows for a caller to check that a capability does in fact +// correspond to a particular name. The scoped keeper will lookup the capability +// from the internal in-memory store and check against the provided name. It returns +// true upon success and false upon failure. +// +// Note, the capability's forward mapping is indexed by a string which should +// contain it's unique memory reference. +func (sk ScopedKeeper) AuthenticateCapability(ctx sdk.Context, cap types.Capability, name string) bool { + memStore := ctx.KVStore(sk.memKey) + + bz := memStore.Get(types.FwdCapabilityKey(sk.module, cap)) + return string(bz) == name +} + +// ClaimCapability attempts to claim a given Capability and name tuple. This tuple +// is considered an Owner. It will attempt to add the owner to the persistent +// set of capability owners for the capability index. If the owner already exists, +// it will return an error. Otherwise, it will also set a forward and reverse index +// for the capability and capability name. +func (sk ScopedKeeper) ClaimCapability(ctx sdk.Context, cap types.Capability, name string) error { + store := prefix.NewStore(ctx.KVStore(sk.storeKey), types.KeyPrefixIndexCapability) + memStore := ctx.KVStore(sk.memKey) + indexKey := types.IndexToKey(cap.GetIndex()) + + var capOwners *types.CapabilityOwners + + bz := store.Get(indexKey) + if len(bz) == 0 { + capOwners = types.NewCapabilityOwners() + } else { + sk.cdc.MustUnmarshalBinaryBare(bz, capOwners) + } + + if err := capOwners.Set(types.NewOwner(sk.module, name)); err != nil { + return err + } + + // update capability owner set + store.Set(indexKey, sk.cdc.MustMarshalBinaryBare(capOwners)) + + // Set the forward mapping between the module and capability tuple and the + // capability name in the in-memory store. + memStore.Set(types.FwdCapabilityKey(sk.module, cap), []byte(name)) + + // Set the reverse mapping between the module and capability name and the + // capability in the in-memory store. + memStore.Set(types.RevCapabilityKey(sk.module, name), sk.cdc.MustMarshalBinaryBare(cap)) + + return nil +} diff --git a/x/capability/types/errors.go b/x/capability/types/errors.go new file mode 100644 index 000000000000..fbf718e9fecc --- /dev/null +++ b/x/capability/types/errors.go @@ -0,0 +1,13 @@ +package types + +// DONTCOVER + +import ( + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// x/capability module sentinel errors +var ( + ErrCapabilityTaken = sdkerrors.Register(ModuleName, 2, "capability name already taken") + ErrOwnerClaimed = sdkerrors.Register(ModuleName, 3, "given owner already claimed capability") +) From 0f950e164ebc2ef7470a1ff0cce9d33cb5ab24c2 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 19 Mar 2020 16:08:43 -0400 Subject: [PATCH 07/29] Implement NewCapability --- x/capability/keeper/keeper.go | 70 +++++++++++++++++++++++++++++------ 1 file changed, 58 insertions(+), 12 deletions(-) diff --git a/x/capability/keeper/keeper.go b/x/capability/keeper/keeper.go index d0caad92f358..2ffd18f6df54 100644 --- a/x/capability/keeper/keeper.go +++ b/x/capability/keeper/keeper.go @@ -75,6 +75,43 @@ func (k Keeper) ScopeToModule(moduleName string) ScopedKeeper { } } +// 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 index. The +// newly created capability has the module and name tuple set as the initial owner. +// Finally, the global index is incremented along with forward and reverse indexes +// set in the in-memory store. +func (sk ScopedKeeper) NewCapability(ctx sdk.Context, name string) (types.Capability, error) { + store := ctx.KVStore(sk.storeKey) + memStore := ctx.KVStore(sk.memKey) + + if memStore.Has(types.RevCapabilityKey(sk.module, name)) { + return nil, sdkerrors.Wrapf(types.ErrCapabilityTaken, fmt.Sprintf("module: %s, name: %s", sk.module, name)) + } + + // create new capability with the current global index + index := types.IndexFromKey(store.Get(types.KeyIndex)) + cap := types.NewCapabilityKey(index) + + // update capability owner set + if err := sk.addOwner(ctx, cap, name); err != nil { + return nil, err + } + + // increment global index + store.Set(types.KeyIndex, types.IndexToKey(index+1)) + + // Set the forward mapping between the module and capability tuple and the + // capability name in the in-memory store. + memStore.Set(types.FwdCapabilityKey(sk.module, cap), []byte(name)) + + // Set the reverse mapping between the module and capability name and the + // capability in the in-memory store. + memStore.Set(types.RevCapabilityKey(sk.module, name), sk.cdc.MustMarshalBinaryBare(cap)) + + return cap, nil +} + // AuthenticateCapability attempts to authenticate a given capability and name // from a caller. It allows for a caller to check that a capability does in fact // correspond to a particular name. The scoped keeper will lookup the capability @@ -96,13 +133,31 @@ func (sk ScopedKeeper) AuthenticateCapability(ctx sdk.Context, cap types.Capabil // it will return an error. Otherwise, it will also set a forward and reverse index // for the capability and capability name. func (sk ScopedKeeper) ClaimCapability(ctx sdk.Context, cap types.Capability, name string) error { - store := prefix.NewStore(ctx.KVStore(sk.storeKey), types.KeyPrefixIndexCapability) + // update capability owner set + if err := sk.addOwner(ctx, cap, name); err != nil { + return err + } + memStore := ctx.KVStore(sk.memKey) + + // Set the forward mapping between the module and capability tuple and the + // capability name in the in-memory store. + memStore.Set(types.FwdCapabilityKey(sk.module, cap), []byte(name)) + + // Set the reverse mapping between the module and capability name and the + // capability in the in-memory store. + memStore.Set(types.RevCapabilityKey(sk.module, name), sk.cdc.MustMarshalBinaryBare(cap)) + + return nil +} + +func (sk ScopedKeeper) addOwner(ctx sdk.Context, cap types.Capability, name string) error { + prefixStore := prefix.NewStore(ctx.KVStore(sk.storeKey), types.KeyPrefixIndexCapability) indexKey := types.IndexToKey(cap.GetIndex()) var capOwners *types.CapabilityOwners - bz := store.Get(indexKey) + bz := prefixStore.Get(indexKey) if len(bz) == 0 { capOwners = types.NewCapabilityOwners() } else { @@ -114,15 +169,6 @@ func (sk ScopedKeeper) ClaimCapability(ctx sdk.Context, cap types.Capability, na } // update capability owner set - store.Set(indexKey, sk.cdc.MustMarshalBinaryBare(capOwners)) - - // Set the forward mapping between the module and capability tuple and the - // capability name in the in-memory store. - memStore.Set(types.FwdCapabilityKey(sk.module, cap), []byte(name)) - - // Set the reverse mapping between the module and capability name and the - // capability in the in-memory store. - memStore.Set(types.RevCapabilityKey(sk.module, name), sk.cdc.MustMarshalBinaryBare(cap)) - + prefixStore.Set(indexKey, sk.cdc.MustMarshalBinaryBare(capOwners)) return nil } From 2a3dde91061896f2c6006ef30d7dbbd1dfab30f3 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 20 Mar 2020 10:52:37 -0400 Subject: [PATCH 08/29] Implement InitializeAndSeal --- x/capability/keeper/keeper.go | 44 +++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/x/capability/keeper/keeper.go b/x/capability/keeper/keeper.go index 2ffd18f6df54..4d3acd7495fd 100644 --- a/x/capability/keeper/keeper.go +++ b/x/capability/keeper/keeper.go @@ -6,6 +6,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/capability/types" ) @@ -69,12 +70,55 @@ func (k Keeper) ScopeToModule(moduleName string) ScopedKeeper { k.scopedModules[moduleName] = struct{}{} return ScopedKeeper{ + cdc: k.cdc, storeKey: k.storeKey, memKey: k.memKey, module: moduleName, } } +// InitializeAndSeal loads all capabilities from the persistent KVStore into the +// in-memory store and seals the keeper to prevent further modules from creating +// a scoped keeper. InitializeAndSeal must be called once after the application +// state is loaded. +func (k Keeper) InitializeAndSeal(ctx sdk.Context) { + if k.sealed { + panic("cannot initialize and seal an already sealed capability keeper") + } + + memStore := ctx.KVStore(k.memKey) + memStoreType := memStore.GetStoreType() + + if memStoreType != sdk.StoreTypeMemory { + panic(fmt.Sprintf("invalid memory store type; got %d, expected: %d", memStoreType, sdk.StoreTypeMemory)) + } + + prefixStore := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixIndexCapability) + iterator := sdk.KVStorePrefixIterator(prefixStore, nil) + + // initialize the in-memory store for all persisted capabilities + defer iterator.Close() + for ; iterator.Valid(); iterator.Next() { + index := types.IndexFromKey(iterator.Key()) + cap := types.NewCapabilityKey(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 in-memory store. + memStore.Set(types.FwdCapabilityKey(owner.Module, cap), []byte(owner.Name)) + + // Set the reverse mapping between the module and capability name and the + // capability in the in-memory store. + memStore.Set(types.RevCapabilityKey(owner.Module, owner.Name), k.cdc.MustMarshalBinaryBare(cap)) + } + } + + k.sealed = true +} + // 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 index. The From 4d40d4d55194d5e58726c0a6c6ba3fb6539791ac Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 20 Mar 2020 10:58:07 -0400 Subject: [PATCH 09/29] Update simapp --- simapp/app.go | 43 ++++++++++++++++++++++--------------------- x/capability/alias.go | 2 ++ 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/simapp/app.go b/simapp/app.go index 663f23a7415e..69d6d230d459 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -18,6 +18,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth/ante" "github.com/cosmos/cosmos-sdk/x/auth/vesting" "github.com/cosmos/cosmos-sdk/x/bank" + "github.com/cosmos/cosmos-sdk/x/capability" "github.com/cosmos/cosmos-sdk/x/crisis" distr "github.com/cosmos/cosmos-sdk/x/distribution" "github.com/cosmos/cosmos-sdk/x/evidence" @@ -115,20 +116,21 @@ type SimApp struct { subspaces map[string]params.Subspace // keepers - AccountKeeper auth.AccountKeeper - BankKeeper bank.Keeper - SupplyKeeper supply.Keeper - StakingKeeper staking.Keeper - SlashingKeeper slashing.Keeper - MintKeeper mint.Keeper - DistrKeeper distr.Keeper - GovKeeper gov.Keeper - CrisisKeeper crisis.Keeper - UpgradeKeeper upgrade.Keeper - ParamsKeeper params.Keeper - IBCKeeper ibc.Keeper - EvidenceKeeper evidence.Keeper - TransferKeeper transfer.Keeper + AccountKeeper auth.AccountKeeper + BankKeeper bank.Keeper + CapabilityKeeper capability.Keeper + SupplyKeeper supply.Keeper + StakingKeeper staking.Keeper + SlashingKeeper slashing.Keeper + MintKeeper mint.Keeper + DistrKeeper distr.Keeper + GovKeeper gov.Keeper + CrisisKeeper crisis.Keeper + UpgradeKeeper upgrade.Keeper + ParamsKeeper params.Keeper + IBCKeeper ibc.Keeper + EvidenceKeeper evidence.Keeper + TransferKeeper transfer.Keeper // the module manager mm *module.Manager @@ -153,12 +155,10 @@ func NewSimApp( bam.MainStoreKey, auth.StoreKey, bank.StoreKey, staking.StoreKey, supply.StoreKey, mint.StoreKey, distr.StoreKey, slashing.StoreKey, gov.StoreKey, params.StoreKey, ibc.StoreKey, upgrade.StoreKey, - evidence.StoreKey, transfer.StoreKey, + evidence.StoreKey, transfer.StoreKey, capability.StoreKey, ) tkeys := sdk.NewTransientStoreKeys(params.TStoreKey) - - // TODO: - // memKeys := sdk.NewMemoryStoreKeys(...) + memKeys := sdk.NewMemoryStoreKeys(capability.MemStoreKey) app := &SimApp{ BaseApp: bApp, @@ -188,6 +188,9 @@ func NewSimApp( app.BankKeeper = bank.NewBaseKeeper( app.cdc, keys[bank.StoreKey], app.AccountKeeper, app.subspaces[bank.ModuleName], app.BlacklistedAccAddrs(), ) + app.CapabilityKeeper = capability.NewKeeper( + app.cdc, keys[capability.StoreKey], memKeys[capability.MemStoreKey], + ) app.SupplyKeeper = supply.NewKeeper( app.cdc, keys[supply.StoreKey], app.AccountKeeper, app.BankKeeper, maccPerms, ) @@ -304,9 +307,7 @@ func NewSimApp( // initialize stores app.MountKVStores(keys) app.MountTransientStores(tkeys) - - // TODO: - // app.MountMemoryStores(memKeys) + app.MountMemoryStores(memKeys) // initialize BaseApp app.SetInitChainer(app.InitChainer) diff --git a/x/capability/alias.go b/x/capability/alias.go index 4bdd24ca80ac..3066ca7068dc 100644 --- a/x/capability/alias.go +++ b/x/capability/alias.go @@ -31,6 +31,8 @@ var ( ) type ( + Keeper = keeper.Keeper + ScopedKeeper = keeper.ScopedKeeper Capability = types.Capability CapabilityKey = types.CapabilityKey CapabilityOwners = types.CapabilityOwners From 4c3e8efbf112759467afbf5c81e0e4b822612c42 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 20 Mar 2020 11:05:13 -0400 Subject: [PATCH 10/29] Implement GetCapability --- x/capability/keeper/keeper.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/x/capability/keeper/keeper.go b/x/capability/keeper/keeper.go index 4d3acd7495fd..19783188b744 100644 --- a/x/capability/keeper/keeper.go +++ b/x/capability/keeper/keeper.go @@ -195,6 +195,24 @@ func (sk ScopedKeeper) ClaimCapability(ctx sdk.Context, cap types.Capability, na return nil } +// GetCapability allows a module to fetch a capability which it previously claimed +// by name. The module is not allowed to retrieve capabilities which it does not +// own. If another module claims a capability, the previously owning module will +// no longer be able to claim it. +func (sk ScopedKeeper) GetCapability(ctx sdk.Context, name string) (types.Capability, bool) { + memStore := ctx.KVStore(sk.memKey) + + bz := memStore.Get(types.RevCapabilityKey(sk.module, name)) + if len(bz) == 0 { + return nil, false + } + + var cap types.Capability + sk.cdc.MustUnmarshalBinaryBare(bz, &cap) + + return cap, true +} + func (sk ScopedKeeper) addOwner(ctx sdk.Context, cap types.Capability, name string) error { prefixStore := prefix.NewStore(ctx.KVStore(sk.storeKey), types.KeyPrefixIndexCapability) indexKey := types.IndexToKey(cap.GetIndex()) From 772d0f69c267011d5b298e01b907bc2f3687dffa Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 20 Mar 2020 11:10:32 -0400 Subject: [PATCH 11/29] Add logging for new and claimed caps --- x/capability/keeper/keeper.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/x/capability/keeper/keeper.go b/x/capability/keeper/keeper.go index 19783188b744..2459e1117052 100644 --- a/x/capability/keeper/keeper.go +++ b/x/capability/keeper/keeper.go @@ -3,6 +3,8 @@ package keeper import ( "fmt" + "github.com/tendermint/tendermint/libs/log" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" @@ -153,6 +155,7 @@ func (sk ScopedKeeper) NewCapability(ctx sdk.Context, name string) (types.Capabi // capability in the in-memory store. memStore.Set(types.RevCapabilityKey(sk.module, name), sk.cdc.MustMarshalBinaryBare(cap)) + logger(ctx).Info("created new capability", "module", sk.module, "name", name) return cap, nil } @@ -192,6 +195,7 @@ func (sk ScopedKeeper) ClaimCapability(ctx sdk.Context, cap types.Capability, na // capability in the in-memory store. memStore.Set(types.RevCapabilityKey(sk.module, name), sk.cdc.MustMarshalBinaryBare(cap)) + logger(ctx).Info("claimed capability", "module", sk.module, "name", name, "capability", cap.GetIndex()) return nil } @@ -234,3 +238,7 @@ func (sk ScopedKeeper) addOwner(ctx sdk.Context, cap types.Capability, name stri prefixStore.Set(indexKey, sk.cdc.MustMarshalBinaryBare(capOwners)) return nil } + +func logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) +} From d8f0ee21c230b8d8e2c81c9c9a827c6e59278067 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 20 Mar 2020 11:16:49 -0400 Subject: [PATCH 12/29] Call InitializeAndSeal in SimApp --- simapp/app.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/simapp/app.go b/simapp/app.go index 69d6d230d459..2e90595de2af 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -327,6 +327,12 @@ func NewSimApp( } } + // Initialize and seal the capability keeper so all persistent capabilities + // are loaded in-memory and prevent any further modules from creating scoped + // sub-keepers. + ctx := app.BaseApp.NewContext(true, abci.Header{}) + app.CapabilityKeeper.InitializeAndSeal(ctx) + return app } From e989574861e7aa0bf11e1b57f8391bf5f66d847d Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 20 Mar 2020 13:08:07 -0400 Subject: [PATCH 13/29] Update keeper semantics + unit tests --- simapp/app.go | 17 ++++- store/mem/store.go | 22 +++++- store/rootmulti/store.go | 4 +- store/types/store.go | 21 ++++++ x/capability/keeper/keeper.go | 14 ++-- x/capability/keeper/keeper_test.go | 60 +++++++++++++++ x/capability/module.go | 117 +++++++++++++++++++++++++++++ x/capability/types/codec.go | 3 + x/capability/types/keys.go | 4 + 9 files changed, 247 insertions(+), 15 deletions(-) create mode 100644 x/capability/keeper/keeper_test.go create mode 100644 x/capability/module.go diff --git a/simapp/app.go b/simapp/app.go index 2e90595de2af..6430378b92a2 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -54,6 +54,7 @@ var ( supply.AppModuleBasic{}, genutil.AppModuleBasic{}, bank.AppModuleBasic{}, + capability.AppModuleBasic{}, staking.AppModuleBasic{}, mint.AppModuleBasic{}, distr.AppModuleBasic{}, @@ -109,8 +110,9 @@ type SimApp struct { invCheckPeriod uint // keys to access the substores - keys map[string]*sdk.KVStoreKey - tkeys map[string]*sdk.TransientStoreKey + keys map[string]*sdk.KVStoreKey + tkeys map[string]*sdk.TransientStoreKey + memKeys map[string]*sdk.MemoryStoreKey // subspaces subspaces map[string]params.Subspace @@ -118,7 +120,7 @@ type SimApp struct { // keepers AccountKeeper auth.AccountKeeper BankKeeper bank.Keeper - CapabilityKeeper capability.Keeper + CapabilityKeeper *capability.Keeper SupplyKeeper supply.Keeper StakingKeeper staking.Keeper SlashingKeeper slashing.Keeper @@ -166,6 +168,7 @@ func NewSimApp( invCheckPeriod: invCheckPeriod, keys: keys, tkeys: tkeys, + memKeys: memKeys, subspaces: make(map[string]params.Subspace), } @@ -256,6 +259,7 @@ func NewSimApp( genutil.NewAppModule(app.AccountKeeper, app.StakingKeeper, app.BaseApp.DeliverTx), auth.NewAppModule(app.AccountKeeper), bank.NewAppModule(app.BankKeeper, app.AccountKeeper), + capability.NewAppModule(*app.CapabilityKeeper), crisis.NewAppModule(&app.CrisisKeeper), supply.NewAppModule(app.SupplyKeeper, app.BankKeeper, app.AccountKeeper), gov.NewAppModule(app.GovKeeper, app.AccountKeeper, app.BankKeeper, app.SupplyKeeper), @@ -403,6 +407,13 @@ func (app *SimApp) GetTKey(storeKey string) *sdk.TransientStoreKey { return app.tkeys[storeKey] } +// GetMemKey returns the MemoryStoreKey for the provided store key. +// +// NOTE: This is solely to be used for testing purposes. +func (app *SimApp) GetMemKey(storeKey string) *sdk.MemoryStoreKey { + return app.memKeys[storeKey] +} + // GetSubspace returns a param subspace for a given module name. // // NOTE: This is solely to be used for testing purposes. diff --git a/store/mem/store.go b/store/mem/store.go index b66eb8414817..2a436ee15424 100644 --- a/store/mem/store.go +++ b/store/mem/store.go @@ -1,9 +1,13 @@ package mem import ( + "io" + dbm "github.com/tendermint/tm-db" + "github.com/cosmos/cosmos-sdk/store/cachekv" "github.com/cosmos/cosmos-sdk/store/dbadapter" + "github.com/cosmos/cosmos-sdk/store/tracekv" "github.com/cosmos/cosmos-sdk/store/types" ) @@ -27,13 +31,23 @@ func NewStoreWithDB(db *dbm.MemDB) *Store { } // GetStoreType returns the Store's type. -func (ts *Store) GetStoreType() types.StoreType { +func (s Store) GetStoreType() types.StoreType { return types.StoreTypeMemory } +// CacheWrap cache wraps the underlying store. +func (s Store) CacheWrap() types.CacheWrap { + return cachekv.NewStore(s) +} + +// CacheWrapWithTrace implements KVStore. +func (s Store) CacheWrapWithTrace(w io.Writer, tc types.TraceContext) types.CacheWrap { + return cachekv.NewStore(tracekv.NewStore(s, w, tc)) +} + // Commit performs a no-op as entries are persistent between commitments. -func (ts *Store) Commit() (id types.CommitID) { return } +func (s *Store) Commit() (id types.CommitID) { return } // nolint -func (ts *Store) SetPruning(pruning types.PruningOptions) {} -func (ts *Store) LastCommitID() (id types.CommitID) { return } +func (s *Store) SetPruning(pruning types.PruningOptions) {} +func (s Store) LastCommitID() (id types.CommitID) { return } diff --git a/store/rootmulti/store.go b/store/rootmulti/store.go index 0edbff7fee8d..98a5486aba8a 100644 --- a/store/rootmulti/store.go +++ b/store/rootmulti/store.go @@ -160,17 +160,19 @@ func (rs *Store) loadVersion(ver int64, upgrades *types.StoreUpgrades) error { for _, storeInfo := range cInfo.StoreInfos { infos[storeInfo.Name] = storeInfo } + lastCommitID = cInfo.CommitID() } // load each Store (note this doesn't panic on unmounted keys now) var newStores = make(map[types.StoreKey]types.CommitKVStore) + for key, storeParams := range rs.storesParams { - // Load it store, err := rs.loadCommitStoreFromParams(key, rs.getCommitID(infos, key.Name()), storeParams) if err != nil { return fmt.Errorf("failed to load Store: %v", err) } + newStores[key] = store // If it was deleted, remove all data diff --git a/store/types/store.go b/store/types/store.go index 5de396651dd6..b19fa7a07423 100644 --- a/store/types/store.go +++ b/store/types/store.go @@ -269,6 +269,27 @@ const ( StoreTypeMemory ) +func (st StoreType) String() string { + switch st { + case StoreTypeMulti: + return "StoreTypeMulti" + + case StoreTypeDB: + return "StoreTypeDB" + + case StoreTypeIAVL: + return "StoreTypeIAVL" + + case StoreTypeTransient: + return "StoreTypeTransient" + + case StoreTypeMemory: + return "StoreTypeMemory" + } + + return "unknown store type" +} + //---------------------------------------- // Keys for accessing substores diff --git a/x/capability/keeper/keeper.go b/x/capability/keeper/keeper.go index 2459e1117052..0434d03beafa 100644 --- a/x/capability/keeper/keeper.go +++ b/x/capability/keeper/keeper.go @@ -47,8 +47,8 @@ type ( } ) -func NewKeeper(cdc *codec.Codec, storeKey, memKey sdk.StoreKey) Keeper { - return Keeper{ +func NewKeeper(cdc *codec.Codec, storeKey, memKey sdk.StoreKey) *Keeper { + return &Keeper{ cdc: cdc, storeKey: storeKey, memKey: memKey, @@ -60,7 +60,7 @@ func NewKeeper(cdc *codec.Codec, storeKey, memKey sdk.StoreKey) Keeper { // ScopeToModule attempts to create and return a ScopedKeeper for a given module // by name. It will panic if the keeper is already sealed or if the module name // already has a ScopedKeeper. -func (k Keeper) ScopeToModule(moduleName string) ScopedKeeper { +func (k *Keeper) ScopeToModule(moduleName string) ScopedKeeper { if k.sealed { panic("cannot scope to module via a sealed capability keeper") } @@ -83,7 +83,7 @@ func (k Keeper) ScopeToModule(moduleName string) ScopedKeeper { // in-memory store and seals the keeper to prevent further modules from creating // a scoped keeper. InitializeAndSeal must be called once after the application // state is loaded. -func (k Keeper) InitializeAndSeal(ctx sdk.Context) { +func (k *Keeper) InitializeAndSeal(ctx sdk.Context) { if k.sealed { panic("cannot initialize and seal an already sealed capability keeper") } @@ -92,7 +92,7 @@ func (k Keeper) InitializeAndSeal(ctx sdk.Context) { memStoreType := memStore.GetStoreType() if memStoreType != sdk.StoreTypeMemory { - panic(fmt.Sprintf("invalid memory store type; got %d, expected: %d", memStoreType, sdk.StoreTypeMemory)) + panic(fmt.Sprintf("invalid memory store type; got %s, expected: %s", memStoreType, sdk.StoreTypeMemory)) } prefixStore := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixIndexCapability) @@ -104,8 +104,8 @@ func (k Keeper) InitializeAndSeal(ctx sdk.Context) { index := types.IndexFromKey(iterator.Key()) cap := types.NewCapabilityKey(index) - var capOwners *types.CapabilityOwners - k.cdc.MustUnmarshalBinaryBare(iterator.Value(), capOwners) + 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 diff --git a/x/capability/keeper/keeper_test.go b/x/capability/keeper/keeper_test.go new file mode 100644 index 000000000000..82e94ee2550d --- /dev/null +++ b/x/capability/keeper/keeper_test.go @@ -0,0 +1,60 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/bank" + "github.com/cosmos/cosmos-sdk/x/capability" + "github.com/cosmos/cosmos-sdk/x/capability/keeper" +) + +type KeeperTestSuite struct { + suite.Suite + + ctx sdk.Context + keeper *keeper.Keeper + app *simapp.SimApp +} + +func (suite *KeeperTestSuite) SetupTest() { + checkTx := false + app := simapp.Setup(checkTx) + + // create new keeper so we can define custom scoping before init and seal + keeper := keeper.NewKeeper( + app.Codec(), app.GetKey(capability.StoreKey), app.GetMemKey(capability.MemStoreKey), + ) + + suite.ctx = app.BaseApp.NewContext(checkTx, abci.Header{Height: 1}) + suite.keeper = keeper + suite.app = app +} + +func (suite *KeeperTestSuite) TestInitializeAndSeal() { + sk := suite.keeper.ScopeToModule(bank.ModuleName) + + cap, err := sk.NewCapability(suite.ctx, "transfer") + suite.Require().NoError(err) + suite.Require().NotNil(cap) + + suite.Require().NotPanics(func() { + suite.keeper.InitializeAndSeal(suite.ctx) + }) + + got, ok := sk.GetCapability(suite.ctx, "transfer") + suite.Require().True(ok) + suite.Require().Equal(cap, got) + + suite.Require().Panics(func() { + suite.keeper.InitializeAndSeal(suite.ctx) + }) +} + +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(KeeperTestSuite)) +} diff --git a/x/capability/module.go b/x/capability/module.go new file mode 100644 index 000000000000..da94e00b84d3 --- /dev/null +++ b/x/capability/module.go @@ -0,0 +1,117 @@ +package capability + +import ( + "encoding/json" + + "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" + + "github.com/gorilla/mux" + "github.com/spf13/cobra" + abci "github.com/tendermint/tendermint/abci/types" +) + +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} + + // TODO: Enable simulation once concrete types are defined. + // _ module.AppModuleSimulation = AppModuleSimulation{} +) + +// ---------------------------------------------------------------------------- +// AppModuleBasic +// ---------------------------------------------------------------------------- + +// AppModuleBasic implements the AppModuleBasic interface for the capability module. +type AppModuleBasic struct { +} + +func NewAppModuleBasic() AppModuleBasic { + return AppModuleBasic{} +} + +// Name returns the capability module's name. +func (AppModuleBasic) Name() string { + return ModuleName +} + +// RegisterCodec registers the capability module's types to the provided codec. +func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { + RegisterCodec(cdc) +} + +// DefaultGenesis returns the capability module's default genesis state. +func (AppModuleBasic) DefaultGenesis() json.RawMessage { return []byte("{}") } + +// ValidateGenesis performs genesis state validation for the capability module. +func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { return nil } + +// 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. +func (AppModuleBasic) GetQueryCmd(_ *codec.Codec) *cobra.Command { return nil } + +// ---------------------------------------------------------------------------- +// AppModule +// ---------------------------------------------------------------------------- + +// AppModule implements the AppModule interface for the capability module. +type AppModule struct { + AppModuleBasic + + keeper Keeper +} + +func NewAppModule(keeper Keeper) AppModule { + return AppModule{ + AppModuleBasic: NewAppModuleBasic(), + keeper: keeper, + } +} + +// Name returns the capability module's name. +func (am AppModule) Name() string { + return am.AppModuleBasic.Name() +} + +// Route returns the capability module's message routing key. +func (AppModule) Route() string { return "" } + +// QuerierRoute returns the capability module's query routing key. +func (AppModule) QuerierRoute() string { return "" } + +// NewHandler returns the capability module's message Handler. +func (am AppModule) NewHandler() sdk.Handler { return nil } + +// NewQuerierHandler returns the capability module's Querier. +func (am AppModule) NewQuerierHandler() sdk.Querier { return nil } + +// RegisterInvariants registers the capability module's invariants. +func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {} + +// InitGenesis performs the capability module's genesis initialization It returns +// no validator updates. +func (am AppModule) InitGenesis(ctx sdk.Context, bz json.RawMessage) []abci.ValidatorUpdate { + return []abci.ValidatorUpdate{} +} + +// ExportGenesis returns the capability module's exported genesis state as raw JSON bytes. +func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { + return am.DefaultGenesis() +} + +// BeginBlock executes all ABCI BeginBlock logic respective to the capability module. +func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) {} + +// EndBlock executes all ABCI EndBlock logic respective to the capability module. It +// returns no validator updates. +func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { + return []abci.ValidatorUpdate{} +} diff --git a/x/capability/types/codec.go b/x/capability/types/codec.go index 7b7cea97dcf4..7b483557f2ae 100644 --- a/x/capability/types/codec.go +++ b/x/capability/types/codec.go @@ -12,6 +12,9 @@ var ModuleCdc = codec.New() // capability module. func RegisterCodec(cdc *codec.Codec) { cdc.RegisterInterface((*Capability)(nil), nil) + cdc.RegisterConcrete(&CapabilityKey{}, "cosmos-sdk/CapabilityKey", nil) + cdc.RegisterConcrete(Owner{}, "cosmos-sdk/Owner", nil) + cdc.RegisterConcrete(&CapabilityOwners{}, "cosmos-sdk/CapabilityOwners", nil) } // RegisterCapabilityTypeCodec registers an external concrete Capability type diff --git a/x/capability/types/keys.go b/x/capability/types/keys.go index 6c1a6ddf2fae..99c32b59e919 100644 --- a/x/capability/types/keys.go +++ b/x/capability/types/keys.go @@ -48,5 +48,9 @@ func IndexToKey(index uint64) []byte { // IndexFromKey returns an index from a call to IndexToKey for a given capability // index. func IndexFromKey(key []byte) uint64 { + if len(key) == 0 { + return 0 + } + return binary.LittleEndian.Uint64(key) } From e9298322476d44f54e7ef133c42455d6daaeb2a6 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 20 Mar 2020 13:12:40 -0400 Subject: [PATCH 14/29] Use big endian --- types/utils.go | 10 ++++++++++ x/capability/types/keys.go | 13 ++++--------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/types/utils.go b/types/utils.go index 2a6add6b45b4..ef62f51ae2ed 100644 --- a/types/utils.go +++ b/types/utils.go @@ -49,6 +49,16 @@ func Uint64ToBigEndian(i uint64) []byte { return b } +// BigEndianToUint64 returns an uint64 from big endian encoded bytes. If encoding +// is empty, zero is returned. +func BigEndianToUint64(bz []byte) uint64 { + if len(bz) == 0 { + return 0 + } + + return binary.BigEndian.Uint64(bz) +} + // Slight modification of the RFC3339Nano but it right pads all zeros and drops the time zone info const SortableTimeFormat = "2006-01-02T15:04:05.000000000" diff --git a/x/capability/types/keys.go b/x/capability/types/keys.go index 99c32b59e919..1db474bc635e 100644 --- a/x/capability/types/keys.go +++ b/x/capability/types/keys.go @@ -1,8 +1,9 @@ package types import ( - "encoding/binary" "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" ) const ( @@ -40,17 +41,11 @@ func FwdCapabilityKey(module string, cap Capability) []byte { // IndexToKey returns bytes to be used as a key for a given capability index. func IndexToKey(index uint64) []byte { - buf := make([]byte, 8) - binary.LittleEndian.PutUint64(buf, index) - return buf + return sdk.Uint64ToBigEndian(index) } // IndexFromKey returns an index from a call to IndexToKey for a given capability // index. func IndexFromKey(key []byte) uint64 { - if len(key) == 0 { - return 0 - } - - return binary.LittleEndian.Uint64(key) + return sdk.BigEndianToUint64(key) } From 3a95ff8c741226e73166c5a16998e4a2c3d04368 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 20 Mar 2020 13:28:49 -0400 Subject: [PATCH 15/29] More unit tests --- x/capability/keeper/keeper_test.go | 62 +++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 6 deletions(-) diff --git a/x/capability/keeper/keeper_test.go b/x/capability/keeper/keeper_test.go index 82e94ee2550d..4f837b3caae6 100644 --- a/x/capability/keeper/keeper_test.go +++ b/x/capability/keeper/keeper_test.go @@ -1,6 +1,7 @@ package keeper_test import ( + "fmt" "testing" "github.com/stretchr/testify/suite" @@ -11,6 +12,8 @@ import ( "github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/capability" "github.com/cosmos/cosmos-sdk/x/capability/keeper" + "github.com/cosmos/cosmos-sdk/x/capability/types" + "github.com/cosmos/cosmos-sdk/x/staking" ) type KeeperTestSuite struct { @@ -38,23 +41,70 @@ func (suite *KeeperTestSuite) SetupTest() { func (suite *KeeperTestSuite) TestInitializeAndSeal() { sk := suite.keeper.ScopeToModule(bank.ModuleName) - cap, err := sk.NewCapability(suite.ctx, "transfer") - suite.Require().NoError(err) - suite.Require().NotNil(cap) + caps := make([]types.Capability, 5) + + for i := range caps { + cap, err := sk.NewCapability(suite.ctx, fmt.Sprintf("transfer-%d", i)) + suite.Require().NoError(err) + suite.Require().NotNil(cap) + suite.Require().Equal(uint64(i), cap.GetIndex()) + + caps[i] = cap + } suite.Require().NotPanics(func() { suite.keeper.InitializeAndSeal(suite.ctx) }) - got, ok := sk.GetCapability(suite.ctx, "transfer") - suite.Require().True(ok) - suite.Require().Equal(cap, got) + for i, cap := range caps { + got, ok := sk.GetCapability(suite.ctx, fmt.Sprintf("transfer-%d", i)) + suite.Require().True(ok) + suite.Require().Equal(cap, got) + suite.Require().Equal(uint64(i), got.GetIndex()) + } suite.Require().Panics(func() { suite.keeper.InitializeAndSeal(suite.ctx) }) } +func (suite *KeeperTestSuite) TestNewCapability() { + sk := suite.keeper.ScopeToModule(bank.ModuleName) + + cap, err := sk.NewCapability(suite.ctx, "transfer") + suite.Require().NoError(err) + suite.Require().NotNil(cap) + + got, ok := sk.GetCapability(suite.ctx, "transfer") + suite.Require().True(ok) + suite.Require().Equal(cap, got) + + cap, err = sk.NewCapability(suite.ctx, "transfer") + suite.Require().Error(err) + suite.Require().Nil(cap) +} + +func (suite *KeeperTestSuite) TestAuthenticateCapability() { + sk1 := suite.keeper.ScopeToModule(bank.ModuleName) + sk2 := suite.keeper.ScopeToModule(staking.ModuleName) + + cap1, err := sk1.NewCapability(suite.ctx, "transfer") + suite.Require().NoError(err) + suite.Require().NotNil(cap1) + + cap2, err := sk2.NewCapability(suite.ctx, "bond") + suite.Require().NoError(err) + suite.Require().NotNil(cap2) + + suite.Require().True(sk1.AuthenticateCapability(suite.ctx, cap1, "transfer")) + suite.Require().False(sk1.AuthenticateCapability(suite.ctx, cap1, "invalid")) + suite.Require().False(sk1.AuthenticateCapability(suite.ctx, cap2, "transfer")) + + suite.Require().True(sk2.AuthenticateCapability(suite.ctx, cap2, "bond")) + suite.Require().False(sk2.AuthenticateCapability(suite.ctx, cap2, "invalid")) + suite.Require().False(sk2.AuthenticateCapability(suite.ctx, cap1, "bond")) +} + func TestKeeperTestSuite(t *testing.T) { suite.Run(t, new(KeeperTestSuite)) } From 9e463688d693105c321bf95465d84087417e5e98 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 20 Mar 2020 14:01:13 -0400 Subject: [PATCH 16/29] Increase keeper test coverage --- x/capability/keeper/keeper.go | 6 ++++-- x/capability/keeper/keeper_test.go | 32 ++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/x/capability/keeper/keeper.go b/x/capability/keeper/keeper.go index 0434d03beafa..60a8007a5a01 100644 --- a/x/capability/keeper/keeper.go +++ b/x/capability/keeper/keeper.go @@ -104,7 +104,7 @@ func (k *Keeper) InitializeAndSeal(ctx sdk.Context) { index := types.IndexFromKey(iterator.Key()) cap := types.NewCapabilityKey(index) - var capOwners types.CapabilityOwners + var capOwners *types.CapabilityOwners k.cdc.MustUnmarshalBinaryBare(iterator.Value(), &capOwners) for _, owner := range capOwners.Owners { @@ -180,6 +180,8 @@ func (sk ScopedKeeper) AuthenticateCapability(ctx sdk.Context, cap types.Capabil // it will return an error. Otherwise, it will also set a forward and reverse index // for the capability and capability name. func (sk ScopedKeeper) ClaimCapability(ctx sdk.Context, cap types.Capability, name string) error { + // TODO: Do we need to authenticate the capability against the name first? + // update capability owner set if err := sk.addOwner(ctx, cap, name); err != nil { return err @@ -227,7 +229,7 @@ func (sk ScopedKeeper) addOwner(ctx sdk.Context, cap types.Capability, name stri if len(bz) == 0 { capOwners = types.NewCapabilityOwners() } else { - sk.cdc.MustUnmarshalBinaryBare(bz, capOwners) + sk.cdc.MustUnmarshalBinaryBare(bz, &capOwners) } if err := capOwners.Set(types.NewOwner(sk.module, name)); err != nil { diff --git a/x/capability/keeper/keeper_test.go b/x/capability/keeper/keeper_test.go index 4f837b3caae6..515d78b1a137 100644 --- a/x/capability/keeper/keeper_test.go +++ b/x/capability/keeper/keeper_test.go @@ -66,6 +66,10 @@ func (suite *KeeperTestSuite) TestInitializeAndSeal() { suite.Require().Panics(func() { suite.keeper.InitializeAndSeal(suite.ctx) }) + + suite.Require().Panics(func() { + _ = suite.keeper.ScopeToModule(staking.ModuleName) + }) } func (suite *KeeperTestSuite) TestNewCapability() { @@ -79,6 +83,10 @@ func (suite *KeeperTestSuite) TestNewCapability() { suite.Require().True(ok) suite.Require().Equal(cap, got) + got, ok = sk.GetCapability(suite.ctx, "invalid") + suite.Require().False(ok) + suite.Require().Nil(got) + cap, err = sk.NewCapability(suite.ctx, "transfer") suite.Require().Error(err) suite.Require().Nil(cap) @@ -103,6 +111,30 @@ func (suite *KeeperTestSuite) TestAuthenticateCapability() { suite.Require().True(sk2.AuthenticateCapability(suite.ctx, cap2, "bond")) suite.Require().False(sk2.AuthenticateCapability(suite.ctx, cap2, "invalid")) suite.Require().False(sk2.AuthenticateCapability(suite.ctx, cap1, "bond")) + + badCap := types.NewCapabilityKey(100) + suite.Require().False(sk1.AuthenticateCapability(suite.ctx, badCap, "transfer")) + suite.Require().False(sk2.AuthenticateCapability(suite.ctx, badCap, "bond")) +} + +func (suite *KeeperTestSuite) TestClaimCapability() { + sk1 := suite.keeper.ScopeToModule(bank.ModuleName) + sk2 := suite.keeper.ScopeToModule(staking.ModuleName) + + cap, err := sk1.NewCapability(suite.ctx, "transfer") + suite.Require().NoError(err) + suite.Require().NotNil(cap) + + suite.Require().Error(sk1.ClaimCapability(suite.ctx, cap, "transfer")) + suite.Require().NoError(sk2.ClaimCapability(suite.ctx, cap, "transfer")) + + got, ok := sk1.GetCapability(suite.ctx, "transfer") + suite.Require().True(ok) + suite.Require().Equal(cap, got) + + got, ok = sk2.GetCapability(suite.ctx, "transfer") + suite.Require().True(ok) + suite.Require().Equal(cap, got) } func TestKeeperTestSuite(t *testing.T) { From abc15d88afd8a1413378c909c7518a1969a4bffb Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 20 Mar 2020 14:02:55 -0400 Subject: [PATCH 17/29] Remove TODO --- x/capability/keeper/keeper.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/x/capability/keeper/keeper.go b/x/capability/keeper/keeper.go index 60a8007a5a01..00e65599f531 100644 --- a/x/capability/keeper/keeper.go +++ b/x/capability/keeper/keeper.go @@ -180,8 +180,6 @@ func (sk ScopedKeeper) AuthenticateCapability(ctx sdk.Context, cap types.Capabil // it will return an error. Otherwise, it will also set a forward and reverse index // for the capability and capability name. func (sk ScopedKeeper) ClaimCapability(ctx sdk.Context, cap types.Capability, name string) error { - // TODO: Do we need to authenticate the capability against the name first? - // update capability owner set if err := sk.addOwner(ctx, cap, name); err != nil { return err From 74d09b9aeb2991df7061f1cabf13dd8be22a8956 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 20 Mar 2020 14:37:24 -0400 Subject: [PATCH 18/29] Add module doc --- x/capability/docs/README.md | 63 +++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 x/capability/docs/README.md diff --git a/x/capability/docs/README.md b/x/capability/docs/README.md new file mode 100644 index 000000000000..9a259e45ac0a --- /dev/null +++ b/x/capability/docs/README.md @@ -0,0 +1,63 @@ +# x/capability + +## Abstract + +`x/capability` is an implementation of a Cosmos SDK module, per [ADR 003](./../../../docs/architecture/adr-003-dynamic-capability-store.md), +that allows for provisioning, tracking, and authenticating multi-owner capabilities +at runtime. + +The keeper maintains two states: persistent and ephemeral in-memory. The persistent +store maintains a globally unique auto-incrementing index and a mapping from +capability index to a set of capability owners that are defined as a module and +capability name tuple. The in-memory ephemeral state keeps track of the actual +capabilities with both forward and reverse indexes. The forward index maps +module name and capability tuples to the capability name. The reverse index maps +between the module and capability name and the capability itself. + +The keeper allows the creation of "scoped" sub-keepers which are tied to a particular +module by name. Scoped keepers must be created at application initialization and +passed to modules, which can then use them to claim capabilities they receive and +retrieve capabilities which they own by name, in addition to creating new capabilities +& authenticating capabilities passed by other modules. + +The keeper provides no other core functionality that can be found in other modules +like queriers, REST and CLI handlers, and genesis state. + +## Initialization + +During application initialization, the keeper must be instantiated with a persistent +store key and an in-memory store key. + +```go +type App struct { + // ... + + capabilityKeeper *capability.Keeper +} + +func NewApp(...) *App { + // ... + + app.capabilityKeeper = capability.NewKeeper(codec, persistentStoreKey, memStoreKey) +} +``` + +After the keeper is created, it can be used to create scoped sub-keepers which +are passed to other modules that can create, authenticate, and claim capabilities. +After all the necessary scoped keepers are created and the state is loaded, the +main capability keeper must be initialized and sealed to populate the in-memory +state and to prevent further scoped keepers from being created. + +```go +func NewApp(...) *App { + // ... + + // Initialize and seal the capability keeper so all persistent capabilities + // are loaded in-memory and prevent any further modules from creating scoped + // sub-keepers. + ctx := app.BaseApp.NewContext(true, abci.Header{}) + app.capabilityKeeper.InitializeAndSeal(ctx) + + return app +} +``` From 083494cb58ebd375fbff4db182616f3e16e26617 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 20 Mar 2020 15:12:02 -0400 Subject: [PATCH 19/29] Update doc --- x/capability/docs/README.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/x/capability/docs/README.md b/x/capability/docs/README.md index 9a259e45ac0a..63a67a001603 100644 --- a/x/capability/docs/README.md +++ b/x/capability/docs/README.md @@ -61,3 +61,27 @@ func NewApp(...) *App { return app } ``` + +## Capabilities + +Capabilities are multi-owner. A scoped keeper can create a capability via `NewCapability` +which creates a new unique, unforgeable object-capability reference. The newly +created capability is automatically persisted; the calling module need not call +`ClaimCapability`. Calling `NewCapability` will create the capability with the +calling module and name as a tuple to be treated the capabilities first owner. + +Capabilities can be claimed by other modules which add them as owners. `ClaimCapability` +allows a module to claim a capability key which it has received from another +module so that future `GetCapability` calls will succeed. `ClaimCapability` MUST +be called if a module which receives a capability wishes to access it by name in +the future. Again, capabilities are multi-owner, so if multiple modules have a +single Capability reference, they will all own it. + +`AuthenticateCapability` can be called by any module to check that a capability +does in fact correspond to a particular name (the name can be un-trusted user input) +with which the calling module previously associated it. + +`GetCapability` allows a module to fetch a capability which it has previously +claimed by name. The module is not allowed to retrieve capabilities which it does +not own. If another module claims a capability, the previously owning module will +no longer be able to claim it. From d6b5f5b92ea21098f8ed40d0c276b804af4dbb16 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 23 Mar 2020 11:37:17 +0100 Subject: [PATCH 20/29] Apply suggestions from code review Co-Authored-By: Aditya --- store/mem/store.go | 4 ++-- x/capability/docs/README.md | 9 ++++----- x/capability/keeper/keeper.go | 5 ++--- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/store/mem/store.go b/store/mem/store.go index 2a436ee15424..8987a37adff9 100644 --- a/store/mem/store.go +++ b/store/mem/store.go @@ -16,8 +16,8 @@ var ( _ types.Committer = (*Store)(nil) ) -// Store implements an in-memory only KVStore. Entries are peresistent between -// commits and thus between blocks. +// Store implements an in-memory only KVStore. Entries are persisted between +// commits and thus between blocks. State in Memory store is not committed as part of app state but maintained privately by each node type Store struct { dbadapter.Store } diff --git a/x/capability/docs/README.md b/x/capability/docs/README.md index 63a67a001603..395e5b69c84f 100644 --- a/x/capability/docs/README.md +++ b/x/capability/docs/README.md @@ -10,7 +10,7 @@ The keeper maintains two states: persistent and ephemeral in-memory. The persist store maintains a globally unique auto-incrementing index and a mapping from capability index to a set of capability owners that are defined as a module and capability name tuple. The in-memory ephemeral state keeps track of the actual -capabilities with both forward and reverse indexes. The forward index maps +capabilities, represented as addresses in local memory, with both forward and reverse indexes. The forward index maps module name and capability tuples to the capability name. The reverse index maps between the module and capability name and the capability itself. @@ -18,7 +18,7 @@ The keeper allows the creation of "scoped" sub-keepers which are tied to a parti module by name. Scoped keepers must be created at application initialization and passed to modules, which can then use them to claim capabilities they receive and retrieve capabilities which they own by name, in addition to creating new capabilities -& authenticating capabilities passed by other modules. +& authenticating capabilities passed by other modules. A scoped keeper cannot escape its scope, so a module cannot interfere with or inspect capabilities owned by other modules. The keeper provides no other core functionality that can be found in other modules like queriers, REST and CLI handlers, and genesis state. @@ -75,7 +75,7 @@ allows a module to claim a capability key which it has received from another module so that future `GetCapability` calls will succeed. `ClaimCapability` MUST be called if a module which receives a capability wishes to access it by name in the future. Again, capabilities are multi-owner, so if multiple modules have a -single Capability reference, they will all own it. +single Capability reference, they will all own it. If a module receives a capability from another module but does not call `ClaimCapability`, it may use it in the executing transaction but will not be able to access it afterwards. `AuthenticateCapability` can be called by any module to check that a capability does in fact correspond to a particular name (the name can be un-trusted user input) @@ -83,5 +83,4 @@ with which the calling module previously associated it. `GetCapability` allows a module to fetch a capability which it has previously claimed by name. The module is not allowed to retrieve capabilities which it does -not own. If another module claims a capability, the previously owning module will -no longer be able to claim it. +not own. diff --git a/x/capability/keeper/keeper.go b/x/capability/keeper/keeper.go index 00e65599f531..3f30be774e2a 100644 --- a/x/capability/keeper/keeper.go +++ b/x/capability/keeper/keeper.go @@ -166,7 +166,7 @@ func (sk ScopedKeeper) NewCapability(ctx sdk.Context, name string) (types.Capabi // true upon success and false upon failure. // // Note, the capability's forward mapping is indexed by a string which should -// contain it's unique memory reference. +// contain its unique memory reference. func (sk ScopedKeeper) AuthenticateCapability(ctx sdk.Context, cap types.Capability, name string) bool { memStore := ctx.KVStore(sk.memKey) @@ -201,8 +201,7 @@ func (sk ScopedKeeper) ClaimCapability(ctx sdk.Context, cap types.Capability, na // GetCapability allows a module to fetch a capability which it previously claimed // by name. The module is not allowed to retrieve capabilities which it does not -// own. If another module claims a capability, the previously owning module will -// no longer be able to claim it. +// own. func (sk ScopedKeeper) GetCapability(ctx sdk.Context, name string) (types.Capability, bool) { memStore := ctx.KVStore(sk.memKey) From c5e9c605e888817fa8b1c78941b3280b6abc85a7 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Mon, 23 Mar 2020 10:49:13 -0400 Subject: [PATCH 21/29] Update NewCapability godoc --- x/capability/keeper/keeper.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/x/capability/keeper/keeper.go b/x/capability/keeper/keeper.go index 3f30be774e2a..3a5be5e7bdf5 100644 --- a/x/capability/keeper/keeper.go +++ b/x/capability/keeper/keeper.go @@ -123,10 +123,13 @@ func (k *Keeper) InitializeAndSeal(ctx sdk.Context) { // 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 index. The -// newly created capability has the module and name tuple set as the initial owner. -// Finally, the global index is incremented along with forward and reverse indexes -// set in the in-memory store. +// Otherwise, a new capability is created with the current global unique index. +// The newly created capability has the module and name tuple set as the initial +// owner. Finally, the global index is incremented along with forward and reverse +// indexes set in the in-memory store. +// +// Note, namespacing is completely local, which is safe since records are prefixed +// with the module name and no two ScopedKeeper can have the same module name. func (sk ScopedKeeper) NewCapability(ctx sdk.Context, name string) (types.Capability, error) { store := ctx.KVStore(sk.storeKey) memStore := ctx.KVStore(sk.memKey) From 0fa35229dfe8d651ac96be5bca291f834138c020 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Mon, 23 Mar 2020 11:06:43 -0400 Subject: [PATCH 22/29] Clarify owner --- x/capability/keeper/keeper.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/x/capability/keeper/keeper.go b/x/capability/keeper/keeper.go index 3a5be5e7bdf5..03bd897a3832 100644 --- a/x/capability/keeper/keeper.go +++ b/x/capability/keeper/keeper.go @@ -124,9 +124,9 @@ func (k *Keeper) InitializeAndSeal(ctx sdk.Context) { // 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. -// The newly created capability has the module and name tuple set as the initial -// owner. Finally, the global index is incremented along with forward and reverse -// indexes set in the in-memory store. +// The newly created capability has the scoped module name and capability name +// tuple set as the initial owner. Finally, the global index is incremented along +// with forward and reverse indexes set in the in-memory store. // // Note, namespacing is completely local, which is safe since records are prefixed // with the module name and no two ScopedKeeper can have the same module name. @@ -177,11 +177,11 @@ func (sk ScopedKeeper) AuthenticateCapability(ctx sdk.Context, cap types.Capabil return string(bz) == name } -// ClaimCapability attempts to claim a given Capability and name tuple. This tuple -// is considered an Owner. It will attempt to add the owner to the persistent -// set of capability owners for the capability index. If the owner already exists, -// it will return an error. Otherwise, it will also set a forward and reverse index -// for the capability and capability name. +// ClaimCapability attempts to claim a given Capability. The provided name and +// the scoped module's name tuple are treated as the owner. It will attempt +// to add the owner to the persistent set of capability owners for the capability +// index. If the owner already exists, it will return an error. Otherwise, it will +// also set a forward and reverse index for the capability and capability name. func (sk ScopedKeeper) ClaimCapability(ctx sdk.Context, cap types.Capability, name string) error { // update capability owner set if err := sk.addOwner(ctx, cap, name); err != nil { From f3a514aa11607b55920e1bf3cdc128e87590fb0a Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Mon, 23 Mar 2020 11:13:10 -0400 Subject: [PATCH 23/29] Add forgery test case to TestAuthenticateCapability --- x/capability/keeper/keeper_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/x/capability/keeper/keeper_test.go b/x/capability/keeper/keeper_test.go index 515d78b1a137..dc52d9b96d5e 100644 --- a/x/capability/keeper/keeper_test.go +++ b/x/capability/keeper/keeper_test.go @@ -100,6 +100,9 @@ func (suite *KeeperTestSuite) TestAuthenticateCapability() { suite.Require().NoError(err) suite.Require().NotNil(cap1) + forgedCap := types.NewCapabilityKey(0) // index should be the same index as the first capability + suite.Require().False(sk2.AuthenticateCapability(suite.ctx, forgedCap, "transfer")) + cap2, err := sk2.NewCapability(suite.ctx, "bond") suite.Require().NoError(err) suite.Require().NotNil(cap2) From 1db27ad73706f14831162a6129cfc7ff55e22f81 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Mon, 23 Mar 2020 11:17:12 -0400 Subject: [PATCH 24/29] Format doc --- x/capability/docs/README.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/x/capability/docs/README.md b/x/capability/docs/README.md index 395e5b69c84f..a280743d7edf 100644 --- a/x/capability/docs/README.md +++ b/x/capability/docs/README.md @@ -10,15 +10,16 @@ The keeper maintains two states: persistent and ephemeral in-memory. The persist store maintains a globally unique auto-incrementing index and a mapping from capability index to a set of capability owners that are defined as a module and capability name tuple. The in-memory ephemeral state keeps track of the actual -capabilities, represented as addresses in local memory, with both forward and reverse indexes. The forward index maps -module name and capability tuples to the capability name. The reverse index maps -between the module and capability name and the capability itself. +capabilities, represented as addresses in local memory, with both forward and reverse indexes. +The forward index maps module name and capability tuples to the capability name. The +reverse index maps between the module and capability name and the capability itself. The keeper allows the creation of "scoped" sub-keepers which are tied to a particular module by name. Scoped keepers must be created at application initialization and passed to modules, which can then use them to claim capabilities they receive and retrieve capabilities which they own by name, in addition to creating new capabilities -& authenticating capabilities passed by other modules. A scoped keeper cannot escape its scope, so a module cannot interfere with or inspect capabilities owned by other modules. +& authenticating capabilities passed by other modules. A scoped keeper cannot escape its scope, +so a module cannot interfere with or inspect capabilities owned by other modules. The keeper provides no other core functionality that can be found in other modules like queriers, REST and CLI handlers, and genesis state. @@ -75,7 +76,9 @@ allows a module to claim a capability key which it has received from another module so that future `GetCapability` calls will succeed. `ClaimCapability` MUST be called if a module which receives a capability wishes to access it by name in the future. Again, capabilities are multi-owner, so if multiple modules have a -single Capability reference, they will all own it. If a module receives a capability from another module but does not call `ClaimCapability`, it may use it in the executing transaction but will not be able to access it afterwards. +single Capability reference, they will all own it. If a module receives a capability +from another module but does not call `ClaimCapability`, it may use it in the executing +transaction but will not be able to access it afterwards. `AuthenticateCapability` can be called by any module to check that a capability does in fact correspond to a particular name (the name can be un-trusted user input) From 290344c9e03cb5ab21f6606b884faedb20030c39 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Mon, 23 Mar 2020 12:38:48 -0400 Subject: [PATCH 25/29] Update ADR --- docs/architecture/adr-003-dynamic-capability-store.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/architecture/adr-003-dynamic-capability-store.md b/docs/architecture/adr-003-dynamic-capability-store.md index 7fd438fd931c..c1dbf4741f5c 100644 --- a/docs/architecture/adr-003-dynamic-capability-store.md +++ b/docs/architecture/adr-003-dynamic-capability-store.md @@ -171,8 +171,7 @@ func (sck ScopedCapabilityKeeper) ClaimCapability(ctx Context, capability Capabi } ``` -`GetCapability` allows a module to fetch a capability which it has previously claimed by name. The module is not allowed to retrieve capabilities which it does not own. If another module -claims a capability, the previously owning module will no longer be able to claim it. +`GetCapability` allows a module to fetch a capability which it has previously claimed by name. The module is not allowed to retrieve capabilities which it does not own. ```golang func (sck ScopedCapabilityKeeper) GetCapability(ctx Context, name string) (Capability, error) { From 76a5c3e5e2715232f7d591b60115dd7649d607a7 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Mon, 23 Mar 2020 12:40:33 -0400 Subject: [PATCH 26/29] Explicitly take pointer in FwdCapabilityKey --- x/capability/types/keys.go | 2 +- x/capability/types/keys_test.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/x/capability/types/keys.go b/x/capability/types/keys.go index 1db474bc635e..76726015246b 100644 --- a/x/capability/types/keys.go +++ b/x/capability/types/keys.go @@ -36,7 +36,7 @@ func RevCapabilityKey(module, name string) []byte { // FwdCapabilityKey returns a forward lookup key for a given module and capability // reference. func FwdCapabilityKey(module string, cap Capability) []byte { - return []byte(fmt.Sprintf("%s/fwd/%s", module, cap)) + return []byte(fmt.Sprintf("%s/fwd/%p", module, cap)) } // IndexToKey returns bytes to be used as a key for a given capability index. diff --git a/x/capability/types/keys_test.go b/x/capability/types/keys_test.go index b4a0168912b2..fb380888e90f 100644 --- a/x/capability/types/keys_test.go +++ b/x/capability/types/keys_test.go @@ -16,14 +16,14 @@ func TestRevCapabilityKey(t *testing.T) { func TestFwdCapabilityKey(t *testing.T) { cap := types.NewCapabilityKey(23) - expected := []byte(fmt.Sprintf("bank/fwd/%s", cap)) + expected := []byte(fmt.Sprintf("bank/fwd/%p", cap)) require.Equal(t, expected, types.FwdCapabilityKey("bank", cap)) } func TestIndexToKey(t *testing.T) { - require.Equal(t, []byte{0x5a, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, types.IndexToKey(3162)) + require.Equal(t, []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x5a}, types.IndexToKey(3162)) } func TestIndexFromKey(t *testing.T) { - require.Equal(t, uint64(3162), types.IndexFromKey([]byte{0x5a, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0})) + require.Equal(t, uint64(3162), types.IndexFromKey([]byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x5a})) } From 82afa7da02a820e940d5dd6c2983e9cf33715f47 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Mon, 23 Mar 2020 14:25:15 -0400 Subject: [PATCH 27/29] Update set to be logn --- x/capability/types/types.go | 21 ++++++++++++++------- x/capability/types/types_test.go | 27 +++++++++++++++++++++------ 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/x/capability/types/types.go b/x/capability/types/types.go index e6da5535d0d9..afa5b79b4e93 100644 --- a/x/capability/types/types.go +++ b/x/capability/types/types.go @@ -2,6 +2,7 @@ package types import ( "fmt" + "sort" "gopkg.in/yaml.v2" @@ -72,15 +73,21 @@ func NewCapabilityOwners() *CapabilityOwners { return &CapabilityOwners{Owners: make([]Owner, 0)} } -// Set attempts to add a given owner to the CapabilityOwners. If the owner already -// exists, an error will be returned. +// Set attempts to add a given owner to the CapabilityOwners. If the owner +// already exists, an error will be returned. Set runs in O(log n) average time +// and O(n) in the worst case. func (co *CapabilityOwners) Set(owner Owner) error { - for _, o := range co.Owners { - if o.Key() == owner.Key() { - return sdkerrors.Wrapf(ErrOwnerClaimed, owner.String()) - } + // find smallest index s.t. co.Owners[i] >= owner in O(log n) time + i := sort.Search(len(co.Owners), func(i int) bool { return co.Owners[i].Key() >= owner.Key() }) + if i < len(co.Owners) && co.Owners[i].Key() == owner.Key() { + // owner already exists at co.Owners[i] + return sdkerrors.Wrapf(ErrOwnerClaimed, owner.String()) } - co.Owners = append(co.Owners, owner) + // owner does not exist in the set of owners, so we insert at position i + co.Owners = append(co.Owners, Owner{}) // expand by 1 in amortized O(1) / O(n) worst case + copy(co.Owners[i+1:], co.Owners[i:]) + co.Owners[i] = owner + return nil } diff --git a/x/capability/types/types_test.go b/x/capability/types/types_test.go index 74da351d7ac3..0c1ba6e3d3bc 100644 --- a/x/capability/types/types_test.go +++ b/x/capability/types/types_test.go @@ -2,6 +2,7 @@ package types_test import ( "fmt" + "sort" "testing" "github.com/stretchr/testify/require" @@ -24,11 +25,25 @@ func TestOwner(t *testing.T) { func TestCapabilityOwners(t *testing.T) { co := types.NewCapabilityOwners() - o1 := types.NewOwner("bank", "send") - o2 := types.NewOwner("slashing", "slash") - require.NoError(t, co.Set(o1)) - require.Error(t, co.Set(o1)) - require.NoError(t, co.Set(o2)) - require.Equal(t, []types.Owner{o1, o2}, co.Owners) + owners := make([]types.Owner, 1024) + for i := range owners { + var owner types.Owner + + if i%2 == 0 { + owner = types.NewOwner("bank", fmt.Sprintf("send-%d", i)) + } else { + owner = types.NewOwner("slashing", fmt.Sprintf("slash-%d", i)) + } + + owners[i] = owner + require.NoError(t, co.Set(owner)) + } + + sort.Slice(owners, func(i, j int) bool { return owners[i].Key() < owners[j].Key() }) + require.Equal(t, owners, co.Owners) + + for _, owner := range owners { + require.Error(t, co.Set(owner)) + } } From a8efe0fd05450ceb452d64ee3bf43c2a12f2d28a Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Mon, 23 Mar 2020 14:37:08 -0400 Subject: [PATCH 28/29] Update app module --- x/capability/module.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/x/capability/module.go b/x/capability/module.go index da94e00b84d3..1ee89573c4b4 100644 --- a/x/capability/module.go +++ b/x/capability/module.go @@ -44,10 +44,10 @@ func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { } // DefaultGenesis returns the capability module's default genesis state. -func (AppModuleBasic) DefaultGenesis() json.RawMessage { return []byte("{}") } +func (AppModuleBasic) DefaultGenesis(_ codec.JSONMarshaler) json.RawMessage { return []byte("{}") } // ValidateGenesis performs genesis state validation for the capability module. -func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { return nil } +func (AppModuleBasic) ValidateGenesis(_ codec.JSONMarshaler, _ json.RawMessage) error { return nil } // RegisterRESTRoutes registers the capability module's REST service handlers. func (a AppModuleBasic) RegisterRESTRoutes(_ context.CLIContext, _ *mux.Router) {} @@ -94,24 +94,24 @@ func (am AppModule) NewHandler() sdk.Handler { return nil } func (am AppModule) NewQuerierHandler() sdk.Querier { return nil } // RegisterInvariants registers the capability module's invariants. -func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {} +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, bz json.RawMessage) []abci.ValidatorUpdate { +func (am AppModule) InitGenesis(_ sdk.Context, _ codec.JSONMarshaler, _ json.RawMessage) []abci.ValidatorUpdate { return []abci.ValidatorUpdate{} } // ExportGenesis returns the capability module's exported genesis state as raw JSON bytes. -func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { - return am.DefaultGenesis() +func (am AppModule) ExportGenesis(_ sdk.Context, cdc codec.JSONMarshaler) json.RawMessage { + return am.DefaultGenesis(cdc) } // BeginBlock executes all ABCI BeginBlock logic respective to the capability module. -func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) {} +func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} // EndBlock executes all ABCI EndBlock logic respective to the capability module. It // returns no validator updates. -func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { +func (am AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { return []abci.ValidatorUpdate{} } From 9b4f448c57d5e00bba3511f56f6d263f615445cd Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Mon, 23 Mar 2020 14:50:45 -0400 Subject: [PATCH 29/29] Lint --- store/mem/store.go | 2 +- x/capability/alias.go | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/store/mem/store.go b/store/mem/store.go index 8987a37adff9..3a1cdd617a0a 100644 --- a/store/mem/store.go +++ b/store/mem/store.go @@ -26,7 +26,7 @@ func NewStore() *Store { return NewStoreWithDB(dbm.NewMemDB()) } -func NewStoreWithDB(db *dbm.MemDB) *Store { +func NewStoreWithDB(db *dbm.MemDB) *Store { // nolint: interfacer return &Store{Store: dbadapter.Store{DB: db}} } diff --git a/x/capability/alias.go b/x/capability/alias.go index 3066ca7068dc..3aa949677e50 100644 --- a/x/capability/alias.go +++ b/x/capability/alias.go @@ -5,15 +5,16 @@ import ( "github.com/cosmos/cosmos-sdk/x/capability/types" ) -// nolint // DONTCOVER +// nolint const ( ModuleName = types.ModuleName StoreKey = types.StoreKey MemStoreKey = types.MemStoreKey ) +// nolint var ( NewKeeper = keeper.NewKeeper NewCapabilityKey = types.NewCapabilityKey @@ -30,6 +31,7 @@ var ( NewCapabilityOwners = types.NewCapabilityOwners ) +// nolint type ( Keeper = keeper.Keeper ScopedKeeper = keeper.ScopedKeeper