Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ADR-003: Dynamic Capabilities #5828

Merged
merged 30 commits into from
Mar 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
af10e05
Implement in-memory KVStore
alexanderbez Mar 18, 2020
29d68cf
Start keeper and types
alexanderbez Mar 18, 2020
191df59
Add codec
alexanderbez Mar 19, 2020
cb6fc18
Add keys logic
alexanderbez Mar 19, 2020
447221b
Update types
alexanderbez Mar 19, 2020
970e786
Update keeper
alexanderbez Mar 19, 2020
0f950e1
Implement NewCapability
alexanderbez Mar 19, 2020
2a3dde9
Implement InitializeAndSeal
alexanderbez Mar 20, 2020
4d40d4d
Update simapp
alexanderbez Mar 20, 2020
4c3e8ef
Implement GetCapability
alexanderbez Mar 20, 2020
772d0f6
Add logging for new and claimed caps
alexanderbez Mar 20, 2020
d8f0ee2
Call InitializeAndSeal in SimApp
alexanderbez Mar 20, 2020
e989574
Update keeper semantics + unit tests
alexanderbez Mar 20, 2020
e929832
Use big endian
alexanderbez Mar 20, 2020
3a95ff8
More unit tests
alexanderbez Mar 20, 2020
9e46368
Increase keeper test coverage
alexanderbez Mar 20, 2020
abc15d8
Remove TODO
alexanderbez Mar 20, 2020
74d09b9
Add module doc
alexanderbez Mar 20, 2020
083494c
Update doc
alexanderbez Mar 20, 2020
d6b5f5b
Apply suggestions from code review
cwgoes Mar 23, 2020
c5e9c60
Update NewCapability godoc
alexanderbez Mar 23, 2020
0fa3522
Clarify owner
alexanderbez Mar 23, 2020
f3a514a
Add forgery test case to TestAuthenticateCapability
alexanderbez Mar 23, 2020
1db27ad
Format doc
alexanderbez Mar 23, 2020
290344c
Update ADR
alexanderbez Mar 23, 2020
76a5c3e
Explicitly take pointer in FwdCapabilityKey
alexanderbez Mar 23, 2020
82afa7d
Update set to be logn
alexanderbez Mar 23, 2020
84707a8
Merge branch 'ibc-alpha' into bez/adr-003-dynamic-cap
alexanderbez Mar 23, 2020
a8efe0f
Update app module
alexanderbez Mar 23, 2020
9b4f448
Lint
alexanderbez Mar 23, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions baseapp/baseapp.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,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) {
Expand Down
3 changes: 1 addition & 2 deletions docs/architecture/adr-003-dynamic-capability-store.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
58 changes: 41 additions & 17 deletions simapp/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/auth/ante"
"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"
Expand Down Expand Up @@ -54,6 +55,7 @@ var (
supply.AppModuleBasic{},
genutil.AppModuleBasic{},
bank.AppModuleBasic{},
capability.AppModuleBasic{},
staking.AppModuleBasic{},
mint.AppModuleBasic{},
distr.AppModuleBasic{},
Expand Down Expand Up @@ -98,27 +100,29 @@ 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whats the difference between "memStoreKey" and "transientStoreKey"?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The memory store is in-memory only but persists across blocks, whereas the transient store is reset after each Commit, I believe.

Copy link
Contributor Author

@alexanderbez alexanderbez Mar 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What @cwgoes stated is correct. Think of the in-memory store as a simple cache/map. Since both are really in-memory and the only semantic difference is on commit, perhaps it makes sense to rename in-memory store (future PR).

memKeys map[string]*sdk.MemoryStoreKey

// subspaces
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
Expand All @@ -145,16 +149,18 @@ 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)
memKeys := sdk.NewMemoryStoreKeys(capability.MemStoreKey)

app := &SimApp{
BaseApp: bApp,
cdc: cdc,
invCheckPeriod: invCheckPeriod,
keys: keys,
tkeys: tkeys,
memKeys: memKeys,
subspaces: make(map[string]params.Subspace),
}

Expand All @@ -177,6 +183,9 @@ func NewSimApp(
app.BankKeeper = bank.NewBaseKeeper(
appCodec, 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(
appCodec, keys[supply.StoreKey], app.AccountKeeper, app.BankKeeper, maccPerms,
)
Expand Down Expand Up @@ -242,6 +251,7 @@ func NewSimApp(
genutil.NewAppModule(app.AccountKeeper, app.StakingKeeper, app.BaseApp.DeliverTx),
auth.NewAppModule(app.AccountKeeper, app.SupplyKeeper),
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),
Expand Down Expand Up @@ -293,6 +303,7 @@ func NewSimApp(
// initialize stores
app.MountKVStores(keys)
app.MountTransientStores(tkeys)
app.MountMemoryStores(memKeys)

// initialize BaseApp
app.SetInitChainer(app.InitChainer)
Expand All @@ -312,6 +323,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
}

Expand Down Expand Up @@ -382,6 +399,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.
Expand Down
39 changes: 39 additions & 0 deletions store/mem/mem_test.go
Original file line number Diff line number Diff line change
@@ -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))
}
53 changes: 53 additions & 0 deletions store/mem/store.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
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"
)

var (
_ types.KVStore = (*Store)(nil)
_ types.Committer = (*Store)(nil)
)

// 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
}

func NewStore() *Store {
return NewStoreWithDB(dbm.NewMemDB())
}

func NewStoreWithDB(db *dbm.MemDB) *Store { // nolint: interfacer
return &Store{Store: dbadapter.Store{DB: db}}
}

// GetStoreType returns the Store's type.
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 (s *Store) Commit() (id types.CommitID) { return }

// nolint
func (s *Store) SetPruning(pruning types.PruningOptions) {}
func (s Store) LastCommitID() (id types.CommitID) { return }
11 changes: 10 additions & 1 deletion store/rootmulti/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,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"
Expand Down Expand Up @@ -169,12 +170,13 @@ func (rs *Store) loadVersion(ver int64, upgrades *types.StoreUpgrades) error {

// 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 errors.Wrap(err, "failed to load store")
}

newStores[key] = store

// If it was deleted, remove all data
Expand Down Expand Up @@ -527,6 +529,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))
}
Expand Down
41 changes: 41 additions & 0 deletions store/types/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,8 +273,30 @@ const (
StoreTypeDB
StoreTypeIAVL
StoreTypeTransient
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

Expand Down Expand Up @@ -333,6 +355,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 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd add a Type() sdk.StoreKeyType method to the sdk.StoreKey interface

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
Expand Down
15 changes: 15 additions & 0 deletions types/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ const (
StoreTypeDB = types.StoreTypeDB
StoreTypeIAVL = types.StoreTypeIAVL
StoreTypeTransient = types.StoreTypeTransient
StoreTypeMemory = types.StoreTypeMemory
)

// nolint - reexport
Expand All @@ -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.
Expand All @@ -99,6 +101,7 @@ func NewKVStoreKeys(names ...string) map[string]*KVStoreKey {
for _, name := range names {
keys[name] = NewKVStoreKey(name)
}

return keys
}

Expand All @@ -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
}

Expand Down
Loading