From a54952261b5eff46f7c604c3b7357491773d17e9 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Tue, 31 Aug 2021 20:00:06 +0800 Subject: [PATCH 01/78] [WIP] refactor multistore => root store simapp, baseapp and dependencies update db interface options refactor app.CloseStore() LoadLatestVersion => Init ... --- baseapp/abci.go | 22 +-- baseapp/abci_test.go | 25 +-- baseapp/baseapp.go | 223 +++++++++++----------- baseapp/baseapp_test.go | 308 ++++++++++++++----------------- baseapp/custom_txhandler_test.go | 22 +-- baseapp/grpcrouter_test.go | 4 +- baseapp/options.go | 91 +++++---- baseapp/state.go | 10 +- baseapp/test_helpers.go | 2 +- baseapp/util_test.go | 6 +- docs/core/upgrade.md | 6 +- server/types/app.go | 6 +- server/util.go | 4 +- simapp/app.go | 81 +++++--- simapp/app_test.go | 19 +- simapp/sim_bench_test.go | 3 +- simapp/sim_test.go | 21 ++- simapp/simd/cmd/root.go | 13 +- simapp/test_helpers.go | 13 +- simapp/utils.go | 11 +- snapshots/store.go | 50 +++-- testutil/context.go | 32 ++-- testutil/network/network.go | 4 +- types/context.go | 42 +++-- types/store.go | 28 ++- x/auth/middleware/run_msgs.go | 24 +++ x/params/types/subspace.go | 3 +- x/upgrade/types/storeloader.go | 20 +- 28 files changed, 587 insertions(+), 506 deletions(-) diff --git a/baseapp/abci.go b/baseapp/abci.go index 9f9a80b91fcf..ec80acc84b45 100644 --- a/baseapp/abci.go +++ b/baseapp/abci.go @@ -34,7 +34,7 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC if req.InitialHeight > 1 { app.initialHeight = req.InitialHeight initHeader = tmproto.Header{ChainID: req.ChainId, Height: req.InitialHeight, Time: req.Time} - err := app.cms.SetInitialVersion(req.InitialHeight) + err := app.store.SetInitialVersion(uint64(req.InitialHeight)) if err != nil { panic(err) } @@ -104,7 +104,7 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC // Info implements the ABCI interface. func (app *BaseApp) Info(req abci.RequestInfo) abci.ResponseInfo { - lastCommitID := app.cms.LastCommitID() + lastCommitID := app.store.LastCommitID() return abci.ResponseInfo{ Data: app.name, @@ -136,8 +136,8 @@ func (app *BaseApp) FilterPeerByID(info string) abci.ResponseQuery { // BeginBlock implements the ABCI application interface. func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeginBlock) { - if app.cms.TracingEnabled() { - app.cms.SetTracingContext(sdk.TraceContext( + if app.store.TracingEnabled() { + app.store.SetTraceContext(sdk.TraceContext( map[string]interface{}{"blockHeight": req.Header.Height}, )) } @@ -203,7 +203,7 @@ func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeg func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBlock) { if app.deliverState.ms.TracingEnabled() { - app.deliverState.ms = app.deliverState.ms.SetTracingContext(nil).(sdk.CacheMultiStore) + app.deliverState.ms.SetTraceContext(nil) } if app.endBlocker != nil { @@ -306,9 +306,9 @@ func (app *BaseApp) Commit() (res abci.ResponseCommit) { // Write the DeliverTx state into branched storage and commit the MultiStore. // The write to the DeliverTx state writes all state transitions to the root - // MultiStore (app.cms) so when Commit() is called is persists those values. + // MultiStore (app.store) so when Commit() is called it persists those values. app.deliverState.ms.Write() - commitID := app.cms.Commit() + commitID := app.store.Commit() app.logger.Info("commit synced", "commit", fmt.Sprintf("%X", commitID)) // Reset the Check state to the latest committed. @@ -637,7 +637,7 @@ func (app *BaseApp) createQueryContext(height int64, prove bool) (sdk.Context, e ) } - cacheMS, err := app.cms.CacheMultiStoreWithVersion(height) + cacheMS, err := app.store.GetVersion(height) if err != nil { return sdk.Context{}, sdkerrors.Wrapf( @@ -711,7 +711,7 @@ func (app *BaseApp) GetBlockRetentionHeight(commitHeight int64) int64 { // Define the state pruning offset, i.e. the block offset at which the // underlying logical database is persisted to disk. - statePruningOffset := int64(app.cms.GetPruning().KeepEvery) + statePruningOffset := int64(app.store.GetPruning().KeepEvery) if statePruningOffset > 0 { if commitHeight > statePruningOffset { v := commitHeight - (commitHeight % statePruningOffset) @@ -789,12 +789,12 @@ func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) abci.Res func handleQueryStore(app *BaseApp, path []string, req abci.RequestQuery) abci.ResponseQuery { // "/store" prefix for store queries - queryable, ok := app.cms.(sdk.Queryable) + queryable, ok := app.store.(sdk.Queryable) if !ok { return sdkerrors.QueryResult(sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "multistore doesn't support queries"), app.trace) } - req.Path = "/" + strings.Join(path[1:], "/") + req.Path = "/" + strings.Join(path[1:], "/") // = /store/main/key -> /main/key if req.Height <= 1 && req.Prove { return sdkerrors.QueryResult( diff --git a/baseapp/abci_test.go b/baseapp/abci_test.go index 338c7c4d0cb3..39f3c464721d 100644 --- a/baseapp/abci_test.go +++ b/baseapp/abci_test.go @@ -7,15 +7,14 @@ import ( "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" tmprototypes "github.com/tendermint/tendermint/proto/tendermint/types" - dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/db/memdb" sdk "github.com/cosmos/cosmos-sdk/types" ) func TestGetBlockRentionHeight(t *testing.T) { logger := defaultLogger() - db := dbm.NewMemDB() name := t.Name() testCases := map[string]struct { @@ -25,20 +24,20 @@ func TestGetBlockRentionHeight(t *testing.T) { expected int64 }{ "defaults": { - bapp: baseapp.NewBaseApp(name, logger, db), + bapp: baseapp.NewBaseApp(name, logger, memdb.NewDB(), nil), maxAgeBlocks: 0, commitHeight: 499000, expected: 0, }, "pruning unbonding time only": { - bapp: baseapp.NewBaseApp(name, logger, db, baseapp.SetMinRetainBlocks(1)), + bapp: baseapp.NewBaseApp(name, logger, memdb.NewDB(), nil, baseapp.SetMinRetainBlocks(1)), maxAgeBlocks: 362880, commitHeight: 499000, expected: 136120, }, "pruning iavl snapshot only": { bapp: baseapp.NewBaseApp( - name, logger, db, + name, logger, memdb.NewDB(), nil, baseapp.SetPruning(sdk.PruningOptions{KeepEvery: 10000}), baseapp.SetMinRetainBlocks(1), ), @@ -48,7 +47,7 @@ func TestGetBlockRentionHeight(t *testing.T) { }, "pruning state sync snapshot only": { bapp: baseapp.NewBaseApp( - name, logger, db, + name, logger, memdb.NewDB(), nil, baseapp.SetSnapshotInterval(50000), baseapp.SetSnapshotKeepRecent(3), baseapp.SetMinRetainBlocks(1), @@ -59,7 +58,7 @@ func TestGetBlockRentionHeight(t *testing.T) { }, "pruning min retention only": { bapp: baseapp.NewBaseApp( - name, logger, db, + name, logger, memdb.NewDB(), nil, baseapp.SetMinRetainBlocks(400000), ), maxAgeBlocks: 0, @@ -68,7 +67,7 @@ func TestGetBlockRentionHeight(t *testing.T) { }, "pruning all conditions": { bapp: baseapp.NewBaseApp( - name, logger, db, + name, logger, memdb.NewDB(), nil, baseapp.SetPruning(sdk.PruningOptions{KeepEvery: 10000}), baseapp.SetMinRetainBlocks(400000), baseapp.SetSnapshotInterval(50000), baseapp.SetSnapshotKeepRecent(3), @@ -79,7 +78,7 @@ func TestGetBlockRentionHeight(t *testing.T) { }, "no pruning due to no persisted state": { bapp: baseapp.NewBaseApp( - name, logger, db, + name, logger, memdb.NewDB(), nil, baseapp.SetPruning(sdk.PruningOptions{KeepEvery: 10000}), baseapp.SetMinRetainBlocks(400000), baseapp.SetSnapshotInterval(50000), baseapp.SetSnapshotKeepRecent(3), @@ -90,7 +89,7 @@ func TestGetBlockRentionHeight(t *testing.T) { }, "disable pruning": { bapp: baseapp.NewBaseApp( - name, logger, db, + name, logger, memdb.NewDB(), nil, baseapp.SetPruning(sdk.PruningOptions{KeepEvery: 10000}), baseapp.SetMinRetainBlocks(0), baseapp.SetSnapshotInterval(50000), baseapp.SetSnapshotKeepRecent(3), @@ -104,7 +103,8 @@ func TestGetBlockRentionHeight(t *testing.T) { for name, tc := range testCases { tc := tc - tc.bapp.SetParamStore(¶mStore{db: dbm.NewMemDB()}) + tc.bapp.SetParamStore(newParamStore(memdb.NewDB())) + require.NoError(t, tc.bapp.Init()) tc.bapp.InitChain(abci.RequestInitChain{ ConsensusParams: &tmprototypes.ConsensusParams{ Evidence: &tmprototypes.EvidenceParams{ @@ -116,6 +116,7 @@ func TestGetBlockRentionHeight(t *testing.T) { t.Run(name, func(t *testing.T) { require.Equal(t, tc.expected, tc.bapp.GetBlockRetentionHeight(tc.commitHeight)) }) + require.NoError(t, tc.bapp.CloseStore()) } } @@ -125,7 +126,7 @@ func TestBaseAppCreateQueryContextRejectsNegativeHeights(t *testing.T) { t.Parallel() logger := defaultLogger() - db := dbm.NewMemDB() + db := memdb.NewDB() name := t.Name() app := baseapp.NewBaseApp(name, logger, db) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 0f1a1263f5c2..0bd72870cb1b 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -2,19 +2,21 @@ package baseapp import ( "context" - "errors" "fmt" + // "reflect" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/codec/types" + dbm "github.com/cosmos/cosmos-sdk/db" + // dbutil "github.com/cosmos/cosmos-sdk/internal/db" "github.com/cosmos/cosmos-sdk/snapshots" - "github.com/cosmos/cosmos-sdk/store" - "github.com/cosmos/cosmos-sdk/store/rootmulti" - storetypes "github.com/cosmos/cosmos-sdk/store/types" + // "github.com/cosmos/cosmos-sdk/store" + // "github.com/cosmos/cosmos-sdk/store/rootmulti" + stypes "github.com/cosmos/cosmos-sdk/store/v2" + "github.com/cosmos/cosmos-sdk/store/v2/flat" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/tx" ) @@ -34,23 +36,31 @@ type ( // Enum mode for app.runTx runTxMode uint8 - // StoreLoader defines a customizable function to control how we load the CommitMultiStore + // StoreLoader defines a customizable function to control how we load the CommitRootStore // from disk. This is useful for state migration, when loading a datastore written with // an older version of the software. In particular, if a module changed the substore key name // (or removed a substore) between two versions of the software. - StoreLoader func(ms sdk.CommitMultiStore) error + // StoreLoader func(ms sdk.CommitRootStore) error + + // StoreLoader func(initialVersion uint64) (sdk.CommitRootStore, error) + // StoreLoader func(sdk.RootStoreConfig) (sdk.CommitRootStore, error) + // StoreLoader func(*sdk.RootStoreConfig, uint64) (sdk.CommitRootStore, error) + StoreOption func(*sdk.RootStoreConfig, uint64) error + StoreConstructor func(dbm.DBConnection, sdk.RootStoreConfig) (sdk.CommitRootStore, error) ) // BaseApp reflects the ABCI application implementation. type BaseApp struct { // nolint: maligned // initialized on creation - logger log.Logger - name string // application name from abci.Info - db dbm.DB // common DB backend - cms sdk.CommitMultiStore // Main (uncached) state - storeLoader StoreLoader // function to handle store loading, may be overridden with SetStoreLoader() - queryRouter sdk.QueryRouter // router for redirecting query calls - grpcQueryRouter *GRPCQueryRouter // router for redirecting gRPC query calls + logger log.Logger + name string // application name from abci.Info + db dbm.DBConnection + storeCtor StoreConstructor + storeOpts []StoreOption // options to configure root store + store sdk.CommitRootStore // Main (uncached) state + // storeLoader StoreLoader // function to handle store loading + queryRouter sdk.QueryRouter // router for redirecting query calls + grpcQueryRouter *GRPCQueryRouter // router for redirecting gRPC query calls interfaceRegistry types.InterfaceRegistry txHandler tx.Handler // txHandler for {Deliver,Check}Tx and simulations @@ -73,9 +83,6 @@ type BaseApp struct { // nolint: maligned checkState *state // for CheckTx deliverState *state // for DeliverTx - // an inter-block write-through cache provided to the context during deliverState - interBlockCache sdk.MultiStorePersistentCache - // absent validators from begin block voteInfos []abci.VoteInfo @@ -130,33 +137,87 @@ type BaseApp struct { // nolint: maligned abciListeners []ABCIListener } +// OptionOrder represents the required ordering for options that are order dependent +type OptionOrder int + +const ( + OptionOrderDefault = iota + OptionOrderAfterStore +) + +// AppOption is a configuration option for a BaseApp +type AppOption interface { + Apply(*BaseApp) + Order() OptionOrder +} + +type AppOptionFunc func(*BaseApp) + +type AppOptionOrdered struct { + AppOptionFunc + order OptionOrder +} + +func (opt AppOptionOrdered) Order() OptionOrder { return opt.order } + +func (opt AppOptionFunc) Apply(app *BaseApp) { opt(app) } +func (opt AppOptionFunc) Order() OptionOrder { return OptionOrderDefault } + +func (opt StoreOption) Apply(app *BaseApp) { app.storeOpts = append(app.storeOpts, opt) } +func (opt StoreOption) Order() OptionOrder { return OptionOrderDefault } + // NewBaseApp returns a reference to an initialized BaseApp. It accepts a // variadic number of option functions, which act on the BaseApp to set // configuration choices. // // NOTE: The db is used to store the version number for now. func NewBaseApp( - name string, logger log.Logger, db dbm.DB, options ...func(*BaseApp), + name string, + logger log.Logger, + db dbm.DBConnection, + txDecoder sdk.TxDecoder, + options ...AppOption, ) *BaseApp { app := &BaseApp{ logger: logger, name: name, db: db, - cms: store.NewCommitMultiStore(db), - storeLoader: DefaultStoreLoader, + storeCtor: DefaultStoreConstructor, queryRouter: NewQueryRouter(), grpcQueryRouter: NewGRPCQueryRouter(), fauxMerkleMode: false, } + var afterStoreOpts []AppOption for _, option := range options { - option(app) + if int(option.Order()) > int(OptionOrderDefault) { + afterStoreOpts = append(afterStoreOpts, option) + } else { + option.Apply(app) + } } - if app.interBlockCache != nil { - app.cms.SetInterBlockCache(app.interBlockCache) + err := app.loadStore() + if err != nil { + panic(err) + } + for _, option := range afterStoreOpts { + option.Apply(app) } + // // TODO: conditional loading of multistore/rootstore + // if true { + // var err error + // opts := store.RootStoreConfig{PersistentCache: app.interBlockCache} + // app.store, err = store.NewCommitRootStore(db, opts) + // if err != nil { + // panic(err) + // } + // } else { + // // app.store = nil store.NewCommitMultiStore(dbutil.ConnectionAsTmdb(db)) + // app.store = store.MultiStoreAsRootStore(db) + // } + return app } @@ -185,105 +246,47 @@ func (app *BaseApp) Trace() bool { return app.trace } -// MountStores mounts all IAVL or DB stores to the provided keys in the BaseApp -// multistore. -func (app *BaseApp) MountStores(keys ...storetypes.StoreKey) { - for _, key := range keys { - switch key.(type) { - case *storetypes.KVStoreKey: - if !app.fauxMerkleMode { - app.MountStore(key, storetypes.StoreTypeIAVL) - } else { - // StoreTypeDB doesn't do anything upon commit, and it doesn't - // retain history, but it's useful for faster simulation. - app.MountStore(key, storetypes.StoreTypeDB) - } - - case *storetypes.TransientStoreKey: - app.MountStore(key, storetypes.StoreTypeTransient) - - default: - panic(fmt.Sprintf("Unrecognized store key type :%T", key)) - } - } -} - -// MountKVStores mounts all IAVL or DB stores to the provided keys in the -// BaseApp multistore. -func (app *BaseApp) MountKVStores(keys map[string]*storetypes.KVStoreKey) { - for _, key := range keys { - if !app.fauxMerkleMode { - app.MountStore(key, storetypes.StoreTypeIAVL) - } else { - // StoreTypeDB doesn't do anything upon commit, and it doesn't - // retain history, but it's useful for faster simulation. - app.MountStore(key, storetypes.StoreTypeDB) - } +func (app *BaseApp) loadStore() error { + versions, err := app.db.Versions() + if err != nil { + return err } -} - -// MountTransientStores mounts all transient stores to the provided keys in -// the BaseApp multistore. -func (app *BaseApp) MountTransientStores(keys map[string]*storetypes.TransientStoreKey) { - for _, key := range keys { - app.MountStore(key, storetypes.StoreTypeTransient) + latest := versions.Last() + config := flat.DefaultRootStoreConfig() + for _, opt := range app.storeOpts { + opt(&config, latest) } -} - -// MountMemoryStores mounts all in-memory KVStores with the BaseApp's internal -// commit multi-store. -func (app *BaseApp) MountMemoryStores(keys map[string]*storetypes.MemoryStoreKey) { - for _, memKey := range keys { - app.MountStore(memKey, storetypes.StoreTypeMemory) - } -} - -// MountStore mounts a store to the provided key in the BaseApp multistore, -// using the default DB. -func (app *BaseApp) MountStore(key storetypes.StoreKey, typ storetypes.StoreType) { - app.cms.MountStoreWithDB(key, typ, nil) -} - -// LoadLatestVersion loads the latest application version. It will panic if -// called more than once on a running BaseApp. -func (app *BaseApp) LoadLatestVersion() error { - err := app.storeLoader(app.cms) + app.store, err = app.storeCtor(app.db, config) if err != nil { return fmt.Errorf("failed to load latest version: %w", err) } - - return app.init() + return nil } -// DefaultStoreLoader will be used by default and loads the latest version -func DefaultStoreLoader(ms sdk.CommitMultiStore) error { - return ms.LoadLatestVersion() +func (app *BaseApp) CloseStore() error { + return app.store.Close() } -// LoadVersion loads the BaseApp application version. It will panic if called -// more than once on a running baseapp. -func (app *BaseApp) LoadVersion(version int64) error { - err := app.cms.LoadVersion(version) - if err != nil { - return fmt.Errorf("failed to load version %d: %w", version, err) - } - - return app.init() +// DefaultStoreConstructor attempts to create a new store, but loads from existing data if present. +func DefaultStoreConstructor(db dbm.DBConnection, config sdk.RootStoreConfig) (stypes.CommitRootStore, error) { + return flat.NewRootStore(db, config) } // LastCommitID returns the last CommitID of the multistore. -func (app *BaseApp) LastCommitID() storetypes.CommitID { - return app.cms.LastCommitID() +func (app *BaseApp) LastCommitID() stypes.CommitID { + return app.store.LastCommitID() } // LastBlockHeight returns the last committed block height. func (app *BaseApp) LastBlockHeight() int64 { - return app.cms.LastCommitID().Version + return app.store.LastCommitID().Version } -func (app *BaseApp) init() error { +// Init sets the check state and seals the app. It will panic if +// called more than once on a running BaseApp. +func (app *BaseApp) Init() error { if app.sealed { - panic("cannot call initFromMainStore: baseapp already sealed") + panic("cannot call Init: baseapp already sealed") } // needed for the export command which inits from store but never calls initchain @@ -292,11 +295,7 @@ func (app *BaseApp) init() error { // make sure the snapshot interval is a multiple of the pruning KeepEvery interval if app.snapshotManager != nil && app.snapshotInterval > 0 { - rms, ok := app.cms.(*rootmulti.Store) - if !ok { - return errors.New("state sync snapshots require a rootmulti store") - } - pruningOpts := rms.GetPruning() + pruningOpts := app.store.GetPruning() if pruningOpts.KeepEvery > 0 && app.snapshotInterval%pruningOpts.KeepEvery != 0 { return fmt.Errorf( "state sync snapshot interval %v must be a multiple of pruning keep every interval %v", @@ -323,10 +322,6 @@ func (app *BaseApp) setMinRetainBlocks(minRetainBlocks uint64) { app.minRetainBlocks = minRetainBlocks } -func (app *BaseApp) setInterBlockCache(cache sdk.MultiStorePersistentCache) { - app.interBlockCache = cache -} - func (app *BaseApp) setTrace(trace bool) { app.trace = trace } @@ -353,7 +348,7 @@ func (app *BaseApp) IsSealed() bool { return app.sealed } // provided header, and minimum gas prices set. It is set on InitChain and reset // on Commit. func (app *BaseApp) setCheckState(header tmproto.Header) { - ms := app.cms.CacheMultiStore() + ms := app.store.CacheRootStore() app.checkState = &state{ ms: ms, ctx: sdk.NewContext(ms, header, true, app.logger).WithMinGasPrices(app.minGasPrices), @@ -365,7 +360,7 @@ func (app *BaseApp) setCheckState(header tmproto.Header) { // and provided header. It is set on InitChain and BeginBlock and set to nil on // Commit. func (app *BaseApp) setDeliverState(header tmproto.Header) { - ms := app.cms.CacheMultiStore() + ms := app.store.CacheRootStore() app.deliverState = &state{ ms: ms, ctx: sdk.NewContext(ms, header, false, app.logger), diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index bb3d614ece08..3455a85837f8 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -20,16 +20,17 @@ import ( abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" + dbm "github.com/cosmos/cosmos-sdk/db" + "github.com/cosmos/cosmos-sdk/db/memdb" "github.com/cosmos/cosmos-sdk/simapp" "github.com/cosmos/cosmos-sdk/snapshots" snapshottypes "github.com/cosmos/cosmos-sdk/snapshots/types" - "github.com/cosmos/cosmos-sdk/store/rootmulti" - storetypes "github.com/cosmos/cosmos-sdk/store/types" + stypes "github.com/cosmos/cosmos-sdk/store/v2" + "github.com/cosmos/cosmos-sdk/store/v2/flat" "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -50,7 +51,12 @@ func init() { } type paramStore struct { - db *dbm.MemDB + db *memdb.MemDB + rw dbm.DBReadWriter +} + +func newParamStore(db *memdb.MemDB) *paramStore { + return ¶mStore{db: db, rw: db.ReadWriter()} } func (ps *paramStore) Set(_ sdk.Context, key []byte, value interface{}) { @@ -59,11 +65,11 @@ func (ps *paramStore) Set(_ sdk.Context, key []byte, value interface{}) { panic(err) } - ps.db.Set(key, bz) + ps.rw.Set(key, bz) } func (ps *paramStore) Has(_ sdk.Context, key []byte) bool { - ok, err := ps.db.Has(key) + ok, err := ps.rw.Has(key) if err != nil { panic(err) } @@ -72,7 +78,7 @@ func (ps *paramStore) Has(_ sdk.Context, key []byte) bool { } func (ps *paramStore) Get(_ sdk.Context, key []byte, ptr interface{}) { - bz, err := ps.db.Get(key) + bz, err := ps.rw.Get(key) if err != nil { panic(err) } @@ -91,9 +97,9 @@ func defaultLogger() log.Logger { return logger.With("module", "sdk/app") } -func newBaseApp(name string, options ...func(*baseapp.BaseApp)) *baseapp.BaseApp { +func newBaseApp(name string, options ...baseapp.AppOption) *baseapp.BaseApp { logger := defaultLogger() - db := dbm.NewMemDB() + db := memdb.NewDB() codec := codec.NewLegacyAmino() registerTestCodec(codec) return baseapp.NewBaseApp(name, logger, db, options...) @@ -114,15 +120,15 @@ func aminoTxEncoder(cdc *codec.LegacyAmino) sdk.TxEncoder { } // simple one store baseapp -func setupBaseApp(t *testing.T, options ...func(*baseapp.BaseApp)) *baseapp.BaseApp { +func setupBaseApp(t *testing.T, options ...baseapp.AppOption) *baseapp.BaseApp { + options = append(options, baseapp.SetStorePrefixes(capKey1, capKey2)) app := newBaseApp(t.Name(), options...) require.Equal(t, t.Name(), app.Name()) - app.MountStores(capKey1, capKey2) - app.SetParamStore(¶mStore{db: dbm.NewMemDB()}) + app.SetParamStore(newParamStore(memdb.NewDB())) // stores are mounted - err := app.LoadLatestVersion() + err := app.Init() require.Nil(t, err) return app } @@ -143,14 +149,14 @@ func testTxHandler(options middleware.TxHandlerOptions, customTxHandlerMiddlewar } // simple one store baseapp with data and snapshots. Each tx is 1 MB in size (uncompressed). -func setupBaseAppWithSnapshots(t *testing.T, blocks uint, blockTxs int, options ...func(*baseapp.BaseApp)) (*baseapp.BaseApp, func()) { +func setupBaseAppWithSnapshots(t *testing.T, blocks uint, blockTxs int, options ...baseapp.AppOption) (*baseapp.BaseApp, func()) { codec := codec.NewLegacyAmino() registerTestCodec(codec) - routerOpt := func(bapp *baseapp.BaseApp) { + routerOpt := baseapp.AppOptionFunc(func(bapp *baseapp.BaseApp) { legacyRouter := middleware.NewLegacyRouter() legacyRouter.AddRoute(sdk.NewRoute(routeMsgKeyValue, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { kv := msg.(*msgKeyValue) - bapp.CMS().GetCommitKVStore(capKey2).Set(kv.Key, kv.Value) + bapp.Store().GetKVStore(capKey2).Set(kv.Key, kv.Value) any, err := codectypes.NewAnyWithValue(msg) if err != nil { return nil, err @@ -169,13 +175,13 @@ func setupBaseAppWithSnapshots(t *testing.T, blocks uint, blockTxs int, options func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) { return ctx, nil }, ) bapp.SetTxHandler(txHandler) - } + }) snapshotInterval := uint64(2) snapshotTimeout := 1 * time.Minute snapshotDir, err := os.MkdirTemp("", "baseapp") require.NoError(t, err) - snapshotStore, err := snapshots.NewStore(dbm.NewMemDB(), snapshotDir) + snapshotStore, err := snapshots.NewStore(memdb.NewDB(), snapshotDir) require.NoError(t, err) teardown := func() { os.RemoveAll(snapshotDir) @@ -235,9 +241,9 @@ func TestMountStores(t *testing.T) { app := setupBaseApp(t) // check both stores - store1 := app.CMS().GetCommitKVStore(capKey1) + store1 := app.Store().GetKVStore(capKey1) require.NotNil(t, store1) - store2 := app.CMS().GetCommitKVStore(capKey2) + store2 := app.Store().GetKVStore(capKey2) require.NotNil(t, store2) } @@ -290,16 +296,16 @@ func TestConsensusParamsNotNil(t *testing.T) { // Test that LoadLatestVersion actually does. func TestLoadVersion(t *testing.T) { logger := defaultLogger() - pruningOpt := baseapp.SetPruning(storetypes.PruneNothing) - db := dbm.NewMemDB() + pruningOpt := baseapp.SetPruning(stypes.PruneNothing) + db := memdb.NewDB() name := t.Name() app := baseapp.NewBaseApp(name, logger, db, pruningOpt) // make a cap key and mount the store - err := app.LoadLatestVersion() // needed to make stores non-nil + err := app.Init() // needed to make stores non-nil require.Nil(t, err) - emptyCommitID := storetypes.CommitID{} + emptyCommitID := stypes.CommitID{} // fresh store has zero/empty last commit lastHeight := app.LastBlockHeight() @@ -310,85 +316,77 @@ func TestLoadVersion(t *testing.T) { // execute a block, collect commit ID header := tmproto.Header{Height: 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) - res := app.Commit() - commitID1 := storetypes.CommitID{Version: 1, Hash: res.Data} + _ = app.Commit() // execute a block, collect commit ID header = tmproto.Header{Height: 2} app.BeginBlock(abci.RequestBeginBlock{Header: header}) - res = app.Commit() - commitID2 := storetypes.CommitID{Version: 2, Hash: res.Data} + res := app.Commit() + commitID2 := stypes.CommitID{Version: 2, Hash: res.Data} + app.CloseStore() // reload with LoadLatestVersion - app = baseapp.NewBaseApp(name, logger, db, pruningOpt) - app.MountStores() - err = app.LoadLatestVersion() + app = baseapp.NewBaseApp(name, logger, db, nil, pruningOpt) + app.SetStoreConstructor(baseapp.DefaultStoreConstructor) + err = app.Init() require.Nil(t, err) testLoadVersionHelper(t, app, int64(2), commitID2) - - // reload with LoadVersion, see if you can commit the same block and get - // the same result - app = baseapp.NewBaseApp(name, logger, db, pruningOpt) - err = app.LoadVersion(1) - require.Nil(t, err) - testLoadVersionHelper(t, app, int64(1), commitID1) - app.BeginBlock(abci.RequestBeginBlock{Header: header}) - app.Commit() - testLoadVersionHelper(t, app, int64(2), commitID2) } -func useDefaultLoader(app *baseapp.BaseApp) { - app.SetStoreLoader(baseapp.DefaultStoreLoader) -} +var useDefaultConstructor = baseapp.AppOptionFunc(func(app *baseapp.BaseApp) { + app.SetStoreConstructor(baseapp.DefaultStoreConstructor) +}) -func initStore(t *testing.T, db dbm.DB, storeKey string, k, v []byte) { - rs := rootmulti.NewStore(db) - rs.SetPruning(storetypes.PruneNothing) +func initStore(t *testing.T, db dbm.DBConnection, storeKey string, k, v []byte) { key := sdk.NewKVStoreKey(storeKey) - rs.MountStoreWithDB(key, storetypes.StoreTypeIAVL, nil) - err := rs.LoadLatestVersion() - require.Nil(t, err) + opts := flat.DefaultRootStoreConfig() + opts.Pruning = stypes.PruneNothing + require.NoError(t, opts.ReservePrefix(key, stypes.StoreTypePersistent)) + rs, err := flat.NewRootStore(db, opts) + require.NoError(t, err) require.Equal(t, int64(0), rs.LastCommitID().Version) // write some data in substore - kv, _ := rs.GetStore(key).(storetypes.KVStore) + kv := rs.GetKVStore(key) require.NotNil(t, kv) kv.Set(k, v) commitID := rs.Commit() require.Equal(t, int64(1), commitID.Version) + require.NoError(t, rs.Close()) } -func checkStore(t *testing.T, db dbm.DB, ver int64, storeKey string, k, v []byte) { - rs := rootmulti.NewStore(db) - rs.SetPruning(storetypes.PruneDefault) +func checkStore(t *testing.T, db dbm.DBConnection, ver int64, storeKey string, k, v []byte) { + opts := flat.DefaultRootStoreConfig() + opts.Pruning = stypes.PruneNothing key := sdk.NewKVStoreKey(storeKey) - rs.MountStoreWithDB(key, storetypes.StoreTypeIAVL, nil) - err := rs.LoadLatestVersion() - require.Nil(t, err) + require.NoError(t, opts.ReservePrefix(key, stypes.StoreTypePersistent)) + rs, err := baseapp.DefaultStoreConstructor(db, opts) + require.NoError(t, err) require.Equal(t, ver, rs.LastCommitID().Version) // query data in substore - kv, _ := rs.GetStore(key).(storetypes.KVStore) + kv := rs.GetKVStore(key) require.NotNil(t, kv) require.Equal(t, v, kv.Get(k)) + require.NoError(t, rs.Close()) } // Test that we can make commits and then reload old versions. // Test that LoadLatestVersion actually does. -func TestSetLoader(t *testing.T) { +func TestSetConstructor(t *testing.T) { cases := map[string]struct { - setLoader func(*baseapp.BaseApp) - origStoreKey string - loadStoreKey string + setConstructor baseapp.AppOption + origStoreKey string + loadStoreKey string }{ - "don't set loader": { + "don't set constructor": { origStoreKey: "foo", loadStoreKey: "foo", }, - "default loader": { - setLoader: useDefaultLoader, - origStoreKey: "foo", - loadStoreKey: "foo", + "default constructor": { + setConstructor: useDefaultConstructor, + origStoreKey: "foo", + loadStoreKey: "foo", }, } @@ -399,23 +397,26 @@ func TestSetLoader(t *testing.T) { tc := tc t.Run(name, func(t *testing.T) { // prepare a db with some data - db := dbm.NewMemDB() + db := memdb.NewDB() initStore(t, db, tc.origStoreKey, k, v) // load the app with the existing db - opts := []func(*baseapp.BaseApp){baseapp.SetPruning(storetypes.PruneNothing)} - if tc.setLoader != nil { - opts = append(opts, tc.setLoader) + opts := []baseapp.AppOption{ + baseapp.SetPruning(stypes.PruneNothing), + baseapp.SetStorePrefixes(sdk.NewKVStoreKey(tc.loadStoreKey)), + } + if tc.setConstructor != nil { + opts = append(opts, tc.setConstructor) } - app := baseapp.NewBaseApp(t.Name(), defaultLogger(), db, opts...) - app.MountStores(sdk.NewKVStoreKey(tc.loadStoreKey)) - err := app.LoadLatestVersion() + app := baseapp.NewBaseApp(t.Name(), defaultLogger(), db, nil, opts...) + err := app.Init() require.Nil(t, err) // "execute" one block app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: 2}}) res := app.Commit() require.NotNil(t, res.Data) + require.NoError(t, app.CloseStore()) // check db is properly updated checkStore(t, db, 2, tc.loadStoreKey, k, v) @@ -426,11 +427,10 @@ func TestSetLoader(t *testing.T) { func TestVersionSetterGetter(t *testing.T) { logger := defaultLogger() - pruningOpt := baseapp.SetPruning(storetypes.PruneDefault) - db := dbm.NewMemDB() + pruningOpt := baseapp.SetPruning(stypes.PruneDefault) + db := memdb.NewDB() name := t.Name() - app := baseapp.NewBaseApp(name, logger, db, pruningOpt) - + app := baseapp.NewBaseApp(name, logger, db, nil, pruningOpt) require.Equal(t, "", app.Version()) res := app.Query(abci.RequestQuery{Path: "app/version"}) require.True(t, res.IsOK()) @@ -444,58 +444,23 @@ func TestVersionSetterGetter(t *testing.T) { require.Equal(t, versionString, string(res.Value)) } -func TestLoadVersionInvalid(t *testing.T) { - logger := log.NewNopLogger() - pruningOpt := baseapp.SetPruning(storetypes.PruneNothing) - db := dbm.NewMemDB() - name := t.Name() - app := baseapp.NewBaseApp(name, logger, db, pruningOpt) - - err := app.LoadLatestVersion() - require.Nil(t, err) - - // require error when loading an invalid version - err = app.LoadVersion(-1) - require.Error(t, err) - - header := tmproto.Header{Height: 1} - app.BeginBlock(abci.RequestBeginBlock{Header: header}) - res := app.Commit() - commitID1 := storetypes.CommitID{Version: 1, Hash: res.Data} - - // create a new app with the stores mounted under the same cap key - app = baseapp.NewBaseApp(name, logger, db, pruningOpt) - - // require we can load the latest version - err = app.LoadVersion(1) - require.Nil(t, err) - testLoadVersionHelper(t, app, int64(1), commitID1) - - // require error when loading an invalid version - err = app.LoadVersion(2) - require.Error(t, err) -} - func TestLoadVersionPruning(t *testing.T) { logger := log.NewNopLogger() - pruningOptions := storetypes.PruningOptions{ + pruningOptions := stypes.PruningOptions{ KeepRecent: 2, KeepEvery: 3, Interval: 1, } pruningOpt := baseapp.SetPruning(pruningOptions) - db := dbm.NewMemDB() + schemaOpt := baseapp.SetStorePrefixes(sdk.NewKVStoreKey("key1")) + db := memdb.NewDB() name := t.Name() - app := baseapp.NewBaseApp(name, logger, db, pruningOpt) + app := baseapp.NewBaseApp(name, logger, db, nil, pruningOpt, schemaOpt) - // make a cap key and mount the store - capKey := sdk.NewKVStoreKey("key1") - app.MountStores(capKey) - - err := app.LoadLatestVersion() // needed to make stores non-nil + err := app.Init() // needed to make stores non-nil require.Nil(t, err) - emptyCommitID := storetypes.CommitID{} + emptyCommitID := stypes.CommitID{} // fresh store has zero/empty last commit lastHeight := app.LastBlockHeight() @@ -503,36 +468,38 @@ func TestLoadVersionPruning(t *testing.T) { require.Equal(t, int64(0), lastHeight) require.Equal(t, emptyCommitID, lastID) - var lastCommitID storetypes.CommitID + var lastCommitID stypes.CommitID // Commit seven blocks, of which 7 (latest) is kept in addition to 6, 5 // (keep recent) and 3 (keep every). for i := int64(1); i <= 7; i++ { app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: i}}) res := app.Commit() - lastCommitID = storetypes.CommitID{Version: i, Hash: res.Data} + lastCommitID = stypes.CommitID{Version: i, Hash: res.Data} } + // TODO: behavior change - + // CacheMultiStoreWithVersion returned no error on missing version (?) for _, v := range []int64{1, 2, 4} { - _, err = app.CMS().CacheMultiStoreWithVersion(v) - require.NoError(t, err) + _, err = app.Store().GetVersion(v) + require.Error(t, err, "version=%v", v) } for _, v := range []int64{3, 5, 6, 7} { - _, err = app.CMS().CacheMultiStoreWithVersion(v) - require.NoError(t, err) + _, err = app.Store().GetVersion(v) + require.NoError(t, err, "version=%v", v) } + require.NoError(t, app.CloseStore()) - // reload with LoadLatestVersion, check it loads last version - app = baseapp.NewBaseApp(name, logger, db, pruningOpt) - app.MountStores(capKey) + // reload app, check it loads last version + app = baseapp.NewBaseApp(name, logger, db, nil, pruningOpt, schemaOpt) - err = app.LoadLatestVersion() + err = app.Init() require.Nil(t, err) testLoadVersionHelper(t, app, int64(7), lastCommitID) } -func testLoadVersionHelper(t *testing.T, app *baseapp.BaseApp, expectedHeight int64, expectedID storetypes.CommitID) { +func testLoadVersionHelper(t *testing.T, app *baseapp.BaseApp, expectedHeight int64, expectedID stypes.CommitID) { lastHeight := app.LastBlockHeight() lastID := app.LastCommitID() require.Equal(t, expectedHeight, lastHeight) @@ -541,15 +508,13 @@ func testLoadVersionHelper(t *testing.T, app *baseapp.BaseApp, expectedHeight in func TestOptionFunction(t *testing.T) { logger := defaultLogger() - db := dbm.NewMemDB() - bap := baseapp.NewBaseApp("starting name", logger, db, testChangeNameHelper("new name")) - require.Equal(t, bap.GetName(), "new name", "BaseApp should have had name changed via option function") + db := memdb.NewDB() + bap := baseapp.NewBaseApp("starting name", logger, db, nil, testChangeNameHelper("new name")) + require.Equal(t, bap.Name(), "new name", "BaseApp should have had name changed via option function") } -func testChangeNameHelper(name string) func(*baseapp.BaseApp) { - return func(bap *baseapp.BaseApp) { - bap.SetName(name) - } +func testChangeNameHelper(name string) baseapp.AppOptionFunc { + return func(bap *baseapp.BaseApp) { bap.SetName(name) } } // Test that Info returns the latest committed state. @@ -579,12 +544,6 @@ func TestBaseAppOptionSeal(t *testing.T) { require.Panics(t, func() { app.SetVersion("") }) - require.Panics(t, func() { - app.SetDB(nil) - }) - require.Panics(t, func() { - app.SetCMS(nil) - }) require.Panics(t, func() { app.SetInitChainer(nil) }) @@ -618,12 +577,12 @@ func TestInitChainer(t *testing.T) { name := t.Name() // keep the db and logger ourselves so // we can reload the same app later - db := dbm.NewMemDB() + db := memdb.NewDB() logger := defaultLogger() - app := baseapp.NewBaseApp(name, logger, db) capKey := sdk.NewKVStoreKey("main") capKey2 := sdk.NewKVStoreKey("key2") - app.MountStores(capKey, capKey2) + schemaOpt := baseapp.SetStorePrefixes(capKey, capKey2) + app := baseapp.NewBaseApp(name, logger, db, nil, schemaOpt) // set a value in the store on init chain key, value := []byte("hello"), []byte("goodbye") @@ -641,13 +600,13 @@ func TestInitChainer(t *testing.T) { // initChainer is nil - nothing happens app.InitChain(abci.RequestInitChain{}) res := app.Query(query) - require.Equal(t, 0, len(res.Value)) + require.Nil(t, res.Value) // set initChainer and try again - should see the value app.SetInitChainer(initChainer) // stores are mounted and private members are set - sealing baseapp - err := app.LoadLatestVersion() // needed to make stores non-nil + err := app.Init() require.Nil(t, err) require.Equal(t, int64(0), app.LastBlockHeight()) @@ -671,14 +630,16 @@ func TestInitChainer(t *testing.T) { app.Commit() res = app.Query(query) + require.True(t, res.IsOK()) require.Equal(t, int64(1), app.LastBlockHeight()) require.Equal(t, value, res.Value) + require.NoError(t, app.CloseStore()) // reload app - app = baseapp.NewBaseApp(name, logger, db) + app = baseapp.NewBaseApp(name, logger, db, nil, schemaOpt) app.SetInitChainer(initChainer) - app.MountStores(capKey, capKey2) - err = app.LoadLatestVersion() // needed to make stores non-nil + + err = app.Init() require.Nil(t, err) require.Equal(t, int64(1), app.LastBlockHeight()) @@ -697,7 +658,7 @@ func TestInitChainer(t *testing.T) { func TestInitChain_WithInitialHeight(t *testing.T) { name := t.Name() - db := dbm.NewMemDB() + db := memdb.NewDB() logger := defaultLogger() app := baseapp.NewBaseApp(name, logger, db) @@ -713,7 +674,7 @@ func TestInitChain_WithInitialHeight(t *testing.T) { func TestBeginBlock_WithInitialHeight(t *testing.T) { name := t.Name() - db := dbm.NewMemDB() + db := memdb.NewDB() logger := defaultLogger() app := baseapp.NewBaseApp(name, logger, db) @@ -884,7 +845,7 @@ func testTxDecoder(cdc *codec.LegacyAmino) sdk.TxDecoder { } } -func customHandlerTxTest(t *testing.T, capKey storetypes.StoreKey, storeKey []byte) handlerFun { +func customHandlerTxTest(t *testing.T, capKey stypes.StoreKey, storeKey []byte) handlerFun { return func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) { store := ctx.KVStore(capKey) txTest := tx.(txTest) @@ -915,7 +876,7 @@ func counterEvent(evType string, msgCount int64) sdk.Events { } } -func handlerMsgCounter(t *testing.T, capKey storetypes.StoreKey, deliverKey []byte) sdk.Handler { +func handlerMsgCounter(t *testing.T, capKey stypes.StoreKey, deliverKey []byte) sdk.Handler { return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { ctx = ctx.WithEventManager(sdk.NewEventManager()) store := ctx.KVStore(capKey) @@ -995,7 +956,7 @@ func TestCheckTx(t *testing.T) { // This ensures changes to the kvstore persist across successive CheckTx. counterKey := []byte("counter-key") - txHandlerOpt := func(bapp *baseapp.BaseApp) { + txHandlerOpt := baseapp.AppOptionFunc(func(bapp *baseapp.BaseApp) { legacyRouter := middleware.NewLegacyRouter() // TODO: can remove this once CheckTx doesnt process msgs. legacyRouter.AddRoute(sdk.NewRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { @@ -1010,7 +971,7 @@ func TestCheckTx(t *testing.T) { customHandlerTxTest(t, capKey1, counterKey), ) bapp.SetTxHandler(txHandler) - } + }) app := setupBaseApp(t, txHandlerOpt) @@ -1054,8 +1015,7 @@ func TestDeliverTx(t *testing.T) { anteKey := []byte("ante-key") // test increments in the handler deliverKey := []byte("deliver-key") - - txHandlerOpt := func(bapp *baseapp.BaseApp) { + txHandlerOpt := baseapp.AppOptionFunc(func(bapp *baseapp.BaseApp) { legacyRouter := middleware.NewLegacyRouter() r := sdk.NewRoute(routeMsgCounter, handlerMsgCounter(t, capKey1, deliverKey)) legacyRouter.AddRoute(r) @@ -1068,7 +1028,7 @@ func TestDeliverTx(t *testing.T) { customHandlerTxTest(t, capKey1, anteKey), ) bapp.SetTxHandler(txHandler) - } + }) app := setupBaseApp(t, txHandlerOpt) app.InitChain(abci.RequestInitChain{}) @@ -1129,7 +1089,7 @@ func TestMultiMsgDeliverTx(t *testing.T) { ) bapp.SetTxHandler(txHandler) } - app := setupBaseApp(t, txHandlerOpt) + app := setupBaseApp(t, baseapp.AppOptionFunc(txHandlerOpt)) // run a multi-msg tx // with all msgs the same route @@ -1214,7 +1174,7 @@ func TestSimulateTx(t *testing.T) { ) bapp.SetTxHandler(txHandler) } - app := setupBaseApp(t, txHandlerOpt) + app := setupBaseApp(t, baseapp.AppOptionFunc(txHandlerOpt)) app.InitChain(abci.RequestInitChain{}) @@ -1288,7 +1248,7 @@ func TestRunInvalidTransaction(t *testing.T) { ) bapp.SetTxHandler(txHandler) } - app := setupBaseApp(t, txHandlerOpt) + app := setupBaseApp(t, baseapp.AppOptionFunc(txHandlerOpt)) header := tmproto.Header{Height: 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) @@ -1414,7 +1374,7 @@ func TestTxGasLimits(t *testing.T) { bapp.SetTxHandler(txHandler) } - app := setupBaseApp(t, txHandlerOpt) + app := setupBaseApp(t, baseapp.AppOptionFunc(txHandlerOpt)) header := tmproto.Header{Height: 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) @@ -1501,7 +1461,7 @@ func TestMaxBlockGasLimits(t *testing.T) { ) bapp.SetTxHandler(txHandler) } - app := setupBaseApp(t, txHandlerOpt) + app := setupBaseApp(t, baseapp.AppOptionFunc(txHandlerOpt)) app.InitChain(abci.RequestInitChain{ ConsensusParams: &tmproto.ConsensusParams{ @@ -1587,7 +1547,7 @@ func TestBaseAppMiddleware(t *testing.T) { ) bapp.SetTxHandler(txHandler) } - app := setupBaseApp(t, txHandlerOpt) + app := setupBaseApp(t, baseapp.AppOptionFunc(txHandlerOpt)) app.InitChain(abci.RequestInitChain{}) @@ -1678,7 +1638,7 @@ func TestGasConsumptionBadTx(t *testing.T) { ) bapp.SetTxHandler(txHandler) } - app := setupBaseApp(t, txHandlerOpt) + app := setupBaseApp(t, baseapp.AppOptionFunc(txHandlerOpt)) app.InitChain(abci.RequestInitChain{ ConsensusParams: &tmproto.ConsensusParams{ @@ -1744,7 +1704,7 @@ func TestQuery(t *testing.T) { ) bapp.SetTxHandler(txHandler) } - app := setupBaseApp(t, txHandlerOpt) + app := setupBaseApp(t, baseapp.AppOptionFunc(txHandlerOpt)) app.InitChain(abci.RequestInitChain{}) @@ -1792,7 +1752,7 @@ func TestGRPCQuery(t *testing.T) { ) } - app := setupBaseApp(t, grpcQueryOpt) + app := setupBaseApp(t, baseapp.AppOptionFunc(grpcQueryOpt)) app.InitChain(abci.RequestInitChain{}) header := tmproto.Header{Height: app.LastBlockHeight() + 1} @@ -1834,7 +1794,7 @@ func TestP2PQuery(t *testing.T) { }) } - app := setupBaseApp(t, addrPeerFilterOpt, idPeerFilterOpt) + app := setupBaseApp(t, baseapp.AppOptionFunc(addrPeerFilterOpt), baseapp.AppOptionFunc(idPeerFilterOpt)) addrQuery := abci.RequestQuery{ Path: "/p2p/filter/addr/1.1.1.1:8000", @@ -2060,7 +2020,7 @@ func TestWithRouter(t *testing.T) { ) bapp.SetTxHandler(txHandler) } - app := setupBaseApp(t, txHandlerOpt) + app := setupBaseApp(t, baseapp.AppOptionFunc(txHandlerOpt)) app.InitChain(abci.RequestInitChain{}) nBlocks := 3 @@ -2087,7 +2047,7 @@ func TestWithRouter(t *testing.T) { } func TestBaseApp_EndBlock(t *testing.T) { - db := dbm.NewMemDB() + db := memdb.NewDB() name := t.Name() logger := defaultLogger() @@ -2097,8 +2057,8 @@ func TestBaseApp_EndBlock(t *testing.T) { }, } - app := baseapp.NewBaseApp(name, logger, db) - app.SetParamStore(¶mStore{db: dbm.NewMemDB()}) + app := baseapp.NewBaseApp(name, logger, db, nil) + app.SetParamStore(newParamStore(memdb.NewDB())) app.InitChain(abci.RequestInitChain{ ConsensusParams: cp, }) diff --git a/baseapp/custom_txhandler_test.go b/baseapp/custom_txhandler_test.go index 27cec992ed10..9b4183ae2884 100644 --- a/baseapp/custom_txhandler_test.go +++ b/baseapp/custom_txhandler_test.go @@ -65,7 +65,7 @@ func (txh customTxHandler) runHandler(ctx context.Context, tx sdk.Tx, txBytes [] return sdkCtx, nil } - ms := sdkCtx.MultiStore() + store := sdkCtx.RootStore() // Branch context before Handler call in case it aborts. // This is required for both CheckTx and DeliverTx. @@ -74,7 +74,7 @@ func (txh customTxHandler) runHandler(ctx context.Context, tx sdk.Tx, txBytes [] // NOTE: Alternatively, we could require that Handler ensures that // writes do not happen if aborted/failed. This may have some // performance benefits, but it'll be more difficult to get right. - cacheCtx, msCache := cacheTxContext(sdkCtx, txBytes) + cacheCtx, storeCache := cacheTxContext(sdkCtx, txBytes) cacheCtx = cacheCtx.WithEventManager(sdk.NewEventManager()) newCtx, err := txh.handler(cacheCtx, tx, isSimulate) if err != nil { @@ -88,29 +88,29 @@ func (txh customTxHandler) runHandler(ctx context.Context, tx sdk.Tx, txBytes [] // Also, in the case of the tx aborting, we need to track gas consumed via // the instantiated gas meter in the Handler, so we update the context // prior to returning. - sdkCtx = newCtx.WithMultiStore(ms) + sdkCtx = newCtx.WithRootStore(store) } - msCache.Write() + storeCache.Write() return sdkCtx, nil } // cacheTxContext returns a new context based off of the provided context with // a branched multi-store. -func cacheTxContext(sdkCtx sdk.Context, txBytes []byte) (sdk.Context, sdk.CacheMultiStore) { - ms := sdkCtx.MultiStore() +func cacheTxContext(sdkCtx sdk.Context, txBytes []byte) (sdk.Context, sdk.CacheRootStore) { + store := sdkCtx.RootStore() // TODO: https://github.com/cosmos/cosmos-sdk/issues/2824 - msCache := ms.CacheMultiStore() - if msCache.TracingEnabled() { - msCache = msCache.SetTracingContext( + storeCache := store.CacheRootStore() + if storeCache.TracingEnabled() { + storeCache.SetTraceContext( sdk.TraceContext( map[string]interface{}{ "txHash": fmt.Sprintf("%X", tmhash.Sum(txBytes)), }, ), - ).(sdk.CacheMultiStore) + ) } - return sdkCtx.WithMultiStore(msCache), msCache + return sdkCtx.WithRootStore(storeCache), storeCache } diff --git a/baseapp/grpcrouter_test.go b/baseapp/grpcrouter_test.go index ca7a6e9b5486..45da2e51af6c 100644 --- a/baseapp/grpcrouter_test.go +++ b/baseapp/grpcrouter_test.go @@ -7,10 +7,10 @@ import ( "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/libs/log" - dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/db/memdb" "github.com/cosmos/cosmos-sdk/simapp" "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" @@ -52,7 +52,7 @@ func TestGRPCGatewayRouter(t *testing.T) { func TestRegisterQueryServiceTwice(t *testing.T) { // Setup baseapp. - db := dbm.NewMemDB() + db := memdb.NewDB() encCfg := simapp.MakeTestEncodingConfig() logger, _ := log.NewDefaultLogger("plain", "info", false) app := baseapp.NewBaseApp("test", logger, db) diff --git a/baseapp/options.go b/baseapp/options.go index 1d92698d7fa9..837f659c1411 100644 --- a/baseapp/options.go +++ b/baseapp/options.go @@ -4,11 +4,9 @@ import ( "fmt" "io" - dbm "github.com/tendermint/tm-db" - "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/snapshots" - "github.com/cosmos/cosmos-sdk/store" + storetypes "github.com/cosmos/cosmos-sdk/store/v2" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/tx" ) @@ -17,12 +15,12 @@ import ( // for options that need access to non-exported fields of the BaseApp // SetPruning sets a pruning option on the multistore associated with the app -func SetPruning(opts sdk.PruningOptions) func(*BaseApp) { - return func(bapp *BaseApp) { bapp.cms.SetPruning(opts) } +func SetPruning(opts sdk.PruningOptions) StoreOption { + return func(config *sdk.RootStoreConfig, _ uint64) error { config.Pruning = opts; return nil } } // SetMinGasPrices returns an option that sets the minimum gas prices on the app. -func SetMinGasPrices(gasPricesStr string) func(*BaseApp) { +func SetMinGasPrices(gasPricesStr string) AppOptionFunc { gasPrices, err := sdk.ParseDecCoins(gasPricesStr) if err != nil { panic(fmt.Sprintf("invalid minimum gas prices: %v", err)) @@ -32,29 +30,29 @@ func SetMinGasPrices(gasPricesStr string) func(*BaseApp) { } // SetHaltHeight returns a BaseApp option function that sets the halt block height. -func SetHaltHeight(blockHeight uint64) func(*BaseApp) { - return func(bapp *BaseApp) { bapp.setHaltHeight(blockHeight) } +func SetHaltHeight(blockHeight uint64) AppOptionFunc { + return func(bap *BaseApp) { bap.setHaltHeight(blockHeight) } } // SetHaltTime returns a BaseApp option function that sets the halt block time. -func SetHaltTime(haltTime uint64) func(*BaseApp) { - return func(bapp *BaseApp) { bapp.setHaltTime(haltTime) } +func SetHaltTime(haltTime uint64) AppOptionFunc { + return func(bap *BaseApp) { bap.setHaltTime(haltTime) } } // SetMinRetainBlocks returns a BaseApp option function that sets the minimum // block retention height value when determining which heights to prune during // ABCI Commit. -func SetMinRetainBlocks(minRetainBlocks uint64) func(*BaseApp) { +func SetMinRetainBlocks(minRetainBlocks uint64) AppOptionFunc { return func(bapp *BaseApp) { bapp.setMinRetainBlocks(minRetainBlocks) } } // SetTrace will turn on or off trace flag -func SetTrace(trace bool) func(*BaseApp) { +func SetTrace(trace bool) AppOptionFunc { return func(app *BaseApp) { app.setTrace(trace) } } // SetIndexEvents provides a BaseApp option function that sets the events to index. -func SetIndexEvents(ie []string) func(*BaseApp) { +func SetIndexEvents(ie []string) AppOptionFunc { return func(app *BaseApp) { app.setIndexEvents(ie) } } @@ -65,23 +63,46 @@ func SetIAVLCacheSize(size int) func(*BaseApp) { // SetInterBlockCache provides a BaseApp option function that sets the // inter-block cache. -func SetInterBlockCache(cache sdk.MultiStorePersistentCache) func(*BaseApp) { - return func(app *BaseApp) { app.setInterBlockCache(cache) } +func SetInterBlockCache(cache sdk.RootStorePersistentCache) AppOptionFunc { + opt := func(cfg *sdk.RootStoreConfig, v uint64) error { + cfg.PersistentCache = cache + return nil + } + return func(app *BaseApp) { app.storeOpts = append(app.storeOpts, opt) } } // SetSnapshotInterval sets the snapshot interval. -func SetSnapshotInterval(interval uint64) func(*BaseApp) { +func SetSnapshotInterval(interval uint64) AppOptionFunc { return func(app *BaseApp) { app.SetSnapshotInterval(interval) } } // SetSnapshotKeepRecent sets the recent snapshots to keep. -func SetSnapshotKeepRecent(keepRecent uint32) func(*BaseApp) { +func SetSnapshotKeepRecent(keepRecent uint32) AppOptionFunc { return func(app *BaseApp) { app.SetSnapshotKeepRecent(keepRecent) } } // SetSnapshotStore sets the snapshot store. -func SetSnapshotStore(snapshotStore *snapshots.Store) func(*BaseApp) { - return func(app *BaseApp) { app.SetSnapshotStore(snapshotStore) } +func SetSnapshotStore(snapshotStore *snapshots.Store) AppOptionOrdered { + return AppOptionOrdered{ + func(app *BaseApp) { app.SetSnapshotStore(snapshotStore) }, + OptionOrderAfterStore, + } +} + +// SetStorePrefixes store reserves prefix according to app configuration +func SetStorePrefixes(keys ...storetypes.StoreKey) StoreOption { + return func(config *sdk.RootStoreConfig, _ uint64) error { + for _, key := range keys { + typ, err := storetypes.StoreKeyToType(key) + if err != nil { + return err + } + if err = config.ReservePrefix(key, typ); err != nil { + return err + } + } + return nil + } } func (app *BaseApp) SetName(name string) { @@ -114,22 +135,6 @@ func (app *BaseApp) SetProtocolVersion(v uint64) { app.appVersion = v } -func (app *BaseApp) SetDB(db dbm.DB) { - if app.sealed { - panic("SetDB() on sealed BaseApp") - } - - app.db = db -} - -func (app *BaseApp) SetCMS(cms store.CommitMultiStore) { - if app.sealed { - panic("SetEndBlocker() on sealed BaseApp") - } - - app.cms = cms -} - func (app *BaseApp) SetInitChainer(initChainer sdk.InitChainer) { if app.sealed { panic("SetInitChainer() on sealed BaseApp") @@ -189,16 +194,20 @@ func (app *BaseApp) SetFauxMerkleMode() { // SetCommitMultiStoreTracer sets the store tracer on the BaseApp's underlying // CommitMultiStore. func (app *BaseApp) SetCommitMultiStoreTracer(w io.Writer) { - app.cms.SetTracer(w) + opt := func(cfg *sdk.RootStoreConfig, v uint64) error { + cfg.TraceWriter = w + return nil + } + app.storeOpts = append(app.storeOpts, opt) } -// SetStoreLoader allows us to customize the rootMultiStore initialization. -func (app *BaseApp) SetStoreLoader(loader StoreLoader) { +// SetStoreLoader allows us to customize the root store initialization. +func (app *BaseApp) SetStoreConstructor(ctor StoreConstructor) { if app.sealed { - panic("SetStoreLoader() on sealed BaseApp") + panic("SetStoreConstructor() on sealed BaseApp") } - app.storeLoader = loader + app.storeCtor = ctor } // SetSnapshotStore sets the snapshot store. @@ -210,7 +219,7 @@ func (app *BaseApp) SetSnapshotStore(snapshotStore *snapshots.Store) { app.snapshotManager = nil return } - app.snapshotManager = snapshots.NewManager(snapshotStore, app.cms) + app.snapshotManager = snapshots.NewManager(snapshotStore, app.store) } // SetSnapshotInterval sets the snapshot interval. diff --git a/baseapp/state.go b/baseapp/state.go index addc89cb342c..6f6ce61dce8b 100644 --- a/baseapp/state.go +++ b/baseapp/state.go @@ -5,14 +5,14 @@ import ( ) type state struct { - ms sdk.CacheMultiStore + ms sdk.CacheRootStore ctx sdk.Context } -// CacheMultiStore calls and returns a CacheMultiStore on the state's underling -// CacheMultiStore. -func (st *state) CacheMultiStore() sdk.CacheMultiStore { - return st.ms.CacheMultiStore() +// CacheRootStore calls and returns a CacheRootStore on the state's underling +// CacheRootStore. +func (st *state) CacheRootStore() sdk.CacheRootStore { + return st.ms.CacheRootStore() } // Context returns the Context of the state. diff --git a/baseapp/test_helpers.go b/baseapp/test_helpers.go index 6b770499c70b..1b41b93846c3 100644 --- a/baseapp/test_helpers.go +++ b/baseapp/test_helpers.go @@ -89,5 +89,5 @@ func (app *BaseApp) NewContext(isCheckTx bool, header tmproto.Header) sdk.Contex } func (app *BaseApp) NewUncachedContext(isCheckTx bool, header tmproto.Header) sdk.Context { - return sdk.NewContext(app.cms, header, isCheckTx, app.logger) + return sdk.NewContext(app.store, header, isCheckTx, app.logger) } diff --git a/baseapp/util_test.go b/baseapp/util_test.go index 7244aff8307a..c2a8e27ba158 100644 --- a/baseapp/util_test.go +++ b/baseapp/util_test.go @@ -24,11 +24,11 @@ func (app *BaseApp) DeliverState() *state { return app.deliverState } -// CMS is an exported method to be able to access baseapp's cms in tests. +// Store is an exported method to be able to access baseapp's root store in tests. // // This method is only accessible in baseapp tests. -func (app *BaseApp) CMS() types.CommitMultiStore { - return app.cms +func (app *BaseApp) Store() types.CommitRootStore { + return app.store } // GetMaximumBlockGas return maximum blocks gas. diff --git a/docs/core/upgrade.md b/docs/core/upgrade.md index 2782785e75cd..1517c8940a5c 100644 --- a/docs/core/upgrade.md +++ b/docs/core/upgrade.md @@ -75,7 +75,7 @@ You can introduce entirely new modules to the application during an upgrade. New ### Add StoreUpgrades for New Modules -All chains preparing to run in-place store migrations will need to manually add store upgrades for new modules and then configure the store loader to apply those upgrades. This ensures that the new module's stores are added to the multistore before the migrations begin. +All chains preparing to run in-place store migrations will need to manually add store upgrades for new modules and then configure the store to apply those upgrades. This ensures that the new module's stores are added to the multistore before the migrations begin. ```go upgradeInfo, err := app.UpgradeKeeper.ReadUpgradeInfoFromDisk() @@ -91,8 +91,8 @@ if upgradeInfo.Name == "my-plan" && !app.UpgradeKeeper.IsSkipHeight(upgradeInfo. // ... } - // configure store loader that checks if version == upgradeHeight and applies store upgrades - app.SetStoreLoader(upgradetypes.UpgradeStoreLoader(upgradeInfo.Height, &storeUpgrades)) + // configure store option that checks if version == upgradeHeight and applies store upgrades + app.SetStoreOption(upgradetypes.UpgradeStoreOption(upgradeInfo.Height, &storeUpgrades)) } ``` diff --git a/server/types/app.go b/server/types/app.go index 76493b060351..6e5765978f99 100644 --- a/server/types/app.go +++ b/server/types/app.go @@ -11,9 +11,9 @@ import ( "github.com/tendermint/tendermint/libs/log" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmtypes "github.com/tendermint/tendermint/types" - dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/client" + dbm "github.com/cosmos/cosmos-sdk/db" "github.com/cosmos/cosmos-sdk/server/api" "github.com/cosmos/cosmos-sdk/server/config" ) @@ -56,7 +56,7 @@ type ( // AppCreator is a function that allows us to lazily initialize an // application using various configurations. - AppCreator func(log.Logger, dbm.DB, io.Writer, AppOptions) Application + AppCreator func(log.Logger, dbm.DBConnection, io.Writer, AppOptions) Application // ModuleInitFlags takes a start command and adds modules specific init flags. ModuleInitFlags func(startCmd *cobra.Command) @@ -76,5 +76,5 @@ type ( // AppExporter is a function that dumps all app state to // JSON-serializable structure and returns the current validator set. - AppExporter func(log.Logger, dbm.DB, io.Writer, int64, bool, []string, AppOptions) (ExportedApp, error) + AppExporter func(log.Logger, dbm.DBConnection, io.Writer, int64, bool, []string, AppOptions) (ExportedApp, error) ) diff --git a/server/util.go b/server/util.go index 16c9f3a185eb..27af68e5eb34 100644 --- a/server/util.go +++ b/server/util.go @@ -21,9 +21,9 @@ import ( "github.com/spf13/viper" tmcfg "github.com/tendermint/tendermint/config" tmlog "github.com/tendermint/tendermint/libs/log" - dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/client/flags" + dbm "github.com/cosmos/cosmos-sdk/db" "github.com/cosmos/cosmos-sdk/server/config" "github.com/cosmos/cosmos-sdk/server/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -371,7 +371,7 @@ func addrToIP(addr net.Addr) net.IP { return ip } -func openDB(rootDir string) (dbm.DB, error) { +func openDB(rootDir string) (dbm.DBConnection, error) { dataDir := filepath.Join(rootDir, "data") return sdk.NewLevelDB("application", dataDir) } diff --git a/simapp/app.go b/simapp/app.go index 6fa1e2f4482c..0f968dd33ef4 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -2,6 +2,7 @@ package simapp import ( "encoding/json" + "errors" "io" "net/http" "os" @@ -12,14 +13,14 @@ import ( "github.com/spf13/cast" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" - tmos "github.com/tendermint/tendermint/libs/os" - dbm "github.com/tendermint/tm-db" + // tmos "github.com/tendermint/tendermint/libs/os" "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/grpc/tmservice" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec/types" + dbm "github.com/cosmos/cosmos-sdk/db" "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/server/api" "github.com/cosmos/cosmos-sdk/server/config" @@ -48,6 +49,13 @@ import ( "github.com/cosmos/cosmos-sdk/x/capability" capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + + storetypes "github.com/cosmos/cosmos-sdk/store/types" + storev2 "github.com/cosmos/cosmos-sdk/store/v2" + authmiddleware "github.com/cosmos/cosmos-sdk/x/auth/middleware" + "github.com/cosmos/cosmos-sdk/x/authz" + authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper" + authzmodule "github.com/cosmos/cosmos-sdk/x/authz/module" "github.com/cosmos/cosmos-sdk/x/crisis" crisiskeeper "github.com/cosmos/cosmos-sdk/x/crisis/keeper" crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types" @@ -202,20 +210,22 @@ func init() { // NewSimApp returns a reference to an initialized SimApp. func NewSimApp( - logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool, skipUpgradeHeights map[int64]bool, - homePath string, invCheckPeriod uint, encodingConfig simappparams.EncodingConfig, - appOpts servertypes.AppOptions, baseAppOptions ...func(*baseapp.BaseApp), + logger log.Logger, + db dbm.DBConnection, + // rootStore sdk.CommitRootStore, + traceStore io.Writer, + loadLatest bool, + skipUpgradeHeights map[int64]bool, + homePath string, + invCheckPeriod uint, + encodingConfig simappparams.EncodingConfig, + appOpts servertypes.AppOptions, baseAppOptions ...baseapp.AppOption, ) *SimApp { appCodec := encodingConfig.Codec legacyAmino := encodingConfig.Amino interfaceRegistry := encodingConfig.InterfaceRegistry - bApp := baseapp.NewBaseApp(appName, logger, db, baseAppOptions...) - bApp.SetCommitMultiStoreTracer(traceStore) - bApp.SetVersion(version.Version) - bApp.SetInterfaceRegistry(interfaceRegistry) - keys := sdk.NewKVStoreKeys( authtypes.StoreKey, banktypes.StoreKey, stakingtypes.StoreKey, minttypes.StoreKey, distrtypes.StoreKey, slashingtypes.StoreKey, @@ -227,6 +237,43 @@ func NewSimApp( // NOTE: The testingkey is just mounted for testing purposes. Actual applications should // not include this key. memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey, "testingkey") + // initialize stores + setNamespaces := func(config *sdk.RootStoreConfig, ver uint64) error { + for _, key := range keys { + typ, err := storev2.StoreKeyToType(key) + if err != nil { + return err + } + if err = config.ReservePrefix(key, typ); err != nil { + return err + } + } + for _, key := range memKeys { + typ, err := storev2.StoreKeyToType(key) + if err != nil { + return err + } + if err = config.ReservePrefix(key, typ); err != nil { + return err + } + } + for _, key := range tkeys { + typ, err := storev2.StoreKeyToType(key) + if err != nil { + return err + } + if err = config.ReservePrefix(key, typ); err != nil { + return err + } + } + return nil + } + baseAppOptions = append(baseAppOptions, baseapp.StoreOption(setNamespaces)) + + bApp := baseapp.NewBaseApp(appName, logger, db, encodingConfig.TxConfig.TxDecoder(), baseAppOptions...) + bApp.SetCommitMultiStoreTracer(traceStore) + bApp.SetVersion(version.Version) + bApp.SetInterfaceRegistry(interfaceRegistry) // configure state listening capabilities using AppOptions // we are doing nothing with the returned streamingServices and waitGroup in this case @@ -420,23 +467,12 @@ func NewSimApp( app.sm.RegisterStoreDecoders() - // initialize stores - app.MountKVStores(keys) - app.MountTransientStores(tkeys) - app.MountMemoryStores(memKeys) - // initialize BaseApp app.SetInitChainer(app.InitChainer) app.SetBeginBlocker(app.BeginBlocker) app.SetEndBlocker(app.EndBlocker) app.setTxHandler(encodingConfig.TxConfig, cast.ToStringSlice(appOpts.Get(server.FlagIndexEvents))) - if loadLatest { - if err := app.LoadLatestVersion(); err != nil { - tmos.Exit(err.Error()) - } - } - return app } @@ -489,7 +525,8 @@ func (app *SimApp) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci. // LoadHeight loads a particular height func (app *SimApp) LoadHeight(height int64) error { - return app.LoadVersion(height) + // return app.LoadVersion(height) + return errors.New("cannot load arbitrary height") } // ModuleAccountAddrs returns all the app's module account addresses. diff --git a/simapp/app_test.go b/simapp/app_test.go index 7dd4f73917d7..5c44896b9c08 100644 --- a/simapp/app_test.go +++ b/simapp/app_test.go @@ -9,9 +9,9 @@ import ( abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/db/memdb" "github.com/cosmos/cosmos-sdk/tests/mocks" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" @@ -38,8 +38,7 @@ import ( func TestSimAppExportAndBlockedAddrs(t *testing.T) { encCfg := MakeTestEncodingConfig() - db := dbm.NewMemDB() - logger, _ := log.NewDefaultLogger("plain", "info", false) + db := memdb.NewDB() app := NewSimappWithCustomOptions(t, false, SetupOptions{ Logger: logger, DB: db, @@ -59,10 +58,12 @@ func TestSimAppExportAndBlockedAddrs(t *testing.T) { } app.Commit() + require.NoError(t, app.CloseStore()) logger2, _ := log.NewDefaultLogger("plain", "info", false) // Making a new app object with the db, so that initchain hasn't been called app2 := NewSimApp(logger2, db, nil, true, map[int64]bool{}, DefaultNodeHome, 0, encCfg, EmptyAppOptions{}) + require.NoError(t, app2.Init()) _, err := app2.ExportAppStateAndValidators(false, []string{}) require.NoError(t, err, "ExportAppStateAndValidators should not have an error") } @@ -73,13 +74,12 @@ func TestGetMaccPerms(t *testing.T) { } func TestRunMigrations(t *testing.T) { - db := dbm.NewMemDB() encCfg := MakeTestEncodingConfig() logger, _ := log.NewDefaultLogger("plain", "info", false) - app := NewSimApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, 0, encCfg, EmptyAppOptions{}) + app := NewSimApp(logger, memdb.NewDB(), nil, true, map[int64]bool{}, DefaultNodeHome, 0, encCfg, EmptyAppOptions{}) // Create a new baseapp and configurator for the purpose of this test. - bApp := baseapp.NewBaseApp(appName, logger, db) + bApp := baseapp.NewBaseApp(appName, logger, memdb.NewDB(), encCfg.TxConfig.TxDecoder()) bApp.SetCommitMultiStoreTracer(nil) bApp.SetInterfaceRegistry(encCfg.InterfaceRegistry) msr := authmiddleware.NewMsgServiceRouter(encCfg.InterfaceRegistry) @@ -98,6 +98,7 @@ func TestRunMigrations(t *testing.T) { module.RegisterServices(app.configurator) } + require.NoError(t, app.Init()) // Initialize the chain app.InitChain(abci.RequestInitChain{}) @@ -203,10 +204,11 @@ func TestRunMigrations(t *testing.T) { } func TestInitGenesisOnMigration(t *testing.T) { - db := dbm.NewMemDB() + db := memdb.NewDB() encCfg := MakeTestEncodingConfig() logger, _ := log.NewDefaultLogger("plain", "info", false) app := NewSimApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, 0, encCfg, EmptyAppOptions{}) + require.NoError(t, app.Init()) ctx := app.NewContext(true, tmproto.Header{Height: app.LastBlockHeight()}) // Create a mock module. This module will serve as the new module we're @@ -248,8 +250,7 @@ func TestInitGenesisOnMigration(t *testing.T) { func TestUpgradeStateOnGenesis(t *testing.T) { encCfg := MakeTestEncodingConfig() - db := dbm.NewMemDB() - logger, _ := log.NewDefaultLogger("plain", "info", false) + db := memdb.NewDB() app := NewSimappWithCustomOptions(t, false, SetupOptions{ Logger: logger, DB: db, diff --git a/simapp/sim_bench_test.go b/simapp/sim_bench_test.go index b5592051001f..86751d4a8c68 100644 --- a/simapp/sim_bench_test.go +++ b/simapp/sim_bench_test.go @@ -7,6 +7,7 @@ import ( tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + // "github.com/cosmos/cosmos-sdk/store" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" "github.com/cosmos/cosmos-sdk/x/simulation" ) @@ -57,7 +58,7 @@ func BenchmarkFullAppSimulation(b *testing.B) { } if config.Commit { - PrintStats(db) + // PrintStats(db) // TODO } } diff --git a/simapp/sim_test.go b/simapp/sim_test.go index 26b1252086aa..4abf070320a5 100644 --- a/simapp/sim_test.go +++ b/simapp/sim_test.go @@ -14,11 +14,11 @@ import ( abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/db/memdb" "github.com/cosmos/cosmos-sdk/simapp/helpers" - "github.com/cosmos/cosmos-sdk/store" + // "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" @@ -48,14 +48,15 @@ type StoreKeysPrefixes struct { // fauxMerkleModeOpt returns a BaseApp option to use a dbStoreAdapter instead of // an IAVLStore for faster simulation speed. -func fauxMerkleModeOpt(bapp *baseapp.BaseApp) { +var fauxMerkleModeOpt = baseapp.AppOptionFunc(func(bapp *baseapp.BaseApp) { bapp.SetFauxMerkleMode() -} +}) // interBlockCacheOpt returns a BaseApp option function that sets the persistent // inter-block write-through cache. -func interBlockCacheOpt() func(*baseapp.BaseApp) { - return baseapp.SetInterBlockCache(store.NewCommitKVStoreCacheManager()) +func interBlockCacheOpt() baseapp.AppOptionFunc { + return func(*baseapp.BaseApp) {} + // return baseapp.SetInterBlockCache(store.NewCommitKVStoreCacheManager()) } func TestFullAppSimulation(t *testing.T) { @@ -310,7 +311,7 @@ func TestAppStateDeterminism(t *testing.T) { logger = log.NewNopLogger() } - db := dbm.NewMemDB() + db := memdb.NewDB() app := NewSimApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, MakeTestEncodingConfig(), EmptyAppOptions{}, interBlockCacheOpt()) fmt.Printf( @@ -331,9 +332,9 @@ func TestAppStateDeterminism(t *testing.T) { ) require.NoError(t, err) - if config.Commit { - PrintStats(db) - } + // if config.Commit { + // PrintStats(db)//TODO + // } appHash := app.LastCommitID().Hash appHashList[j] = appHash diff --git a/simapp/simd/cmd/root.go b/simapp/simd/cmd/root.go index eee610d941c7..1adc235deae7 100644 --- a/simapp/simd/cmd/root.go +++ b/simapp/simd/cmd/root.go @@ -10,7 +10,6 @@ import ( "github.com/spf13/cobra" tmcli "github.com/tendermint/tendermint/libs/cli" "github.com/tendermint/tendermint/libs/log" - dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/client" @@ -19,6 +18,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/client/rpc" + dbm "github.com/cosmos/cosmos-sdk/db" "github.com/cosmos/cosmos-sdk/server" serverconfig "github.com/cosmos/cosmos-sdk/server/config" servertypes "github.com/cosmos/cosmos-sdk/server/types" @@ -229,8 +229,13 @@ type appCreator struct { } // newApp is an appCreator -func (a appCreator) newApp(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts servertypes.AppOptions) servertypes.Application { - var cache sdk.MultiStorePersistentCache +func (a appCreator) newApp( + logger log.Logger, + db dbm.DBConnection, + traceStore io.Writer, + appOpts servertypes.AppOptions, +) servertypes.Application { + var cache sdk.RootStorePersistentCache if cast.ToBool(appOpts.Get(server.FlagInterBlockCache)) { cache = store.NewCommitKVStoreCacheManager() @@ -279,7 +284,7 @@ func (a appCreator) newApp(logger log.Logger, db dbm.DB, traceStore io.Writer, a // appExport creates a new simapp (optionally at a given height) // and exports state. func (a appCreator) appExport( - logger log.Logger, db dbm.DB, traceStore io.Writer, height int64, forZeroHeight bool, jailAllowedAddrs []string, + logger log.Logger, db dbm.DBConnection, traceStore io.Writer, height int64, forZeroHeight bool, jailAllowedAddrs []string, appOpts servertypes.AppOptions) (servertypes.ExportedApp, error) { var simApp *simapp.SimApp diff --git a/simapp/test_helpers.go b/simapp/test_helpers.go index 364891a5f0c9..3e3d50851ee7 100644 --- a/simapp/test_helpers.go +++ b/simapp/test_helpers.go @@ -16,7 +16,6 @@ import ( "github.com/tendermint/tendermint/libs/log" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmtypes "github.com/tendermint/tendermint/types" - dbm "github.com/tendermint/tm-db" bam "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/client" @@ -25,10 +24,12 @@ import ( "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/db/memdb" "github.com/cosmos/cosmos-sdk/server/types" "github.com/cosmos/cosmos-sdk/simapp/helpers" "github.com/cosmos/cosmos-sdk/simapp/params" "github.com/cosmos/cosmos-sdk/testutil/mock" + // "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/errors" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" @@ -59,7 +60,7 @@ var DefaultConsensusParams = &tmproto.ConsensusParams{ // SetupOptions defines arguments that are passed into `Simapp` constructor. type SetupOptions struct { Logger log.Logger - DB *dbm.MemDB + DB *memdb.MemDB InvCheckPeriod uint HomePath string SkipUpgradeHeights map[int64]bool @@ -68,9 +69,12 @@ type SetupOptions struct { } func setup(withGenesis bool, invCheckPeriod uint) (*SimApp, GenesisState) { - db := dbm.NewMemDB() encCdc := MakeTestEncodingConfig() - app := NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, DefaultNodeHome, invCheckPeriod, encCdc, EmptyAppOptions{}) + // rstore, err := store.NewCommitRootStore(memdb.NewDB(), store.RootStoreConfig{}) + // if err != nil { + // panic(err) + // } + app := NewSimApp(log.NewNopLogger(), memdb.NewDB(), nil, true, map[int64]bool{}, DefaultNodeHome, invCheckPeriod, encCdc, EmptyAppOptions{}) if withGenesis { return app, NewDefaultGenesisState(encCdc.Codec) } @@ -97,6 +101,7 @@ func NewSimappWithCustomOptions(t *testing.T, isCheckTx bool, options SetupOptio } app := NewSimApp(options.Logger, options.DB, nil, true, options.SkipUpgradeHeights, options.HomePath, options.InvCheckPeriod, options.EncConfig, options.AppOpts) + require.NoError(t, app.Init()) genesisState := NewDefaultGenesisState(app.appCodec) genesisState = genesisStateWithValSet(t, app, genesisState, valSet, []authtypes.GenesisAccount{acc}, balance) diff --git a/simapp/utils.go b/simapp/utils.go index 51d8ef54020f..863d78220bdf 100644 --- a/simapp/utils.go +++ b/simapp/utils.go @@ -6,9 +6,9 @@ import ( "os" "github.com/tendermint/tendermint/libs/log" - dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/codec" + dbm "github.com/cosmos/cosmos-sdk/db" "github.com/cosmos/cosmos-sdk/simapp/helpers" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/kv" @@ -19,7 +19,7 @@ import ( // SetupSimulation creates the config, db (levelDB), temporary directory and logger for // the simulation tests. If `FlagEnabledValue` is false it skips the current test. // Returns error on an invalid db intantiation or temp dir creation. -func SetupSimulation(dirPrefix, dbName string) (simtypes.Config, dbm.DB, string, log.Logger, bool, error) { +func SetupSimulation(dirPrefix, dbName string) (simtypes.Config, dbm.DBConnection, string, log.Logger, bool, error) { if !FlagEnabledValue { return simtypes.Config{}, nil, "", nil, true, nil } @@ -104,10 +104,11 @@ func CheckExportSimulation( } // PrintStats prints the corresponding statistics from the app DB. -func PrintStats(db dbm.DB) { +func PrintStats(db dbm.DBConnection) { fmt.Println("\nLevelDB Stats") - fmt.Println(db.Stats()["leveldb.stats"]) - fmt.Println("LevelDB cached block size", db.Stats()["leveldb.cachedblock"]) + fmt.Println("\nTODO") // TODO + // fmt.Println(db.Stats()["leveldb.stats"]) + // fmt.Println("LevelDB cached block size", db.Stats()["leveldb.cachedblock"]) } // GetSimulationLog unmarshals the KVPair's Value to the corresponding type based on the diff --git a/snapshots/store.go b/snapshots/store.go index 77ff58e22ff6..7b0f157191fb 100644 --- a/snapshots/store.go +++ b/snapshots/store.go @@ -11,8 +11,8 @@ import ( "sync" "github.com/gogo/protobuf/proto" - db "github.com/tendermint/tm-db" + dbm "github.com/cosmos/cosmos-sdk/db" "github.com/cosmos/cosmos-sdk/snapshots/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) @@ -24,7 +24,7 @@ const ( // Store is a snapshot store, containing snapshot metadata and binary chunks. type Store struct { - db db.DB + db dbm.DBConnection dir string mtx sync.Mutex @@ -32,7 +32,7 @@ type Store struct { } // NewStore creates a new snapshot store. -func NewStore(db db.DB, dir string) (*Store, error) { +func NewStore(db dbm.DBConnection, dir string) (*Store, error) { if dir == "" { return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "snapshot directory not given") } @@ -57,11 +57,18 @@ func (s *Store) Delete(height uint64, format uint32) error { return sdkerrors.Wrapf(sdkerrors.ErrConflict, "snapshot for height %v format %v is currently being saved", height, format) } - err := s.db.DeleteSync(encodeKey(height, format)) + dbw := s.db.Writer() + defer dbw.Discard() + err := dbw.Delete(encodeKey(height, format)) if err != nil { return sdkerrors.Wrapf(err, "failed to delete snapshot for height %v format %v", height, format) } + err = dbw.Commit() + if err != nil { + return sdkerrors.Wrapf(err, "failed to commit snapshot deletion for height %v format %v", + height, format) + } err = os.RemoveAll(s.pathSnapshot(height, format)) return sdkerrors.Wrapf(err, "failed to delete snapshot chunks for height %v format %v", height, format) @@ -69,7 +76,9 @@ func (s *Store) Delete(height uint64, format uint32) error { // Get fetches snapshot info from the database. func (s *Store) Get(height uint64, format uint32) (*types.Snapshot, error) { - bytes, err := s.db.Get(encodeKey(height, format)) + dbr := s.db.Reader() + defer dbr.Discard() + bytes, err := dbr.Get(encodeKey(height, format)) if err != nil { return nil, sdkerrors.Wrapf(err, "failed to fetch snapshot metadata for height %v format %v", height, format) @@ -91,14 +100,16 @@ func (s *Store) Get(height uint64, format uint32) (*types.Snapshot, error) { // Get fetches the latest snapshot from the database, if any. func (s *Store) GetLatest() (*types.Snapshot, error) { - iter, err := s.db.ReverseIterator(encodeKey(0, 0), encodeKey(uint64(math.MaxUint64), math.MaxUint32)) + dbr := s.db.Reader() + defer dbr.Discard() + iter, err := dbr.ReverseIterator(encodeKey(0, 0), encodeKey(uint64(math.MaxUint64), math.MaxUint32)) if err != nil { return nil, sdkerrors.Wrap(err, "failed to find latest snapshot") } defer iter.Close() var snapshot *types.Snapshot - if iter.Valid() { + if iter.Next() { snapshot = &types.Snapshot{} err := proto.Unmarshal(iter.Value(), snapshot) if err != nil { @@ -111,14 +122,16 @@ func (s *Store) GetLatest() (*types.Snapshot, error) { // List lists snapshots, in reverse order (newest first). func (s *Store) List() ([]*types.Snapshot, error) { - iter, err := s.db.ReverseIterator(encodeKey(0, 0), encodeKey(uint64(math.MaxUint64), math.MaxUint32)) + dbr := s.db.Reader() + defer dbr.Discard() + iter, err := dbr.ReverseIterator(encodeKey(0, 0), encodeKey(uint64(math.MaxUint64), math.MaxUint32)) if err != nil { return nil, sdkerrors.Wrap(err, "failed to list snapshots") } defer iter.Close() snapshots := make([]*types.Snapshot, 0) - for ; iter.Valid(); iter.Next() { + for iter.Next() { snapshot := &types.Snapshot{} err := proto.Unmarshal(iter.Value(), snapshot) if err != nil { @@ -181,7 +194,9 @@ func (s *Store) loadChunkFile(height uint64, format uint32, chunk uint32) (io.Re // Prune removes old snapshots. The given number of most recent heights (regardless of format) are retained. func (s *Store) Prune(retain uint32) (uint64, error) { - iter, err := s.db.ReverseIterator(encodeKey(0, 0), encodeKey(uint64(math.MaxUint64), math.MaxUint32)) + dbr := s.db.Reader() + defer dbr.Discard() + iter, err := dbr.ReverseIterator(encodeKey(0, 0), encodeKey(uint64(math.MaxUint64), math.MaxUint32)) if err != nil { return 0, sdkerrors.Wrap(err, "failed to prune snapshots") } @@ -190,7 +205,7 @@ func (s *Store) Prune(retain uint32) (uint64, error) { pruned := uint64(0) prunedHeights := make(map[uint64]bool) skip := make(map[uint64]bool) - for ; iter.Valid(); iter.Next() { + for iter.Next() { height, format, err := decodeKey(iter.Key()) if err != nil { return 0, sdkerrors.Wrap(err, "failed to prune snapshots") @@ -242,7 +257,9 @@ func (s *Store) Save( s.mtx.Unlock() }() - exists, err := s.db.Has(encodeKey(height, format)) + dbr := s.db.Reader() + defer dbr.Discard() + exists, err := dbr.Has(encodeKey(height, format)) if err != nil { return nil, err } @@ -250,6 +267,7 @@ func (s *Store) Save( return nil, sdkerrors.Wrapf(sdkerrors.ErrConflict, "snapshot already exists for height %v format %v", height, format) } + dbr.Discard() snapshot := &types.Snapshot{ Height: height, @@ -299,7 +317,13 @@ func (s *Store) saveSnapshot(snapshot *types.Snapshot) error { if err != nil { return sdkerrors.Wrap(err, "failed to encode snapshot metadata") } - err = s.db.SetSync(encodeKey(snapshot.Height, snapshot.Format), value) + dbw := s.db.Writer() + defer dbw.Discard() + err = dbw.Set(encodeKey(snapshot.Height, snapshot.Format), value) + if err != nil { + return sdkerrors.Wrap(err, "failed to store snapshot") + } + err = dbw.Commit() return sdkerrors.Wrap(err, "failed to store snapshot") } diff --git a/testutil/context.go b/testutil/context.go index 1addf17c287a..8e410acef77b 100644 --- a/testutil/context.go +++ b/testutil/context.go @@ -3,24 +3,30 @@ package testutil import ( "github.com/tendermint/tendermint/libs/log" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - dbm "github.com/tendermint/tm-db" - "github.com/cosmos/cosmos-sdk/store" - storetypes "github.com/cosmos/cosmos-sdk/store/types" + "github.com/cosmos/cosmos-sdk/db/memdb" + // "github.com/cosmos/cosmos-sdk/store" + stypes "github.com/cosmos/cosmos-sdk/store/v2" + "github.com/cosmos/cosmos-sdk/store/v2/flat" sdk "github.com/cosmos/cosmos-sdk/types" ) // DefaultContext creates a sdk.Context with a fresh MemDB that can be used in tests. -func DefaultContext(key storetypes.StoreKey, tkey storetypes.StoreKey) sdk.Context { - db := dbm.NewMemDB() - cms := store.NewCommitMultiStore(db) - cms.MountStoreWithDB(key, storetypes.StoreTypeIAVL, db) - cms.MountStoreWithDB(tkey, storetypes.StoreTypeTransient, db) - err := cms.LoadLatestVersion() +func DefaultContext(key, tkey stypes.StoreKey) (ret sdk.Context, err error) { + db := memdb.NewDB() + opts := flat.DefaultRootStoreConfig() + err = opts.ReservePrefix(key, stypes.StoreTypePersistent) if err != nil { - panic(err) + return } - ctx := sdk.NewContext(cms, tmproto.Header{}, false, log.NewNopLogger()) - - return ctx + err = opts.ReservePrefix(tkey, stypes.StoreTypeTransient) + if err != nil { + return + } + rs, err := flat.NewRootStore(db, opts) + if err != nil { + return + } + ret = sdk.NewContext(rs, tmproto.Header{}, false, log.NewNopLogger()) + return } diff --git a/testutil/network/network.go b/testutil/network/network.go index 4de1c7288144..f6c542d5826c 100644 --- a/testutil/network/network.go +++ b/testutil/network/network.go @@ -21,7 +21,6 @@ import ( tmrand "github.com/tendermint/tendermint/libs/rand" "github.com/tendermint/tendermint/libs/service" tmclient "github.com/tendermint/tendermint/rpc/client" - dbm "github.com/tendermint/tm-db" "google.golang.org/grpc" "github.com/cosmos/cosmos-sdk/baseapp" @@ -32,6 +31,7 @@ import ( "github.com/cosmos/cosmos-sdk/crypto/hd" "github.com/cosmos/cosmos-sdk/crypto/keyring" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/db/memdb" "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/server/api" srvconfig "github.com/cosmos/cosmos-sdk/server/config" @@ -58,7 +58,7 @@ type AppConstructor = func(val Validator) servertypes.Application func NewAppConstructor(encodingCfg params.EncodingConfig) AppConstructor { return func(val Validator) servertypes.Application { return simapp.NewSimApp( - val.Ctx.Logger, dbm.NewMemDB(), nil, true, make(map[int64]bool), val.Ctx.Config.RootDir, 0, + val.Ctx.Logger, memdb.NewDB(), nil, true, make(map[int64]bool), val.Ctx.Config.RootDir, 0, encodingCfg, simapp.EmptyAppOptions{}, baseapp.SetPruning(storetypes.NewPruningOptionsFromString(val.AppConfig.Pruning)), diff --git a/types/context.go b/types/context.go index ead23d212505..b3eb6f505d34 100644 --- a/types/context.go +++ b/types/context.go @@ -11,7 +11,8 @@ import ( tmproto "github.com/tendermint/tendermint/proto/tendermint/types" "github.com/cosmos/cosmos-sdk/store/gaskv" - storetypes "github.com/cosmos/cosmos-sdk/store/types" + stypes "github.com/cosmos/cosmos-sdk/store/types" + // stypes2 "github.com/cosmos/cosmos-sdk/store/v2" ) /* @@ -24,7 +25,7 @@ and standard additions here would be better just to add to the Context struct */ type Context struct { ctx context.Context - ms MultiStore + store BasicRootStore header tmproto.Header headerHash tmbytes.HexBytes chainID string @@ -44,8 +45,10 @@ type Context struct { type Request = Context // Read-only accessors -func (c Context) Context() context.Context { return c.ctx } -func (c Context) MultiStore() MultiStore { return c.ms } +func (c Context) Context() context.Context { return c.ctx } + +// func (c Context) MultiStore() MultiStore { return c.store } +func (c Context) RootStore() BasicRootStore { return c.store } func (c Context) BlockHeight() int64 { return c.header.Height } func (c Context) BlockTime() time.Time { return c.header.Time } func (c Context) ChainID() string { return c.chainID } @@ -77,17 +80,17 @@ func (c Context) ConsensusParams() *tmproto.ConsensusParams { } // create a new context -func NewContext(ms MultiStore, header tmproto.Header, isCheckTx bool, logger log.Logger) Context { +func NewContext(rs BasicRootStore, header tmproto.Header, isCheckTx bool, logger log.Logger) Context { // https://github.com/gogo/protobuf/issues/519 header.Time = header.Time.UTC() return Context{ ctx: context.Background(), - ms: ms, + store: rs, header: header, chainID: header.ChainID, checkTx: isCheckTx, logger: logger, - gasMeter: storetypes.NewInfiniteGasMeter(), + gasMeter: stypes.NewInfiniteGasMeter(), minGasPrice: DecCoins{}, eventManager: NewEventManager(), } @@ -100,8 +103,8 @@ func (c Context) WithContext(ctx context.Context) Context { } // WithMultiStore returns a Context with an updated MultiStore. -func (c Context) WithMultiStore(ms MultiStore) Context { - c.ms = ms +func (c Context) WithRootStore(rs BasicRootStore) Context { + c.store = rs return c } @@ -216,7 +219,7 @@ func (c Context) WithEventManager(em *EventManager) Context { // TODO: remove??? func (c Context) IsZero() bool { - return c.ms == nil + return c.store == nil } // WithValue is deprecated, provided for backwards compatibility @@ -242,23 +245,24 @@ func (c Context) Value(key interface{}) interface{} { // Store / Caching // ---------------------------------------------------------------------------- -// KVStore fetches a KVStore from the MultiStore. -func (c Context) KVStore(key storetypes.StoreKey) KVStore { - return gaskv.NewStore(c.MultiStore().GetKVStore(key), c.GasMeter(), storetypes.KVGasConfig()) +// KVStore fetches a KVStore from the RootStore. +func (c Context) KVStore(key stypes.StoreKey) stypes.KVStore { + return gaskv.NewStore(c.RootStore().GetKVStore(key), c.GasMeter(), stypes.KVGasConfig()) } -// TransientStore fetches a TransientStore from the MultiStore. -func (c Context) TransientStore(key storetypes.StoreKey) KVStore { - return gaskv.NewStore(c.MultiStore().GetKVStore(key), c.GasMeter(), storetypes.TransientGasConfig()) +// TransientStore fetches a TransientStore from the RootStore. +func (c Context) TransientStore(key stypes.StoreKey) stypes.KVStore { + return gaskv.NewStore(c.RootStore().GetKVStore(key), c.GasMeter(), stypes.TransientGasConfig()) } // CacheContext returns a new Context with the multi-store cached and a new // EventManager. The cached context is written to the context when writeCache // is called. func (c Context) CacheContext() (cc Context, writeCache func()) { - cms := c.MultiStore().CacheMultiStore() - cc = c.WithMultiStore(cms).WithEventManager(NewEventManager()) - return cc, cms.Write + // TODO replace with constructor? + crs := c.RootStore().CacheRootStore() + cc = c.WithRootStore(crs).WithEventManager(NewEventManager()) + return cc, crs.Write } // ContextKey defines a type alias for a stdlib Context key. diff --git a/types/store.go b/types/store.go index b50f95d02a7a..34b537d005ed 100644 --- a/types/store.go +++ b/types/store.go @@ -6,6 +6,8 @@ import ( "strings" "github.com/cosmos/cosmos-sdk/store/types" + types2 "github.com/cosmos/cosmos-sdk/store/v2" + "github.com/cosmos/cosmos-sdk/store/v2/flat" "github.com/cosmos/cosmos-sdk/types/kv" ) @@ -14,16 +16,22 @@ type ( ) type ( - Store = types.Store - Committer = types.Committer - CommitStore = types.CommitStore - Queryable = types.Queryable - MultiStore = types.MultiStore - CacheMultiStore = types.CacheMultiStore - CommitMultiStore = types.CommitMultiStore - MultiStorePersistentCache = types.MultiStorePersistentCache - KVStore = types.KVStore - Iterator = types.Iterator + Store = types.Store + Committer = types.Committer + CommitStore = types.CommitStore + Queryable = types.Queryable + // MultiStore = types.MultiStore + // CacheMultiStore = types.CacheMultiStore + // CommitMultiStore = types.CommitMultiStore + // MultiStorePersistentCache = types.MultiStorePersistentCache + KVStore = types.KVStore + Iterator = types.Iterator + RootStoreConfig = flat.RootStoreConfig + BasicRootStore = types2.BasicRootStore + // RootStore = types2.RootStore + CommitRootStore = types2.CommitRootStore + CacheRootStore = types2.CacheRootStore + RootStorePersistentCache = types2.RootStorePersistentCache ) // StoreDecoderRegistry defines each of the modules store decoders. Used for ImportExport diff --git a/x/auth/middleware/run_msgs.go b/x/auth/middleware/run_msgs.go index d333e5918995..b9f7e913bfb6 100644 --- a/x/auth/middleware/run_msgs.go +++ b/x/auth/middleware/run_msgs.go @@ -47,6 +47,11 @@ func (txh runMsgsTxHandler) SimulateTx(ctx context.Context, req tx.Request) (tx. // Handler does not exist for a given message route. Otherwise, a reference to a // Result is returned. The caller must not commit state if an error is returned. func (txh runMsgsTxHandler) runMsgs(sdkCtx sdk.Context, msgs []sdk.Msg, txBytes []byte) (tx.Response, error) { + // Create a new Context based off of the existing Context with a RootStore branch + // in case message processing fails. At this point, the RootStore + // is a branch of a branch. + runMsgCtx, msCache := cacheTxContext(sdkCtx, txBytes) + // Attempt to execute all messages and only update state if all messages pass // and we're in DeliverTx. Note, runMsgs will never return a reference to a // Result if any single message fails or does not have a registered Handler. @@ -115,3 +120,22 @@ func (txh runMsgsTxHandler) runMsgs(sdkCtx sdk.Context, msgs []sdk.Msg, txBytes MsgResponses: msgResponses, }, nil } + +// cacheTxContext returns a new context based off of the provided context with +// a branched multi-store. +func cacheTxContext(sdkCtx sdk.Context, txBytes []byte) (sdk.Context, sdk.CacheRootStore) { + ms := sdkCtx.RootStore() + // TODO: https://github.com/cosmos/cosmos-sdk/issues/2824 + msCache := ms.CacheRootStore() + if msCache.TracingEnabled() { + msCache.SetTraceContext( + sdk.TraceContext( + map[string]interface{}{ + "txHash": fmt.Sprintf("%X", tmhash.Sum(txBytes)), + }, + ), + ) + } + + return sdkCtx.WithRootStore(msCache), msCache +} diff --git a/x/params/types/subspace.go b/x/params/types/subspace.go index e528a185eb9e..9c98ce774be7 100644 --- a/x/params/types/subspace.go +++ b/x/params/types/subspace.go @@ -80,7 +80,8 @@ func (s Subspace) kvStore(ctx sdk.Context) sdk.KVStore { func (s Subspace) transientStore(ctx sdk.Context) sdk.KVStore { // append here is safe, appends within a function won't cause // weird side effects when its singlethreaded - return prefix.NewStore(ctx.TransientStore(s.tkey), append(s.name, '/')) + ts := ctx.TransientStore(s.tkey) + return prefix.NewStore(ts, append(s.name, '/')) } // Validate attempts to validate a parameter value by its key. If the key is not diff --git a/x/upgrade/types/storeloader.go b/x/upgrade/types/storeloader.go index 3911effbed30..8ec9a1469146 100644 --- a/x/upgrade/types/storeloader.go +++ b/x/upgrade/types/storeloader.go @@ -2,22 +2,20 @@ package types import ( "github.com/cosmos/cosmos-sdk/baseapp" - storetypes "github.com/cosmos/cosmos-sdk/store/types" + storetypes "github.com/cosmos/cosmos-sdk/store/v2" sdk "github.com/cosmos/cosmos-sdk/types" ) -// UpgradeStoreLoader is used to prepare baseapp with a fixed StoreLoader -// pattern. This is useful for custom upgrade loading logic. -func UpgradeStoreLoader(upgradeHeight int64, storeUpgrades *storetypes.StoreUpgrades) baseapp.StoreLoader { - return func(ms sdk.CommitMultiStore) error { - if upgradeHeight == ms.LastCommitID().Version+1 { - // Check if the current commit version and upgrade height matches +// UpgradeStoreOption is used to prepare baseapp with a fixed StoreOption. +// This is useful for custom upgrade loading logic. +func UpgradeStoreOption(upgradeHeight uint64, storeUpgrades *storetypes.StoreUpgrades) baseapp.StoreOption { + return func(config *sdk.RootStoreConfig, loadHeight uint64) error { + // Check if the current commit version and upgrade height matches + if upgradeHeight == loadHeight+1 { if len(storeUpgrades.Renamed) > 0 || len(storeUpgrades.Deleted) > 0 || len(storeUpgrades.Added) > 0 { - return ms.LoadLatestVersionAndUpgrade(storeUpgrades) + config.Upgrades = append(config.Upgrades, *storeUpgrades) } } - - // Otherwise load default store loader - return baseapp.DefaultStoreLoader(ms) + return nil } } From ec5d41e7daac8fc89a744542ef73ddeeb8e6f46e Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Wed, 27 Oct 2021 19:14:59 +0800 Subject: [PATCH 02/78] baseapp - disable snapshot tests --- baseapp/baseapp_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 3455a85837f8..c07d0549ad2f 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -1828,6 +1828,8 @@ func TestGetMaximumBlockGas(t *testing.T) { } func TestListSnapshots(t *testing.T) { + t.Skip("disabled pending RootStore snapshot implementation") + app, teardown := setupBaseAppWithSnapshots(t, 5, 4) defer teardown() @@ -1845,6 +1847,8 @@ func TestListSnapshots(t *testing.T) { } func TestLoadSnapshotChunk(t *testing.T) { + t.Skip("disabled pending RootStore snapshot implementation") + app, teardown := setupBaseAppWithSnapshots(t, 2, 5) defer teardown() @@ -1880,6 +1884,8 @@ func TestLoadSnapshotChunk(t *testing.T) { } func TestOfferSnapshot_Errors(t *testing.T) { + t.Skip("disabled pending RootStore snapshot implementation") + // Set up app before test cases, since it's fairly expensive. app, teardown := setupBaseAppWithSnapshots(t, 0, 0) defer teardown() @@ -1936,6 +1942,8 @@ func TestOfferSnapshot_Errors(t *testing.T) { } func TestApplySnapshotChunk(t *testing.T) { + t.Skip("disabled pending RootStore snapshot implementation") + source, teardown := setupBaseAppWithSnapshots(t, 4, 10) defer teardown() From 9db21a570dc26ac90917620ddca27648bf1c85bb Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Thu, 20 Jan 2022 02:10:36 -0600 Subject: [PATCH 03/78] store updates, fixes "as cache" glue funcs --- store/v2/multi/cache_store.go | 35 +++++-- store/v2/multi/doc.go | 2 +- store/v2/multi/store.go | 174 +++++++++++++++++++--------------- store/v2/multi/store_test.go | 10 +- store/v2/multi/sub_store.go | 4 +- store/v2/multi/test_util.go | 2 +- store/v2/multi/view_store.go | 70 +++++++------- store/v2/types.go | 13 ++- 8 files changed, 180 insertions(+), 130 deletions(-) diff --git a/store/v2/multi/cache_store.go b/store/v2/multi/cache_store.go index 58e261c2020f..6822382927bc 100644 --- a/store/v2/multi/cache_store.go +++ b/store/v2/multi/cache_store.go @@ -1,10 +1,18 @@ -package root +package multi import ( "github.com/cosmos/cosmos-sdk/store/cachekv" types "github.com/cosmos/cosmos-sdk/store/v2" ) +func newCacheStore(bs types.BasicMultiStore) *cacheStore { + return &cacheStore{ + source: bs, + substores: map[string]types.CacheKVStore{}, + traceListenMixin: newTraceListenMixin(), + } +} + // GetKVStore implements BasicMultiStore. func (cs *cacheStore) GetKVStore(skey types.StoreKey) types.KVStore { key := skey.Name() @@ -27,10 +35,23 @@ func (cs *cacheStore) Write() { // CacheMultiStore implements BasicMultiStore. // This recursively wraps the CacheMultiStore in another cache store. -func (cs *cacheStore) CacheMultiStore() types.CacheMultiStore { - return &cacheStore{ - source: cs, - substores: map[string]types.CacheKVStore{}, - traceListenMixin: newTraceListenMixin(), - } +func (cs *cacheStore) CacheWrap() types.CacheMultiStore { + return newCacheStore(cs) +} + +// A non-writable cache for interface wiring purposes +type noopCacheStore struct { + types.CacheMultiStore +} + +func (noopCacheStore) Write() {} + +// pretend basic store is cache store +func BasicAsCacheStore(bs types.BasicMultiStore) types.CacheMultiStore { + return noopCacheStore{newCacheStore(bs)} +} + +// pretend commit store is cache store +func CommitAsCacheStore(s types.CommitMultiStore) types.CacheMultiStore { + return noopCacheStore{newCacheStore(s)} } diff --git a/store/v2/multi/doc.go b/store/v2/multi/doc.go index 76469ab11a69..a977d4d5bd75 100644 --- a/store/v2/multi/doc.go +++ b/store/v2/multi/doc.go @@ -16,4 +16,4 @@ // of a key's (non)existence within the substore SMT, and a proof of the substore's existence within the // MultiStore (using the Merkle map proof spec (TendermintSpec)). -package root +package multi diff --git a/store/v2/multi/store.go b/store/v2/multi/store.go index 08555da03c61..9649ab0d7591 100644 --- a/store/v2/multi/store.go +++ b/store/v2/multi/store.go @@ -1,4 +1,4 @@ -package root +package multi import ( "errors" @@ -64,7 +64,7 @@ type StoreConfig struct { // If nil, Merkle data is stored in the state storage DB under a separate prefix. StateCommitmentDB dbm.DBConnection - prefixRegistry + SchemaBuilder PersistentCache types.MultiStorePersistentCache Upgrades []types.StoreUpgrades @@ -131,7 +131,7 @@ type viewSubstore struct { } // Builder type used to create a valid schema with no prefix conflicts -type prefixRegistry struct { +type SchemaBuilder struct { StoreSchema reserved []string } @@ -152,7 +152,7 @@ func newTraceListenMixin() *traceListenMixin { func DefaultStoreConfig() StoreConfig { return StoreConfig{ Pruning: types.PruneDefault, - prefixRegistry: prefixRegistry{ + SchemaBuilder: SchemaBuilder{ StoreSchema: StoreSchema{}, }, traceListenMixin: newTraceListenMixin(), @@ -191,8 +191,8 @@ func (this StoreSchema) equal(that StoreSchema) bool { } // Parses a schema from the DB -func readSavedSchema(bucket dbm.DBReader) (*prefixRegistry, error) { - ret := prefixRegistry{StoreSchema: StoreSchema{}} +func readSavedSchema(bucket dbm.DBReader) (*SchemaBuilder, error) { + ret := SchemaBuilder{StoreSchema: StoreSchema{}} it, err := bucket.Iterator(nil, nil) if err != nil { return nil, err @@ -282,55 +282,69 @@ func NewStore(db dbm.DBConnection, opts StoreConfig) (ret *Store, err error) { err = util.CombineErrors(err, ret.Close(), "base.Close also failed") } }() + writeSchema := func(reg *SchemaBuilder) { + schemaWriter := prefixdb.NewPrefixWriter(ret.stateTxn, schemaPrefix) + var it dbm.Iterator + it, err = schemaView.Iterator(nil, nil) + if err != nil { + return + } + for it.Next() { + err = schemaWriter.Delete(it.Key()) + if err != nil { + return + } + } + err = it.Close() + if err != nil { + return + } + err = schemaView.Discard() + if err != nil { + return + } + // NB. the migrated contents and schema are not committed until the next store.Commit + for skey, typ := range reg.StoreSchema { + err = schemaWriter.Set([]byte(skey), []byte{byte(typ)}) + if err != nil { + return + } + } + } + reg, err := readSavedSchema(schemaView) if err != nil { return } // If the loaded schema is empty (for new store), just copy the config schema; - // Otherwise, verify it is identical to the config schema + // Otherwise, migrate, then verify it is identical to the config schema if len(reg.StoreSchema) == 0 { for k, v := range opts.StoreSchema { reg.StoreSchema[k] = v } reg.reserved = make([]string, len(opts.reserved)) copy(reg.reserved, opts.reserved) + writeSchema(reg) } else { + // Apply migrations to the schema + for _, upgrades := range opts.Upgrades { + err = reg.MigrateSchema(upgrades) + if err != nil { + return + } + } if !reg.equal(opts.StoreSchema) { err = errors.New("loaded schema does not match configured schema") return } - } - // Apply migrations, then clear old schema and write the new one - for _, upgrades := range opts.Upgrades { - err = reg.migrate(ret, upgrades) - if err != nil { - return - } - } - schemaWriter := prefixdb.NewPrefixWriter(ret.stateTxn, schemaPrefix) - it, err := schemaView.Iterator(nil, nil) - if err != nil { - return - } - for it.Next() { - err = schemaWriter.Delete(it.Key()) - if err != nil { - return + for _, upgrades := range opts.Upgrades { + err = migrateData(ret, upgrades) + if err != nil { + return + } } - } - err = it.Close() - if err != nil { - return - } - err = schemaView.Discard() - if err != nil { - return - } - // NB. the migrated contents and schema are not committed until the next store.Commit - for skey, typ := range reg.StoreSchema { - err = schemaWriter.Set([]byte(skey), []byte{byte(typ)}) - if err != nil { - return + if len(opts.Upgrades) != 0 { + writeSchema(reg) } } ret.schema = reg.StoreSchema @@ -346,7 +360,7 @@ func (s *Store) Close() error { } // Applies store upgrades to the DB contents. -func (pr *prefixRegistry) migrate(store *Store, upgrades types.StoreUpgrades) error { +func migrateData(store *Store, upgrades types.StoreUpgrades) error { // Get a view of current state to allow mutation while iterating reader := store.stateDB.Reader() scReader := reader @@ -355,16 +369,6 @@ func (pr *prefixRegistry) migrate(store *Store, upgrades types.StoreUpgrades) er } for _, key := range upgrades.Deleted { - sst, ix, err := pr.storeInfo(key) - if err != nil { - return err - } - if sst != types.StoreTypePersistent { - return fmt.Errorf("prefix is for non-persistent substore: %v (%v)", key, sst) - } - pr.reserved = append(pr.reserved[:ix], pr.reserved[ix+1:]...) - delete(pr.StoreSchema, key) - pfx := substorePrefix(key) subReader := prefixdb.NewPrefixReader(reader, pfx) it, err := subReader.Iterator(nil, nil) @@ -388,20 +392,6 @@ func (pr *prefixRegistry) migrate(store *Store, upgrades types.StoreUpgrades) er } } for _, rename := range upgrades.Renamed { - sst, ix, err := pr.storeInfo(rename.OldKey) - if err != nil { - return err - } - if sst != types.StoreTypePersistent { - return fmt.Errorf("prefix is for non-persistent substore: %v (%v)", rename.OldKey, sst) - } - pr.reserved = append(pr.reserved[:ix], pr.reserved[ix+1:]...) - delete(pr.StoreSchema, rename.OldKey) - err = pr.RegisterSubstore(rename.NewKey, types.StoreTypePersistent) - if err != nil { - return err - } - oldPrefix := substorePrefix(rename.OldKey) newPrefix := substorePrefix(rename.NewKey) subReader := prefixdb.NewPrefixReader(reader, oldPrefix) @@ -427,13 +417,6 @@ func (pr *prefixRegistry) migrate(store *Store, upgrades types.StoreUpgrades) er it.Close() } } - - for _, key := range upgrades.Added { - err := pr.RegisterSubstore(key, types.StoreTypePersistent) - if err != nil { - return err - } - } return nil } @@ -680,12 +663,8 @@ func (rs *Store) GetVersion(version int64) (types.BasicMultiStore, error) { } // CacheMultiStore implements BasicMultiStore. -func (rs *Store) CacheMultiStore() types.CacheMultiStore { - return &cacheStore{ - source: rs, - substores: map[string]types.CacheKVStore{}, - traceListenMixin: newTraceListenMixin(), - } +func (rs *Store) CacheWrap() types.CacheMultiStore { + return newCacheStore(rs) } // parsePath expects a format like /[/] @@ -824,7 +803,44 @@ func binarySearch(hay []string, ndl string) (int, bool) { return from, false } -func (pr *prefixRegistry) storeInfo(key string) (sst types.StoreType, ix int, err error) { +// Migrates the state of the registry based on the upgrades +func (pr *SchemaBuilder) MigrateSchema(upgrades types.StoreUpgrades) error { + for _, key := range upgrades.Deleted { + sst, ix, err := pr.storeInfo(key) + if err != nil { + return err + } + if sst != types.StoreTypePersistent { + return fmt.Errorf("prefix is for non-persistent substore: %v (%v)", key, sst) + } + pr.reserved = append(pr.reserved[:ix], pr.reserved[ix+1:]...) + delete(pr.StoreSchema, key) + } + for _, rename := range upgrades.Renamed { + sst, ix, err := pr.storeInfo(rename.OldKey) + if err != nil { + return err + } + if sst != types.StoreTypePersistent { + return fmt.Errorf("prefix is for non-persistent substore: %v (%v)", rename.OldKey, sst) + } + pr.reserved = append(pr.reserved[:ix], pr.reserved[ix+1:]...) + delete(pr.StoreSchema, rename.OldKey) + err = pr.RegisterSubstore(rename.NewKey, types.StoreTypePersistent) + if err != nil { + return err + } + } + for _, key := range upgrades.Added { + err := pr.RegisterSubstore(key, types.StoreTypePersistent) + if err != nil { + return err + } + } + return nil +} + +func (pr *SchemaBuilder) storeInfo(key string) (sst types.StoreType, ix int, err error) { ix, has := binarySearch(pr.reserved, key) if !has { err = fmt.Errorf("prefix does not exist: %v", key) @@ -838,7 +854,7 @@ func (pr *prefixRegistry) storeInfo(key string) (sst types.StoreType, ix int, er return } -func (pr *prefixRegistry) RegisterSubstore(key string, typ types.StoreType) error { +func (pr *SchemaBuilder) RegisterSubstore(key string, typ types.StoreType) error { if !validSubStoreType(typ) { return fmt.Errorf("StoreType not supported: %v", typ) } @@ -880,7 +896,7 @@ func (tlm *traceListenMixin) TracingEnabled() bool { func (tlm *traceListenMixin) SetTracer(w io.Writer) { tlm.TraceWriter = w } -func (tlm *traceListenMixin) SetTraceContext(tc types.TraceContext) { +func (tlm *traceListenMixin) SetTracingContext(tc types.TraceContext) { tlm.TraceContext = tc } diff --git a/store/v2/multi/store_test.go b/store/v2/multi/store_test.go index 435277fcbae5..f8e9de24853f 100644 --- a/store/v2/multi/store_test.go +++ b/store/v2/multi/store_test.go @@ -1,4 +1,4 @@ -package root +package multi import ( "bytes" @@ -754,8 +754,12 @@ func TestMultiStoreMigration(t *testing.T) { Deleted: []string{skey_3.Name()}, }, } + // store must be loaded with post-migration schema, so this fails + store, err = NewStore(db, opts) + require.Error(t, err) + + opts.MigrateSchema(opts.Upgrades[0]) store, err = NewStore(db, opts) - require.Nil(t, err) // s1 was not changed s1 = store.GetKVStore(skey_1) @@ -863,7 +867,7 @@ func TestTrace(t *testing.T) { store, err := NewStore(db, opts) require.NoError(t, err) - store.SetTraceContext(tctx) + store.SetTracingContext(tctx) require.False(t, store.TracingEnabled()) var buf bytes.Buffer diff --git a/store/v2/multi/sub_store.go b/store/v2/multi/sub_store.go index e11e8b0d5440..e6fb38c94350 100644 --- a/store/v2/multi/sub_store.go +++ b/store/v2/multi/sub_store.go @@ -1,4 +1,4 @@ -package root +package multi import ( "crypto/sha256" @@ -10,7 +10,7 @@ import ( "github.com/cosmos/cosmos-sdk/store/cachekv" "github.com/cosmos/cosmos-sdk/store/listenkv" "github.com/cosmos/cosmos-sdk/store/tracekv" - "github.com/cosmos/cosmos-sdk/store/types" + types "github.com/cosmos/cosmos-sdk/store/v2" ) // Get implements KVStore. diff --git a/store/v2/multi/test_util.go b/store/v2/multi/test_util.go index 777e59cc2b06..cc03639906d9 100644 --- a/store/v2/multi/test_util.go +++ b/store/v2/multi/test_util.go @@ -1,4 +1,4 @@ -package root +package multi import ( "bytes" diff --git a/store/v2/multi/view_store.go b/store/v2/multi/view_store.go index 85eaeb6e7cfe..40155416f3fd 100644 --- a/store/v2/multi/view_store.go +++ b/store/v2/multi/view_store.go @@ -1,4 +1,4 @@ -package root +package multi import ( "errors" @@ -17,6 +17,42 @@ import ( var ErrReadOnly = errors.New("cannot modify read-only store") +func (vs *viewStore) GetKVStore(skey types.StoreKey) types.KVStore { + key := skey.Name() + if _, has := vs.schema[key]; !has { + panic(ErrStoreNotFound(key)) + } + ret, err := vs.getSubstore(key) + if err != nil { + panic(err) + } + vs.substoreCache[key] = ret + return ret +} + +// Reads but does not update substore cache +func (vs *viewStore) getSubstore(key string) (*viewSubstore, error) { + if cached, has := vs.substoreCache[key]; has { + return cached, nil + } + pfx := substorePrefix(key) + stateR := prefixdb.NewPrefixReader(vs.stateView, pfx) + stateCommitmentR := prefixdb.NewPrefixReader(vs.stateCommitmentView, pfx) + rootHash, err := stateR.Get(merkleRootKey) + if err != nil { + return nil, err + } + return &viewSubstore{ + dataBucket: prefixdb.NewPrefixReader(stateR, dataPrefix), + indexBucket: prefixdb.NewPrefixReader(stateR, indexPrefix), + stateCommitmentStore: loadSMT(dbm.ReaderAsReadWriter(stateCommitmentR), rootHash), + }, nil +} + +func (vs *viewStore) CacheWrap() types.CacheMultiStore { + return newCacheStore(vs) +} + func (s *viewSubstore) GetStateCommitmentStore() *smt.Store { return s.stateCommitmentStore } @@ -127,35 +163,3 @@ func (store *Store) getView(version int64) (ret *viewStore, err error) { } return } - -func (vs *viewStore) GetKVStore(skey types.StoreKey) types.KVStore { - key := skey.Name() - if _, has := vs.schema[key]; !has { - panic(ErrStoreNotFound(key)) - } - ret, err := vs.getSubstore(key) - if err != nil { - panic(err) - } - vs.substoreCache[key] = ret - return ret -} - -// Reads but does not update substore cache -func (vs *viewStore) getSubstore(key string) (*viewSubstore, error) { - if cached, has := vs.substoreCache[key]; has { - return cached, nil - } - pfx := substorePrefix(key) - stateR := prefixdb.NewPrefixReader(vs.stateView, pfx) - stateCommitmentR := prefixdb.NewPrefixReader(vs.stateCommitmentView, pfx) - rootHash, err := stateR.Get(merkleRootKey) - if err != nil { - return nil, err - } - return &viewSubstore{ - dataBucket: prefixdb.NewPrefixReader(stateR, dataPrefix), - indexBucket: prefixdb.NewPrefixReader(stateR, indexPrefix), - stateCommitmentStore: loadSMT(dbm.ReaderAsReadWriter(stateCommitmentR), rootHash), - }, nil -} diff --git a/store/v2/types.go b/store/v2/types.go index 6975cbdc49c3..9a18ab42cef3 100644 --- a/store/v2/types.go +++ b/store/v2/types.go @@ -56,6 +56,9 @@ var ( KVStoreReversePrefixIterator = v1.KVStoreReversePrefixIterator NewStoreKVPairWriteListener = v1.NewStoreKVPairWriteListener + + AssertValidKey = v1.AssertValidKey + AssertValidValue = v1.AssertValidValue ) // BasicMultiStore defines a minimal interface for accessing root state. @@ -63,13 +66,17 @@ type BasicMultiStore interface { // Returns a KVStore which has access only to the namespace of the StoreKey. // Panics if the key is not found in the schema. GetKVStore(StoreKey) KVStore + // Returns a branched whose modifications are later merged back in. + CacheWrap() CacheMultiStore } +type MultiStore = BasicMultiStore + // mixin interface for trace and listen methods type rootStoreTraceListen interface { TracingEnabled() bool SetTracer(w io.Writer) - SetTraceContext(TraceContext) + SetTracingContext(TraceContext) ListeningEnabled(key StoreKey) bool AddListeners(key StoreKey, listeners []WriteListener) } @@ -87,8 +94,6 @@ type CommitMultiStore interface { GetVersion(int64) (BasicMultiStore, error) // Closes the store and all backing transactions. Close() error - // Returns a branched whose modifications are later merged back in. - CacheMultiStore() CacheMultiStore // Defines the minimum version number that can be saved by this store. SetInitialVersion(uint64) error } @@ -99,7 +104,7 @@ type CacheMultiStore interface { rootStoreTraceListen // Returns a branched whose modifications are later merged back in. - CacheMultiStore() CacheMultiStore + CacheWrap() CacheMultiStore // Write all cached changes back to the source store. Note: this overwrites any intervening changes. Write() } From ceabd08f43ffadca8be81ded33a931c75e8bee2a Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Tue, 18 Jan 2022 17:44:55 -0600 Subject: [PATCH 04/78] updates, rf fix storeloader test --- baseapp/abci.go | 8 ++- baseapp/abci_test.go | 16 ++--- baseapp/baseapp.go | 47 ++++++------ baseapp/baseapp_test.go | 54 +++++++------- baseapp/custom_txhandler_test.go | 14 ++-- baseapp/options.go | 23 +++--- baseapp/state.go | 10 +-- baseapp/test_helpers.go | 11 ++- baseapp/util_test.go | 2 +- go.mod | 2 + go.sum | 2 + server/export_test.go | 26 +++---- server/mock/app.go | 15 ++-- server/mock/store.go | 108 +++++++--------------------- server/mock/store_test.go | 10 +-- server/util.go | 6 +- simapp/app.go | 21 ++---- simapp/app_test.go | 4 +- simapp/export.go | 21 ++++-- simapp/simd/cmd/root.go | 5 +- simapp/utils.go | 3 +- snapshots/helpers_test.go | 4 +- snapshots/store_test.go | 10 +-- store/rootmulti/store_test.go | 2 +- testutil/context.go | 20 ++++-- types/context.go | 49 ++++++------- types/store.go | 16 ++--- x/auth/middleware/branch_store.go | 6 +- x/auth/middleware/run_msgs.go | 24 ------- x/auth/module_test.go | 4 +- x/capability/genesis_test.go | 4 +- x/capability/keeper/keeper_test.go | 2 +- x/distribution/module_test.go | 4 +- x/gov/genesis_test.go | 4 +- x/gov/module_test.go | 4 +- x/mint/module_test.go | 4 +- x/params/types/subspace_test.go | 15 ++-- x/staking/module_test.go | 6 +- x/upgrade/abci_test.go | 4 +- x/upgrade/types/storeloader.go | 4 +- x/upgrade/types/storeloader_test.go | 89 ++++++++++++----------- 41 files changed, 305 insertions(+), 378 deletions(-) diff --git a/baseapp/abci.go b/baseapp/abci.go index ec80acc84b45..d039ee637510 100644 --- a/baseapp/abci.go +++ b/baseapp/abci.go @@ -17,6 +17,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" snapshottypes "github.com/cosmos/cosmos-sdk/snapshots/types" + "github.com/cosmos/cosmos-sdk/store/v2/multi" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/types/tx" @@ -137,7 +138,7 @@ func (app *BaseApp) FilterPeerByID(info string) abci.ResponseQuery { func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeginBlock) { if app.store.TracingEnabled() { - app.store.SetTraceContext(sdk.TraceContext( + app.store.SetTracingContext(sdk.TraceContext( map[string]interface{}{"blockHeight": req.Header.Height}, )) } @@ -203,7 +204,7 @@ func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeg func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBlock) { if app.deliverState.ms.TracingEnabled() { - app.deliverState.ms.SetTraceContext(nil) + app.deliverState.ms.SetTracingContext(nil) } if app.endBlocker != nil { @@ -637,7 +638,7 @@ func (app *BaseApp) createQueryContext(height int64, prove bool) (sdk.Context, e ) } - cacheMS, err := app.store.GetVersion(height) + version, err := app.store.GetVersion(height) if err != nil { return sdk.Context{}, sdkerrors.Wrapf( @@ -645,6 +646,7 @@ func (app *BaseApp) createQueryContext(height int64, prove bool) (sdk.Context, e "failed to load state at height %d; %s (latest height: %d)", height, err, app.LastBlockHeight(), ) } + cacheMS := multi.BasicAsCacheStore(version) // branch the commit-multistore for safety ctx := sdk.NewContext( diff --git a/baseapp/abci_test.go b/baseapp/abci_test.go index 39f3c464721d..173e0529f634 100644 --- a/baseapp/abci_test.go +++ b/baseapp/abci_test.go @@ -24,20 +24,20 @@ func TestGetBlockRentionHeight(t *testing.T) { expected int64 }{ "defaults": { - bapp: baseapp.NewBaseApp(name, logger, memdb.NewDB(), nil), + bapp: baseapp.NewBaseApp(name, logger, memdb.NewDB()), maxAgeBlocks: 0, commitHeight: 499000, expected: 0, }, "pruning unbonding time only": { - bapp: baseapp.NewBaseApp(name, logger, memdb.NewDB(), nil, baseapp.SetMinRetainBlocks(1)), + bapp: baseapp.NewBaseApp(name, logger, memdb.NewDB(), baseapp.SetMinRetainBlocks(1)), maxAgeBlocks: 362880, commitHeight: 499000, expected: 136120, }, "pruning iavl snapshot only": { bapp: baseapp.NewBaseApp( - name, logger, memdb.NewDB(), nil, + name, logger, memdb.NewDB(), baseapp.SetPruning(sdk.PruningOptions{KeepEvery: 10000}), baseapp.SetMinRetainBlocks(1), ), @@ -47,7 +47,7 @@ func TestGetBlockRentionHeight(t *testing.T) { }, "pruning state sync snapshot only": { bapp: baseapp.NewBaseApp( - name, logger, memdb.NewDB(), nil, + name, logger, memdb.NewDB(), baseapp.SetSnapshotInterval(50000), baseapp.SetSnapshotKeepRecent(3), baseapp.SetMinRetainBlocks(1), @@ -58,7 +58,7 @@ func TestGetBlockRentionHeight(t *testing.T) { }, "pruning min retention only": { bapp: baseapp.NewBaseApp( - name, logger, memdb.NewDB(), nil, + name, logger, memdb.NewDB(), baseapp.SetMinRetainBlocks(400000), ), maxAgeBlocks: 0, @@ -67,7 +67,7 @@ func TestGetBlockRentionHeight(t *testing.T) { }, "pruning all conditions": { bapp: baseapp.NewBaseApp( - name, logger, memdb.NewDB(), nil, + name, logger, memdb.NewDB(), baseapp.SetPruning(sdk.PruningOptions{KeepEvery: 10000}), baseapp.SetMinRetainBlocks(400000), baseapp.SetSnapshotInterval(50000), baseapp.SetSnapshotKeepRecent(3), @@ -78,7 +78,7 @@ func TestGetBlockRentionHeight(t *testing.T) { }, "no pruning due to no persisted state": { bapp: baseapp.NewBaseApp( - name, logger, memdb.NewDB(), nil, + name, logger, memdb.NewDB(), baseapp.SetPruning(sdk.PruningOptions{KeepEvery: 10000}), baseapp.SetMinRetainBlocks(400000), baseapp.SetSnapshotInterval(50000), baseapp.SetSnapshotKeepRecent(3), @@ -89,7 +89,7 @@ func TestGetBlockRentionHeight(t *testing.T) { }, "disable pruning": { bapp: baseapp.NewBaseApp( - name, logger, memdb.NewDB(), nil, + name, logger, memdb.NewDB(), baseapp.SetPruning(sdk.PruningOptions{KeepEvery: 10000}), baseapp.SetMinRetainBlocks(0), baseapp.SetSnapshotInterval(50000), baseapp.SetSnapshotKeepRecent(3), diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 0bd72870cb1b..f677daa947be 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -16,7 +16,7 @@ import ( // "github.com/cosmos/cosmos-sdk/store" // "github.com/cosmos/cosmos-sdk/store/rootmulti" stypes "github.com/cosmos/cosmos-sdk/store/v2" - "github.com/cosmos/cosmos-sdk/store/v2/flat" + "github.com/cosmos/cosmos-sdk/store/v2/multi" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/tx" ) @@ -36,17 +36,18 @@ type ( // Enum mode for app.runTx runTxMode uint8 - // StoreLoader defines a customizable function to control how we load the CommitRootStore + // StoreLoader defines a customizable function to control how we load the CommitMultiStore // from disk. This is useful for state migration, when loading a datastore written with // an older version of the software. In particular, if a module changed the substore key name // (or removed a substore) between two versions of the software. - // StoreLoader func(ms sdk.CommitRootStore) error - - // StoreLoader func(initialVersion uint64) (sdk.CommitRootStore, error) - // StoreLoader func(sdk.RootStoreConfig) (sdk.CommitRootStore, error) - // StoreLoader func(*sdk.RootStoreConfig, uint64) (sdk.CommitRootStore, error) - StoreOption func(*sdk.RootStoreConfig, uint64) error - StoreConstructor func(dbm.DBConnection, sdk.RootStoreConfig) (sdk.CommitRootStore, error) + // StoreLoader func(ms sdk.CommitMultiStore) error + + // StoreLoader func(initialVersion uint64) (sdk.CommitMultiStore, error) + // StoreLoader func(multi.StoreConfig) (sdk.CommitMultiStore, error) + // StoreLoader func(*multi.StoreConfig, uint64) (sdk.CommitMultiStore, error) + StoreConfig = multi.StoreConfig + StoreOption func(*StoreConfig, uint64) error + StoreConstructor func(dbm.DBConnection, multi.StoreConfig) (sdk.CommitMultiStore, error) ) // BaseApp reflects the ABCI application implementation. @@ -56,8 +57,8 @@ type BaseApp struct { // nolint: maligned name string // application name from abci.Info db dbm.DBConnection storeCtor StoreConstructor - storeOpts []StoreOption // options to configure root store - store sdk.CommitRootStore // Main (uncached) state + storeOpts []StoreOption // options to configure root store + store sdk.CommitMultiStore // Main (uncached) state // storeLoader StoreLoader // function to handle store loading queryRouter sdk.QueryRouter // router for redirecting query calls grpcQueryRouter *GRPCQueryRouter // router for redirecting gRPC query calls @@ -163,6 +164,7 @@ func (opt AppOptionOrdered) Order() OptionOrder { return opt.order } func (opt AppOptionFunc) Apply(app *BaseApp) { opt(app) } func (opt AppOptionFunc) Order() OptionOrder { return OptionOrderDefault } +// StoreOption implements AppOption, and can be passed to the app constructor. func (opt StoreOption) Apply(app *BaseApp) { app.storeOpts = append(app.storeOpts, opt) } func (opt StoreOption) Order() OptionOrder { return OptionOrderDefault } @@ -175,7 +177,6 @@ func NewBaseApp( name string, logger log.Logger, db dbm.DBConnection, - txDecoder sdk.TxDecoder, options ...AppOption, ) *BaseApp { app := &BaseApp{ @@ -206,16 +207,14 @@ func NewBaseApp( } // // TODO: conditional loading of multistore/rootstore - // if true { - // var err error - // opts := store.RootStoreConfig{PersistentCache: app.interBlockCache} - // app.store, err = store.NewCommitRootStore(db, opts) + // if loadv2 { + // opts := store.StoreConfig{PersistentCache: app.interBlockCache} + // store, err := multi.NewStore(db, opts) // if err != nil { // panic(err) // } // } else { - // // app.store = nil store.NewCommitMultiStore(dbutil.ConnectionAsTmdb(db)) - // app.store = store.MultiStoreAsRootStore(db) + // app.store = NewCommitMultiStore(dbutil.ConnectionAsTmdb(db)) // } return app @@ -252,13 +251,13 @@ func (app *BaseApp) loadStore() error { return err } latest := versions.Last() - config := flat.DefaultRootStoreConfig() + config := multi.DefaultStoreConfig() for _, opt := range app.storeOpts { opt(&config, latest) } app.store, err = app.storeCtor(app.db, config) if err != nil { - return fmt.Errorf("failed to load latest version: %w", err) + return fmt.Errorf("failed to load store: %w", err) } return nil } @@ -268,8 +267,8 @@ func (app *BaseApp) CloseStore() error { } // DefaultStoreConstructor attempts to create a new store, but loads from existing data if present. -func DefaultStoreConstructor(db dbm.DBConnection, config sdk.RootStoreConfig) (stypes.CommitRootStore, error) { - return flat.NewRootStore(db, config) +func DefaultStoreConstructor(db dbm.DBConnection, config multi.StoreConfig) (stypes.CommitMultiStore, error) { + return multi.NewStore(db, config) } // LastCommitID returns the last CommitID of the multistore. @@ -348,7 +347,7 @@ func (app *BaseApp) IsSealed() bool { return app.sealed } // provided header, and minimum gas prices set. It is set on InitChain and reset // on Commit. func (app *BaseApp) setCheckState(header tmproto.Header) { - ms := app.store.CacheRootStore() + ms := app.store.CacheWrap() app.checkState = &state{ ms: ms, ctx: sdk.NewContext(ms, header, true, app.logger).WithMinGasPrices(app.minGasPrices), @@ -360,7 +359,7 @@ func (app *BaseApp) setCheckState(header tmproto.Header) { // and provided header. It is set on InitChain and BeginBlock and set to nil on // Commit. func (app *BaseApp) setDeliverState(header tmproto.Header) { - ms := app.store.CacheRootStore() + ms := app.store.CacheWrap() app.deliverState = &state{ ms: ms, ctx: sdk.NewContext(ms, header, false, app.logger), diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index c07d0549ad2f..a76ce604b826 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -30,7 +30,7 @@ import ( "github.com/cosmos/cosmos-sdk/snapshots" snapshottypes "github.com/cosmos/cosmos-sdk/snapshots/types" stypes "github.com/cosmos/cosmos-sdk/store/v2" - "github.com/cosmos/cosmos-sdk/store/v2/flat" + "github.com/cosmos/cosmos-sdk/store/v2/multi" "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -121,7 +121,7 @@ func aminoTxEncoder(cdc *codec.LegacyAmino) sdk.TxEncoder { // simple one store baseapp func setupBaseApp(t *testing.T, options ...baseapp.AppOption) *baseapp.BaseApp { - options = append(options, baseapp.SetStorePrefixes(capKey1, capKey2)) + options = append(options, baseapp.SetSubstores(capKey1, capKey2)) app := newBaseApp(t.Name(), options...) require.Equal(t, t.Name(), app.Name()) @@ -270,19 +270,19 @@ func (th MockTxHandler) SimulateTx(goCtx context.Context, req tx.Request) (tx.Re } func TestConsensusParamsNotNil(t *testing.T) { - app := setupBaseApp(t, func(app *baseapp.BaseApp) { + app := setupBaseApp(t, baseapp.AppOptionFunc(func(app *baseapp.BaseApp) { app.SetBeginBlocker(func(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock { require.NotNil(t, ctx.ConsensusParams()) return abci.ResponseBeginBlock{} }) - }, func(app *baseapp.BaseApp) { + }), baseapp.AppOptionFunc(func(app *baseapp.BaseApp) { app.SetEndBlocker(func(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock { require.NotNil(t, ctx.ConsensusParams()) return abci.ResponseEndBlock{} }) - }, func(app *baseapp.BaseApp) { + }), baseapp.AppOptionFunc(func(app *baseapp.BaseApp) { app.SetTxHandler(MockTxHandler{T: t}) - }) + })) header := tmproto.Header{Height: 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) @@ -326,7 +326,7 @@ func TestLoadVersion(t *testing.T) { app.CloseStore() // reload with LoadLatestVersion - app = baseapp.NewBaseApp(name, logger, db, nil, pruningOpt) + app = baseapp.NewBaseApp(name, logger, db, pruningOpt) app.SetStoreConstructor(baseapp.DefaultStoreConstructor) err = app.Init() require.Nil(t, err) @@ -339,10 +339,10 @@ var useDefaultConstructor = baseapp.AppOptionFunc(func(app *baseapp.BaseApp) { func initStore(t *testing.T, db dbm.DBConnection, storeKey string, k, v []byte) { key := sdk.NewKVStoreKey(storeKey) - opts := flat.DefaultRootStoreConfig() + opts := multi.DefaultStoreConfig() opts.Pruning = stypes.PruneNothing - require.NoError(t, opts.ReservePrefix(key, stypes.StoreTypePersistent)) - rs, err := flat.NewRootStore(db, opts) + require.NoError(t, opts.RegisterSubstore(key.Name(), stypes.StoreTypePersistent)) + rs, err := multi.NewStore(db, opts) require.NoError(t, err) require.Equal(t, int64(0), rs.LastCommitID().Version) @@ -356,10 +356,10 @@ func initStore(t *testing.T, db dbm.DBConnection, storeKey string, k, v []byte) } func checkStore(t *testing.T, db dbm.DBConnection, ver int64, storeKey string, k, v []byte) { - opts := flat.DefaultRootStoreConfig() + opts := multi.DefaultStoreConfig() opts.Pruning = stypes.PruneNothing key := sdk.NewKVStoreKey(storeKey) - require.NoError(t, opts.ReservePrefix(key, stypes.StoreTypePersistent)) + require.NoError(t, opts.RegisterSubstore(key.Name(), stypes.StoreTypePersistent)) rs, err := baseapp.DefaultStoreConstructor(db, opts) require.NoError(t, err) require.Equal(t, ver, rs.LastCommitID().Version) @@ -403,12 +403,12 @@ func TestSetConstructor(t *testing.T) { // load the app with the existing db opts := []baseapp.AppOption{ baseapp.SetPruning(stypes.PruneNothing), - baseapp.SetStorePrefixes(sdk.NewKVStoreKey(tc.loadStoreKey)), + baseapp.SetSubstores(sdk.NewKVStoreKey(tc.loadStoreKey)), } if tc.setConstructor != nil { opts = append(opts, tc.setConstructor) } - app := baseapp.NewBaseApp(t.Name(), defaultLogger(), db, nil, opts...) + app := baseapp.NewBaseApp(t.Name(), defaultLogger(), db, opts...) err := app.Init() require.Nil(t, err) @@ -430,7 +430,7 @@ func TestVersionSetterGetter(t *testing.T) { pruningOpt := baseapp.SetPruning(stypes.PruneDefault) db := memdb.NewDB() name := t.Name() - app := baseapp.NewBaseApp(name, logger, db, nil, pruningOpt) + app := baseapp.NewBaseApp(name, logger, db, pruningOpt) require.Equal(t, "", app.Version()) res := app.Query(abci.RequestQuery{Path: "app/version"}) require.True(t, res.IsOK()) @@ -452,10 +452,10 @@ func TestLoadVersionPruning(t *testing.T) { Interval: 1, } pruningOpt := baseapp.SetPruning(pruningOptions) - schemaOpt := baseapp.SetStorePrefixes(sdk.NewKVStoreKey("key1")) + schemaOpt := baseapp.SetSubstores(sdk.NewKVStoreKey("key1")) db := memdb.NewDB() name := t.Name() - app := baseapp.NewBaseApp(name, logger, db, nil, pruningOpt, schemaOpt) + app := baseapp.NewBaseApp(name, logger, db, pruningOpt, schemaOpt) err := app.Init() // needed to make stores non-nil require.Nil(t, err) @@ -492,7 +492,7 @@ func TestLoadVersionPruning(t *testing.T) { require.NoError(t, app.CloseStore()) // reload app, check it loads last version - app = baseapp.NewBaseApp(name, logger, db, nil, pruningOpt, schemaOpt) + app = baseapp.NewBaseApp(name, logger, db, pruningOpt, schemaOpt) err = app.Init() require.Nil(t, err) @@ -509,7 +509,7 @@ func testLoadVersionHelper(t *testing.T, app *baseapp.BaseApp, expectedHeight in func TestOptionFunction(t *testing.T) { logger := defaultLogger() db := memdb.NewDB() - bap := baseapp.NewBaseApp("starting name", logger, db, nil, testChangeNameHelper("new name")) + bap := baseapp.NewBaseApp("starting name", logger, db, testChangeNameHelper("new name")) require.Equal(t, bap.Name(), "new name", "BaseApp should have had name changed via option function") } @@ -581,8 +581,8 @@ func TestInitChainer(t *testing.T) { logger := defaultLogger() capKey := sdk.NewKVStoreKey("main") capKey2 := sdk.NewKVStoreKey("key2") - schemaOpt := baseapp.SetStorePrefixes(capKey, capKey2) - app := baseapp.NewBaseApp(name, logger, db, nil, schemaOpt) + schemaOpt := baseapp.SetSubstores(capKey, capKey2) + app := baseapp.NewBaseApp(name, logger, db, schemaOpt) // set a value in the store on init chain key, value := []byte("hello"), []byte("goodbye") @@ -636,7 +636,7 @@ func TestInitChainer(t *testing.T) { require.NoError(t, app.CloseStore()) // reload app - app = baseapp.NewBaseApp(name, logger, db, nil, schemaOpt) + app = baseapp.NewBaseApp(name, logger, db, schemaOpt) app.SetInitChainer(initChainer) err = app.Init() @@ -1828,7 +1828,7 @@ func TestGetMaximumBlockGas(t *testing.T) { } func TestListSnapshots(t *testing.T) { - t.Skip("disabled pending RootStore snapshot implementation") + t.Skip("disabled pending MultiStore snapshot implementation") app, teardown := setupBaseAppWithSnapshots(t, 5, 4) defer teardown() @@ -1847,7 +1847,7 @@ func TestListSnapshots(t *testing.T) { } func TestLoadSnapshotChunk(t *testing.T) { - t.Skip("disabled pending RootStore snapshot implementation") + t.Skip("disabled pending MultiStore snapshot implementation") app, teardown := setupBaseAppWithSnapshots(t, 2, 5) defer teardown() @@ -1884,7 +1884,7 @@ func TestLoadSnapshotChunk(t *testing.T) { } func TestOfferSnapshot_Errors(t *testing.T) { - t.Skip("disabled pending RootStore snapshot implementation") + t.Skip("disabled pending MultiStore snapshot implementation") // Set up app before test cases, since it's fairly expensive. app, teardown := setupBaseAppWithSnapshots(t, 0, 0) @@ -1942,7 +1942,7 @@ func TestOfferSnapshot_Errors(t *testing.T) { } func TestApplySnapshotChunk(t *testing.T) { - t.Skip("disabled pending RootStore snapshot implementation") + t.Skip("disabled pending MultiStore snapshot implementation") source, teardown := setupBaseAppWithSnapshots(t, 4, 10) defer teardown() @@ -2065,7 +2065,7 @@ func TestBaseApp_EndBlock(t *testing.T) { }, } - app := baseapp.NewBaseApp(name, logger, db, nil) + app := baseapp.NewBaseApp(name, logger, db) app.SetParamStore(newParamStore(memdb.NewDB())) app.InitChain(abci.RequestInitChain{ ConsensusParams: cp, diff --git a/baseapp/custom_txhandler_test.go b/baseapp/custom_txhandler_test.go index 9b4183ae2884..4a2a4c4f82a6 100644 --- a/baseapp/custom_txhandler_test.go +++ b/baseapp/custom_txhandler_test.go @@ -65,7 +65,7 @@ func (txh customTxHandler) runHandler(ctx context.Context, tx sdk.Tx, txBytes [] return sdkCtx, nil } - store := sdkCtx.RootStore() + store := sdkCtx.MultiStore() // Branch context before Handler call in case it aborts. // This is required for both CheckTx and DeliverTx. @@ -88,7 +88,7 @@ func (txh customTxHandler) runHandler(ctx context.Context, tx sdk.Tx, txBytes [] // Also, in the case of the tx aborting, we need to track gas consumed via // the instantiated gas meter in the Handler, so we update the context // prior to returning. - sdkCtx = newCtx.WithRootStore(store) + sdkCtx = newCtx.WithMultiStore(store) } storeCache.Write() @@ -98,12 +98,12 @@ func (txh customTxHandler) runHandler(ctx context.Context, tx sdk.Tx, txBytes [] // cacheTxContext returns a new context based off of the provided context with // a branched multi-store. -func cacheTxContext(sdkCtx sdk.Context, txBytes []byte) (sdk.Context, sdk.CacheRootStore) { - store := sdkCtx.RootStore() +func cacheTxContext(sdkCtx sdk.Context, txBytes []byte) (sdk.Context, sdk.CacheMultiStore) { + store := sdkCtx.MultiStore() // TODO: https://github.com/cosmos/cosmos-sdk/issues/2824 - storeCache := store.CacheRootStore() + storeCache := store.CacheWrap() if storeCache.TracingEnabled() { - storeCache.SetTraceContext( + storeCache.SetTracingContext( sdk.TraceContext( map[string]interface{}{ "txHash": fmt.Sprintf("%X", tmhash.Sum(txBytes)), @@ -112,5 +112,5 @@ func cacheTxContext(sdkCtx sdk.Context, txBytes []byte) (sdk.Context, sdk.CacheR ) } - return sdkCtx.WithRootStore(storeCache), storeCache + return sdkCtx.WithMultiStore(storeCache), storeCache } diff --git a/baseapp/options.go b/baseapp/options.go index 837f659c1411..47cb63611e37 100644 --- a/baseapp/options.go +++ b/baseapp/options.go @@ -16,7 +16,7 @@ import ( // SetPruning sets a pruning option on the multistore associated with the app func SetPruning(opts sdk.PruningOptions) StoreOption { - return func(config *sdk.RootStoreConfig, _ uint64) error { config.Pruning = opts; return nil } + return func(config *StoreConfig, _ uint64) error { config.Pruning = opts; return nil } } // SetMinGasPrices returns an option that sets the minimum gas prices on the app. @@ -56,15 +56,10 @@ func SetIndexEvents(ie []string) AppOptionFunc { return func(app *BaseApp) { app.setIndexEvents(ie) } } -// SetIAVLCacheSize provides a BaseApp option function that sets the size of IAVL cache. -func SetIAVLCacheSize(size int) func(*BaseApp) { - return func(bapp *BaseApp) { bapp.cms.SetIAVLCacheSize(size) } -} - // SetInterBlockCache provides a BaseApp option function that sets the // inter-block cache. -func SetInterBlockCache(cache sdk.RootStorePersistentCache) AppOptionFunc { - opt := func(cfg *sdk.RootStoreConfig, v uint64) error { +func SetInterBlockCache(cache sdk.MultiStorePersistentCache) AppOptionFunc { + opt := func(cfg *StoreConfig, v uint64) error { cfg.PersistentCache = cache return nil } @@ -89,15 +84,15 @@ func SetSnapshotStore(snapshotStore *snapshots.Store) AppOptionOrdered { } } -// SetStorePrefixes store reserves prefix according to app configuration -func SetStorePrefixes(keys ...storetypes.StoreKey) StoreOption { - return func(config *sdk.RootStoreConfig, _ uint64) error { +// SetSubstores store registers substores according to app configuration +func SetSubstores(keys ...storetypes.StoreKey) StoreOption { + return func(config *StoreConfig, _ uint64) error { for _, key := range keys { typ, err := storetypes.StoreKeyToType(key) if err != nil { return err } - if err = config.ReservePrefix(key, typ); err != nil { + if err = config.RegisterSubstore(key.Name(), typ); err != nil { return err } } @@ -194,7 +189,7 @@ func (app *BaseApp) SetFauxMerkleMode() { // SetCommitMultiStoreTracer sets the store tracer on the BaseApp's underlying // CommitMultiStore. func (app *BaseApp) SetCommitMultiStoreTracer(w io.Writer) { - opt := func(cfg *sdk.RootStoreConfig, v uint64) error { + opt := func(cfg *StoreConfig, v uint64) error { cfg.TraceWriter = w return nil } @@ -248,7 +243,7 @@ func (app *BaseApp) SetInterfaceRegistry(registry types.InterfaceRegistry) { func (app *BaseApp) SetStreamingService(s StreamingService) { // add the listeners for each StoreKey for key, lis := range s.Listeners() { - app.cms.AddListeners(key, lis) + app.store.AddListeners(key, lis) } // register the StreamingService within the BaseApp // BaseApp will pass BeginBlock, DeliverTx, and EndBlock requests and responses to the streaming services to update their ABCI context diff --git a/baseapp/state.go b/baseapp/state.go index 6f6ce61dce8b..90cf7bdba429 100644 --- a/baseapp/state.go +++ b/baseapp/state.go @@ -5,14 +5,14 @@ import ( ) type state struct { - ms sdk.CacheRootStore + ms sdk.CacheMultiStore ctx sdk.Context } -// CacheRootStore calls and returns a CacheRootStore on the state's underling -// CacheRootStore. -func (st *state) CacheRootStore() sdk.CacheRootStore { - return st.ms.CacheRootStore() +// CacheMultiStore calls and returns a CacheMultiStore on the state's underling +// CacheMultiStore. +func (st *state) CacheWrap() sdk.CacheMultiStore { + return st.ms.CacheWrap() } // Context returns the Context of the state. diff --git a/baseapp/test_helpers.go b/baseapp/test_helpers.go index 1b41b93846c3..28cac0c7308a 100644 --- a/baseapp/test_helpers.go +++ b/baseapp/test_helpers.go @@ -4,6 +4,7 @@ import ( abci "github.com/tendermint/tendermint/abci/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + "github.com/cosmos/cosmos-sdk/store/v2/multi" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/types/tx" @@ -89,5 +90,13 @@ func (app *BaseApp) NewContext(isCheckTx bool, header tmproto.Header) sdk.Contex } func (app *BaseApp) NewUncachedContext(isCheckTx bool, header tmproto.Header) sdk.Context { - return sdk.NewContext(app.store, header, isCheckTx, app.logger) + return sdk.NewContext(multi.CommitAsCacheStore(app.store), header, isCheckTx, app.logger) +} + +func (app *BaseApp) NewContextAt(isCheckTx bool, header tmproto.Header, height int64) (sdk.Context, error) { + view, err := app.store.GetVersion(height) + if err != nil { + return sdk.Context{}, err + } + return sdk.NewContext(multi.BasicAsCacheStore(view), header, isCheckTx, app.logger), nil } diff --git a/baseapp/util_test.go b/baseapp/util_test.go index c2a8e27ba158..520e1e66edce 100644 --- a/baseapp/util_test.go +++ b/baseapp/util_test.go @@ -27,7 +27,7 @@ func (app *BaseApp) DeliverState() *state { // Store is an exported method to be able to access baseapp's root store in tests. // // This method is only accessible in baseapp tests. -func (app *BaseApp) Store() types.CommitRootStore { +func (app *BaseApp) Store() types.CommitMultiStore { return app.store } diff --git a/go.mod b/go.mod index e7efd0c930b6..a7e8b78d4f36 100644 --- a/go.mod +++ b/go.mod @@ -73,6 +73,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect github.com/dgraph-io/badger/v2 v2.2007.4 // indirect + github.com/dgraph-io/badger/v3 v3.2103.2 // indirect github.com/dgraph-io/ristretto v0.1.0 // indirect github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect github.com/dustin/go-humanize v1.0.0 // indirect @@ -87,6 +88,7 @@ require ( github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.0.1 // indirect + github.com/google/flatbuffers v2.0.0+incompatible // indirect github.com/google/orderedcode v0.0.1 // indirect github.com/google/uuid v1.3.0 // indirect github.com/googleapis/gax-go/v2 v2.1.1 // indirect diff --git a/go.sum b/go.sum index 49cd71567f2d..e7239ac043fd 100644 --- a/go.sum +++ b/go.sum @@ -329,6 +329,7 @@ github.com/dgraph-io/badger/v2 v2.2007.1/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDm github.com/dgraph-io/badger/v2 v2.2007.2/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= +github.com/dgraph-io/badger/v3 v3.2103.2 h1:dpyM5eCJAtQCBcMCZcT4UBZchuTJgCywerHHgmxfxM8= github.com/dgraph-io/badger/v3 v3.2103.2/go.mod h1:RHo4/GmYcKKh5Lxu63wLEMHJ70Pac2JqZRYGhlyAo2M= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.0.3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= @@ -544,6 +545,7 @@ github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB github.com/google/certificate-transparency-go v1.1.1/go.mod h1:FDKqPvSXawb2ecErVRrD+nfy23RCzyl7eqVCEmlT1Zs= github.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/flatbuffers v2.0.0+incompatible h1:dicJ2oXwypfwUGnB2/TYWYEKiuk9eYQlQO/AnOHl5mI= github.com/google/flatbuffers v2.0.0+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= diff --git a/server/export_test.go b/server/export_test.go index d48b99ebec76..fc169603ca1e 100644 --- a/server/export_test.go +++ b/server/export_test.go @@ -17,10 +17,11 @@ import ( "github.com/tendermint/tendermint/libs/log" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmtypes "github.com/tendermint/tendermint/types" - dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" + dbm "github.com/cosmos/cosmos-sdk/db" + "github.com/cosmos/cosmos-sdk/db/memdb" "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/server/types" "github.com/cosmos/cosmos-sdk/simapp" @@ -32,6 +33,7 @@ func TestExportCmd_ConsensusParams(t *testing.T) { tempDir := t.TempDir() _, ctx, _, cmd := setupApp(t, tempDir) + // require.NoError(t, app.CloseStore()) output := &bytes.Buffer{} cmd.SetOut(output) @@ -105,7 +107,7 @@ func TestExportCmd_Height(t *testing.T) { cmd.SetOut(output) args := append(tc.flags, fmt.Sprintf("--%s=%s", flags.FlagHome, tempDir)) cmd.SetArgs(args) - require.NoError(t, cmd.ExecuteContext(ctx)) + require.NoError(t, cmd.ExecuteContext(ctx), tc.name) var exportedGenDoc tmtypes.GenesisDoc err := tmjson.Unmarshal(output.Bytes(), &exportedGenDoc) @@ -127,7 +129,7 @@ func setupApp(t *testing.T, tempDir string) (*simapp.SimApp, context.Context, *t } logger, _ := log.NewDefaultLogger("plain", "info", false) - db := dbm.NewMemDB() + db := memdb.NewDB() encCfg := simapp.MakeTestEncodingConfig() app := simapp.NewSimApp(logger, db, nil, true, map[int64]bool{}, tempDir, 0, encCfg, simapp.EmptyAppOptions{}) @@ -155,21 +157,13 @@ func setupApp(t *testing.T, tempDir string) (*simapp.SimApp, context.Context, *t app.Commit() cmd := server.ExportCmd( - func(_ log.Logger, _ dbm.DB, _ io.Writer, height int64, forZeroHeight bool, jailAllowedAddrs []string, appOptons types.AppOptions) (types.ExportedApp, error) { + func(_ log.Logger, _ dbm.DBConnection, _ io.Writer, height int64, forZeroHeight bool, jailAllowedAddrs []string, appOptons types.AppOptions) (types.ExportedApp, error) { + require.NoError(t, app.CloseStore()) encCfg := simapp.MakeTestEncodingConfig() + simApp := simapp.NewSimApp(logger, db, nil, true, map[int64]bool{}, "", 0, encCfg, appOptons) + require.NoError(t, simApp.Init()) - var simApp *simapp.SimApp - if height != -1 { - simApp = simapp.NewSimApp(logger, db, nil, false, map[int64]bool{}, "", 0, encCfg, appOptons) - - if err := simApp.LoadHeight(height); err != nil { - return types.ExportedApp{}, err - } - } else { - simApp = simapp.NewSimApp(logger, db, nil, true, map[int64]bool{}, "", 0, encCfg, appOptons) - } - - return simApp.ExportAppStateAndValidators(forZeroHeight, jailAllowedAddrs) + return simApp.ExportAppStateAndValidatorsAt(forZeroHeight, jailAllowedAddrs, height) }, tempDir) ctx := context.Background() diff --git a/server/mock/app.go b/server/mock/app.go index 8ef0ae405e51..d4c3fa27a8ff 100644 --- a/server/mock/app.go +++ b/server/mock/app.go @@ -13,6 +13,7 @@ import ( bam "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/db/badgerdb" "github.com/cosmos/cosmos-sdk/simapp" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -34,7 +35,7 @@ func testTxHandler(options middleware.TxHandlerOptions) tx.Handler { // similar to a real app. Make sure rootDir is empty before running the test, // in order to guarantee consistent results func NewApp(rootDir string, logger log.Logger) (abci.Application, error) { - db, err := sdk.NewLevelDB("mock", filepath.Join(rootDir, "data")) + db, err := badgerdb.NewDB(filepath.Join(rootDir, "data", "mock")) if err != nil { return nil, err } @@ -43,10 +44,8 @@ func NewApp(rootDir string, logger log.Logger) (abci.Application, error) { capKeyMainStore := sdk.NewKVStoreKey("main") // Create BaseApp. - baseApp := bam.NewBaseApp("kvstore", logger, db) - - // Set mounts for BaseApp's MultiStore. - baseApp.MountStores(capKeyMainStore) + opt := bam.SetSubstores(capKeyMainStore) + baseApp := bam.NewBaseApp("kvstore", logger, db, opt) baseApp.SetInitChainer(InitChainer(capKeyMainStore)) @@ -64,12 +63,6 @@ func NewApp(rootDir string, logger log.Logger) (abci.Application, error) { }, ) baseApp.SetTxHandler(txHandler) - - // Load latest version. - if err := baseApp.LoadLatestVersion(); err != nil { - return nil, err - } - return baseApp, nil } diff --git a/server/mock/store.go b/server/mock/store.go index cf67e088fe77..7c18c71d24d1 100644 --- a/server/mock/store.go +++ b/server/mock/store.go @@ -3,35 +3,34 @@ package mock import ( "io" - dbm "github.com/tendermint/tm-db" - - storetypes "github.com/cosmos/cosmos-sdk/store/types" + // storetypes "github.com/cosmos/cosmos-sdk/store/types" + storetypes "github.com/cosmos/cosmos-sdk/store/v2" sdk "github.com/cosmos/cosmos-sdk/types" ) -var _ sdk.MultiStore = multiStore{} +var _ sdk.CommitMultiStore = multiStore{} type multiStore struct { kv map[storetypes.StoreKey]kvStore } -func (ms multiStore) CacheMultiStore() sdk.CacheMultiStore { - panic("not implemented") -} +var _ sdk.KVStore = kvStore{} -func (ms multiStore) CacheMultiStoreWithVersion(_ int64) (sdk.CacheMultiStore, error) { - panic("not implemented") +type kvStore struct { + store map[string][]byte } -func (ms multiStore) CacheWrap() storetypes.CacheWrap { - panic("not implemented") -} +type MultiStoreConfig = []storetypes.StoreKey -func (ms multiStore) CacheWrapWithTrace(_ io.Writer, _ sdk.TraceContext) storetypes.CacheWrap { - panic("not implemented") +func NewCommitMultiStore(c MultiStoreConfig) sdk.CommitMultiStore { + stores := make(map[storetypes.StoreKey]kvStore) + for _, skey := range c { + stores[skey] = kvStore{map[string][]byte{}} + } + return multiStore{kv: stores} } -func (ms multiStore) CacheWrapWithListeners(_ storetypes.StoreKey, _ []storetypes.WriteListener) storetypes.CacheWrap { +func (ms multiStore) CacheWrap() sdk.CacheMultiStore { panic("not implemented") } @@ -39,11 +38,11 @@ func (ms multiStore) TracingEnabled() bool { panic("not implemented") } -func (ms multiStore) SetTracingContext(tc sdk.TraceContext) sdk.MultiStore { +func (ms multiStore) SetTracingContext(tc sdk.TraceContext) { panic("not implemented") } -func (ms multiStore) SetTracer(w io.Writer) sdk.MultiStore { +func (ms multiStore) SetTracer(w io.Writer) { panic("not implemented") } @@ -71,54 +70,15 @@ func (ms multiStore) GetPruning() sdk.PruningOptions { panic("not implemented") } -func (ms multiStore) GetCommitKVStore(key storetypes.StoreKey) storetypes.CommitKVStore { - panic("not implemented") -} - -func (ms multiStore) GetCommitStore(key storetypes.StoreKey) storetypes.CommitStore { - panic("not implemented") -} - -func (ms multiStore) MountStoreWithDB(key storetypes.StoreKey, typ storetypes.StoreType, db dbm.DB) { - ms.kv[key] = kvStore{store: make(map[string][]byte)} -} - -func (ms multiStore) LoadLatestVersion() error { - return nil -} - -func (ms multiStore) LoadLatestVersionAndUpgrade(upgrades *storetypes.StoreUpgrades) error { - return nil -} - -func (ms multiStore) LoadVersionAndUpgrade(ver int64, upgrades *storetypes.StoreUpgrades) error { - panic("not implemented") -} - -func (ms multiStore) LoadVersion(ver int64) error { - panic("not implemented") -} - func (ms multiStore) GetKVStore(key storetypes.StoreKey) sdk.KVStore { return ms.kv[key] } -func (ms multiStore) GetStore(key storetypes.StoreKey) sdk.Store { - panic("not implemented") -} - -func (ms multiStore) GetStoreType() storetypes.StoreType { - panic("not implemented") -} +// func (ms multiStore) GetStoreType() storetypes.StoreType { +// panic("not implemented") +// } -func (ms multiStore) SetInterBlockCache(_ sdk.MultiStorePersistentCache) { - panic("not implemented") -} -func (ms multiStore) SetIAVLCacheSize(size int) { - panic("not implemented") -} - -func (ms multiStore) SetInitialVersion(version int64) error { +func (ms multiStore) SetInitialVersion(version uint64) error { panic("not implemented") } @@ -132,10 +92,12 @@ func (ms multiStore) Restore( panic("not implemented") } -var _ sdk.KVStore = kvStore{} +func (ms multiStore) GetVersion(int64) (storetypes.BasicMultiStore, error) { + panic("not implemented") +} -type kvStore struct { - store map[string][]byte +func (ms multiStore) Close() error { + panic("not implemented") } func (kv kvStore) CacheWrap() storetypes.CacheWrap { @@ -176,14 +138,6 @@ func (kv kvStore) Delete(key []byte) { delete(kv.store, string(key)) } -func (kv kvStore) Prefix(prefix []byte) sdk.KVStore { - panic("not implemented") -} - -func (kv kvStore) Gas(meter sdk.GasMeter, config sdk.GasConfig) sdk.KVStore { - panic("not implmeneted") -} - func (kv kvStore) Iterator(start, end []byte) sdk.Iterator { panic("not implemented") } @@ -191,15 +145,3 @@ func (kv kvStore) Iterator(start, end []byte) sdk.Iterator { func (kv kvStore) ReverseIterator(start, end []byte) sdk.Iterator { panic("not implemented") } - -func (kv kvStore) SubspaceIterator(prefix []byte) sdk.Iterator { - panic("not implemented") -} - -func (kv kvStore) ReverseSubspaceIterator(prefix []byte) sdk.Iterator { - panic("not implemented") -} - -func NewCommitMultiStore() sdk.CommitMultiStore { - return multiStore{kv: make(map[storetypes.StoreKey]kvStore)} -} diff --git a/server/mock/store_test.go b/server/mock/store_test.go index fc5cd12e097e..211603675f7b 100644 --- a/server/mock/store_test.go +++ b/server/mock/store_test.go @@ -4,20 +4,14 @@ import ( "testing" "github.com/stretchr/testify/require" - dbm "github.com/tendermint/tm-db" - storetypes "github.com/cosmos/cosmos-sdk/store/types" + // storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" ) func TestStore(t *testing.T) { - db := dbm.NewMemDB() - cms := NewCommitMultiStore() - key := sdk.NewKVStoreKey("test") - cms.MountStoreWithDB(key, storetypes.StoreTypeIAVL, db) - err := cms.LoadLatestVersion() - require.Nil(t, err) + cms := NewCommitMultiStore(MultiStoreConfig{key}) store := cms.GetKVStore(key) require.NotNil(t, store) diff --git a/server/util.go b/server/util.go index 27af68e5eb34..2f20f7a540d1 100644 --- a/server/util.go +++ b/server/util.go @@ -24,6 +24,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/flags" dbm "github.com/cosmos/cosmos-sdk/db" + "github.com/cosmos/cosmos-sdk/db/badgerdb" "github.com/cosmos/cosmos-sdk/server/config" "github.com/cosmos/cosmos-sdk/server/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -372,8 +373,9 @@ func addrToIP(addr net.Addr) net.IP { } func openDB(rootDir string) (dbm.DBConnection, error) { - dataDir := filepath.Join(rootDir, "data") - return sdk.NewLevelDB("application", dataDir) + // dataDir := filepath.Join(rootDir, "data") + // return sdk.NewLevelDB("application", dataDir) + return badgerdb.NewDB(rootDir) } func openTraceWriter(traceWriterFile string) (w io.Writer, err error) { diff --git a/simapp/app.go b/simapp/app.go index 0f968dd33ef4..23da852740e0 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -13,7 +13,7 @@ import ( "github.com/spf13/cast" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" - // tmos "github.com/tendermint/tendermint/libs/os" + tmos "github.com/tendermint/tendermint/libs/os" "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/client" @@ -28,6 +28,7 @@ import ( simappparams "github.com/cosmos/cosmos-sdk/simapp/params" "github.com/cosmos/cosmos-sdk/store/streaming" storetypes "github.com/cosmos/cosmos-sdk/store/types" + storev2 "github.com/cosmos/cosmos-sdk/store/v2" "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" @@ -49,13 +50,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/capability" capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - - storetypes "github.com/cosmos/cosmos-sdk/store/types" - storev2 "github.com/cosmos/cosmos-sdk/store/v2" - authmiddleware "github.com/cosmos/cosmos-sdk/x/auth/middleware" - "github.com/cosmos/cosmos-sdk/x/authz" - authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper" - authzmodule "github.com/cosmos/cosmos-sdk/x/authz/module" "github.com/cosmos/cosmos-sdk/x/crisis" crisiskeeper "github.com/cosmos/cosmos-sdk/x/crisis/keeper" crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types" @@ -212,7 +206,6 @@ func init() { func NewSimApp( logger log.Logger, db dbm.DBConnection, - // rootStore sdk.CommitRootStore, traceStore io.Writer, loadLatest bool, skipUpgradeHeights map[int64]bool, @@ -238,13 +231,13 @@ func NewSimApp( // not include this key. memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey, "testingkey") // initialize stores - setNamespaces := func(config *sdk.RootStoreConfig, ver uint64) error { + setNamespaces := func(config *baseapp.StoreConfig, ver uint64) error { for _, key := range keys { typ, err := storev2.StoreKeyToType(key) if err != nil { return err } - if err = config.ReservePrefix(key, typ); err != nil { + if err = config.RegisterSubstore(key.Name(), typ); err != nil { return err } } @@ -253,7 +246,7 @@ func NewSimApp( if err != nil { return err } - if err = config.ReservePrefix(key, typ); err != nil { + if err = config.RegisterSubstore(key.Name(), typ); err != nil { return err } } @@ -262,7 +255,7 @@ func NewSimApp( if err != nil { return err } - if err = config.ReservePrefix(key, typ); err != nil { + if err = config.RegisterSubstore(key.Name(), typ); err != nil { return err } } @@ -270,7 +263,7 @@ func NewSimApp( } baseAppOptions = append(baseAppOptions, baseapp.StoreOption(setNamespaces)) - bApp := baseapp.NewBaseApp(appName, logger, db, encodingConfig.TxConfig.TxDecoder(), baseAppOptions...) + bApp := baseapp.NewBaseApp(appName, logger, db, baseAppOptions...) bApp.SetCommitMultiStoreTracer(traceStore) bApp.SetVersion(version.Version) bApp.SetInterfaceRegistry(interfaceRegistry) diff --git a/simapp/app_test.go b/simapp/app_test.go index 5c44896b9c08..3a7b457cb330 100644 --- a/simapp/app_test.go +++ b/simapp/app_test.go @@ -38,6 +38,7 @@ import ( func TestSimAppExportAndBlockedAddrs(t *testing.T) { encCfg := MakeTestEncodingConfig() + logger, _ := log.NewDefaultLogger("plain", "info", false) db := memdb.NewDB() app := NewSimappWithCustomOptions(t, false, SetupOptions{ Logger: logger, @@ -79,7 +80,7 @@ func TestRunMigrations(t *testing.T) { app := NewSimApp(logger, memdb.NewDB(), nil, true, map[int64]bool{}, DefaultNodeHome, 0, encCfg, EmptyAppOptions{}) // Create a new baseapp and configurator for the purpose of this test. - bApp := baseapp.NewBaseApp(appName, logger, memdb.NewDB(), encCfg.TxConfig.TxDecoder()) + bApp := baseapp.NewBaseApp(appName, logger, memdb.NewDB()) bApp.SetCommitMultiStoreTracer(nil) bApp.SetInterfaceRegistry(encCfg.InterfaceRegistry) msr := authmiddleware.NewMsgServiceRouter(encCfg.InterfaceRegistry) @@ -251,6 +252,7 @@ func TestInitGenesisOnMigration(t *testing.T) { func TestUpgradeStateOnGenesis(t *testing.T) { encCfg := MakeTestEncodingConfig() db := memdb.NewDB() + logger, _ := log.NewDefaultLogger("plain", "info", false) app := NewSimappWithCustomOptions(t, false, SetupOptions{ Logger: logger, DB: db, diff --git a/simapp/export.go b/simapp/export.go index 7c59ffc69c0a..5eaaddca5725 100644 --- a/simapp/export.go +++ b/simapp/export.go @@ -18,17 +18,28 @@ import ( func (app *SimApp) ExportAppStateAndValidators( forZeroHeight bool, jailAllowedAddrs []string, ) (servertypes.ExportedApp, error) { + return app.ExportAppStateAndValidatorsAt(forZeroHeight, jailAllowedAddrs, 0) +} + +func (app *SimApp) ExportAppStateAndValidatorsAt( + forZeroHeight bool, jailAllowedAddrs []string, height int64, +) (servertypes.ExportedApp, error) { + if height < 1 { + height = app.LastBlockHeight() + } // as if they could withdraw from the start of the next block - ctx := app.NewContext(true, tmproto.Header{Height: app.LastBlockHeight()}) + ctx, err := app.NewContextAt(true, tmproto.Header{Height: height}, height) + if err != nil { + return servertypes.ExportedApp{}, err + } // We export at last height + 1, because that's the height at which // Tendermint will start InitChain. - height := app.LastBlockHeight() + 1 + exportHeight := height + 1 if forZeroHeight { - height = 0 + exportHeight = 0 app.prepForZeroHeightGenesis(ctx, jailAllowedAddrs) } - genState := app.mm.ExportGenesis(ctx, app.appCodec) appState, err := json.MarshalIndent(genState, "", " ") if err != nil { @@ -39,7 +50,7 @@ func (app *SimApp) ExportAppStateAndValidators( return servertypes.ExportedApp{ AppState: appState, Validators: validators, - Height: height, + Height: exportHeight, ConsensusParams: app.BaseApp.GetConsensusParams(ctx), }, err } diff --git a/simapp/simd/cmd/root.go b/simapp/simd/cmd/root.go index 1adc235deae7..7e482f34504e 100644 --- a/simapp/simd/cmd/root.go +++ b/simapp/simd/cmd/root.go @@ -19,6 +19,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/client/rpc" dbm "github.com/cosmos/cosmos-sdk/db" + "github.com/cosmos/cosmos-sdk/db/badgerdb" "github.com/cosmos/cosmos-sdk/server" serverconfig "github.com/cosmos/cosmos-sdk/server/config" servertypes "github.com/cosmos/cosmos-sdk/server/types" @@ -235,7 +236,7 @@ func (a appCreator) newApp( traceStore io.Writer, appOpts servertypes.AppOptions, ) servertypes.Application { - var cache sdk.RootStorePersistentCache + var cache sdk.MultiStorePersistentCache if cast.ToBool(appOpts.Get(server.FlagInterBlockCache)) { cache = store.NewCommitKVStoreCacheManager() @@ -252,7 +253,7 @@ func (a appCreator) newApp( } snapshotDir := filepath.Join(cast.ToString(appOpts.Get(flags.FlagHome)), "data", "snapshots") - snapshotDB, err := sdk.NewLevelDB("metadata", snapshotDir) + snapshotDB, err := badgerdb.NewDB(filepath.Join(snapshotDir, "metadata")) if err != nil { panic(err) } diff --git a/simapp/utils.go b/simapp/utils.go index 863d78220bdf..60612719f46d 100644 --- a/simapp/utils.go +++ b/simapp/utils.go @@ -9,6 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" dbm "github.com/cosmos/cosmos-sdk/db" + "github.com/cosmos/cosmos-sdk/db/badgerdb" "github.com/cosmos/cosmos-sdk/simapp/helpers" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/kv" @@ -39,7 +40,7 @@ func SetupSimulation(dirPrefix, dbName string) (simtypes.Config, dbm.DBConnectio return simtypes.Config{}, nil, "", nil, false, err } - db, err := sdk.NewLevelDB(dbName, dir) + db, err := badgerdb.NewDB(dbName) if err != nil { return simtypes.Config{}, nil, "", nil, false, err } diff --git a/snapshots/helpers_test.go b/snapshots/helpers_test.go index f530eadf21b8..f7d41529996e 100644 --- a/snapshots/helpers_test.go +++ b/snapshots/helpers_test.go @@ -10,8 +10,8 @@ import ( "time" "github.com/stretchr/testify/require" - db "github.com/tendermint/tm-db" + "github.com/cosmos/cosmos-sdk/db/memdb" "github.com/cosmos/cosmos-sdk/snapshots" "github.com/cosmos/cosmos-sdk/snapshots/types" ) @@ -107,7 +107,7 @@ func setupBusyManager(t *testing.T) *snapshots.Manager { require.NoError(t, err) t.Cleanup(func() { _ = os.RemoveAll(tempdir) }) - store, err := snapshots.NewStore(db.NewMemDB(), tempdir) + store, err := snapshots.NewStore(memdb.NewDB(), tempdir) require.NoError(t, err) hung := newHungSnapshotter() mgr := snapshots.NewManager(store, hung) diff --git a/snapshots/store_test.go b/snapshots/store_test.go index 77ff32a3c465..1f8131a994e7 100644 --- a/snapshots/store_test.go +++ b/snapshots/store_test.go @@ -11,8 +11,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - db "github.com/tendermint/tm-db" + "github.com/cosmos/cosmos-sdk/db/memdb" "github.com/cosmos/cosmos-sdk/snapshots" "github.com/cosmos/cosmos-sdk/snapshots/types" "github.com/cosmos/cosmos-sdk/testutil" @@ -26,7 +26,7 @@ func setupStore(t *testing.T) *snapshots.Store { require.NoError(t, err) t.Cleanup(func() { _ = os.RemoveAll(tempdir) }) - store, err := snapshots.NewStore(db.NewMemDB(), tempdir) + store, err := snapshots.NewStore(memdb.NewDB(), tempdir) require.NoError(t, err) _, err = store.Save(1, 1, makeChunks([][]byte{ @@ -51,20 +51,20 @@ func setupStore(t *testing.T) *snapshots.Store { func TestNewStore(t *testing.T) { tempdir := t.TempDir() - _, err := snapshots.NewStore(db.NewMemDB(), tempdir) + _, err := snapshots.NewStore(memdb.NewDB(), tempdir) require.NoError(t, err) } func TestNewStore_ErrNoDir(t *testing.T) { - _, err := snapshots.NewStore(db.NewMemDB(), "") + _, err := snapshots.NewStore(memdb.NewDB(), "") require.Error(t, err) } func TestNewStore_ErrDirFailure(t *testing.T) { notADir := filepath.Join(testutil.TempFile(t).Name(), "subdir") - _, err := snapshots.NewStore(db.NewMemDB(), notADir) + _, err := snapshots.NewStore(memdb.NewDB(), notADir) require.Error(t, err) } diff --git a/store/rootmulti/store_test.go b/store/rootmulti/store_test.go index a187fef762d0..c1d2909eefaa 100644 --- a/store/rootmulti/store_test.go +++ b/store/rootmulti/store_test.go @@ -296,7 +296,7 @@ func TestMultistoreLoadWithUpgrade(t *testing.T) { require.Equal(t, v4, rl4.Get(k4)) // check commitInfo in storage - ci, err = getCommitInfo(db, 2) + ci, err := getCommitInfo(db, 2) require.NoError(t, err) require.Equal(t, int64(2), ci.Version) require.Equal(t, 3, len(ci.StoreInfos), ci.StoreInfos) diff --git a/testutil/context.go b/testutil/context.go index 8e410acef77b..a81468f68524 100644 --- a/testutil/context.go +++ b/testutil/context.go @@ -7,26 +7,32 @@ import ( "github.com/cosmos/cosmos-sdk/db/memdb" // "github.com/cosmos/cosmos-sdk/store" stypes "github.com/cosmos/cosmos-sdk/store/v2" - "github.com/cosmos/cosmos-sdk/store/v2/flat" + "github.com/cosmos/cosmos-sdk/store/v2/multi" sdk "github.com/cosmos/cosmos-sdk/types" ) // DefaultContext creates a sdk.Context with a fresh MemDB that can be used in tests. -func DefaultContext(key, tkey stypes.StoreKey) (ret sdk.Context, err error) { +func DefaultContext(key, tkey stypes.StoreKey) (ret sdk.Context) { + var err error + defer func() { + if err != nil { + panic(err) + } + }() db := memdb.NewDB() - opts := flat.DefaultRootStoreConfig() - err = opts.ReservePrefix(key, stypes.StoreTypePersistent) + opts := multi.DefaultStoreConfig() + err = opts.RegisterSubstore(key.Name(), stypes.StoreTypePersistent) if err != nil { return } - err = opts.ReservePrefix(tkey, stypes.StoreTypeTransient) + err = opts.RegisterSubstore(tkey.Name(), stypes.StoreTypeTransient) if err != nil { return } - rs, err := flat.NewRootStore(db, opts) + rs, err := multi.NewStore(db, opts) if err != nil { return } - ret = sdk.NewContext(rs, tmproto.Header{}, false, log.NewNopLogger()) + ret = sdk.NewContext(rs.CacheWrap(), tmproto.Header{}, false, log.NewNopLogger()) return } diff --git a/types/context.go b/types/context.go index b3eb6f505d34..a96221c0f87c 100644 --- a/types/context.go +++ b/types/context.go @@ -12,7 +12,7 @@ import ( "github.com/cosmos/cosmos-sdk/store/gaskv" stypes "github.com/cosmos/cosmos-sdk/store/types" - // stypes2 "github.com/cosmos/cosmos-sdk/store/v2" + stypes2 "github.com/cosmos/cosmos-sdk/store/v2" ) /* @@ -25,7 +25,7 @@ and standard additions here would be better just to add to the Context struct */ type Context struct { ctx context.Context - store BasicRootStore + store stypes2.MultiStore header tmproto.Header headerHash tmbytes.HexBytes chainID string @@ -48,19 +48,20 @@ type Request = Context func (c Context) Context() context.Context { return c.ctx } // func (c Context) MultiStore() MultiStore { return c.store } -func (c Context) RootStore() BasicRootStore { return c.store } -func (c Context) BlockHeight() int64 { return c.header.Height } -func (c Context) BlockTime() time.Time { return c.header.Time } -func (c Context) ChainID() string { return c.chainID } -func (c Context) TxBytes() []byte { return c.txBytes } -func (c Context) Logger() log.Logger { return c.logger } -func (c Context) VoteInfos() []abci.VoteInfo { return c.voteInfo } -func (c Context) GasMeter() GasMeter { return c.gasMeter } -func (c Context) BlockGasMeter() GasMeter { return c.blockGasMeter } -func (c Context) IsCheckTx() bool { return c.checkTx } -func (c Context) IsReCheckTx() bool { return c.recheckTx } -func (c Context) MinGasPrices() DecCoins { return c.minGasPrice } -func (c Context) EventManager() *EventManager { return c.eventManager } +// func (c Context) MultiStore() stypes2.CacheMultiStore { return c.store } +func (c Context) MultiStore() stypes2.MultiStore { return c.store } +func (c Context) BlockHeight() int64 { return c.header.Height } +func (c Context) BlockTime() time.Time { return c.header.Time } +func (c Context) ChainID() string { return c.chainID } +func (c Context) TxBytes() []byte { return c.txBytes } +func (c Context) Logger() log.Logger { return c.logger } +func (c Context) VoteInfos() []abci.VoteInfo { return c.voteInfo } +func (c Context) GasMeter() GasMeter { return c.gasMeter } +func (c Context) BlockGasMeter() GasMeter { return c.blockGasMeter } +func (c Context) IsCheckTx() bool { return c.checkTx } +func (c Context) IsReCheckTx() bool { return c.recheckTx } +func (c Context) MinGasPrices() DecCoins { return c.minGasPrice } +func (c Context) EventManager() *EventManager { return c.eventManager } // clone the header before returning func (c Context) BlockHeader() tmproto.Header { @@ -80,7 +81,7 @@ func (c Context) ConsensusParams() *tmproto.ConsensusParams { } // create a new context -func NewContext(rs BasicRootStore, header tmproto.Header, isCheckTx bool, logger log.Logger) Context { +func NewContext(rs stypes2.MultiStore, header tmproto.Header, isCheckTx bool, logger log.Logger) Context { // https://github.com/gogo/protobuf/issues/519 header.Time = header.Time.UTC() return Context{ @@ -103,7 +104,7 @@ func (c Context) WithContext(ctx context.Context) Context { } // WithMultiStore returns a Context with an updated MultiStore. -func (c Context) WithRootStore(rs BasicRootStore) Context { +func (c Context) WithMultiStore(rs stypes2.MultiStore) Context { c.store = rs return c } @@ -245,14 +246,14 @@ func (c Context) Value(key interface{}) interface{} { // Store / Caching // ---------------------------------------------------------------------------- -// KVStore fetches a KVStore from the RootStore. +// KVStore fetches a KVStore from the MultiStore. func (c Context) KVStore(key stypes.StoreKey) stypes.KVStore { - return gaskv.NewStore(c.RootStore().GetKVStore(key), c.GasMeter(), stypes.KVGasConfig()) + return gaskv.NewStore(c.MultiStore().GetKVStore(key), c.GasMeter(), stypes.KVGasConfig()) } -// TransientStore fetches a TransientStore from the RootStore. +// TransientStore fetches a TransientStore from the MultiStore. func (c Context) TransientStore(key stypes.StoreKey) stypes.KVStore { - return gaskv.NewStore(c.RootStore().GetKVStore(key), c.GasMeter(), stypes.TransientGasConfig()) + return gaskv.NewStore(c.MultiStore().GetKVStore(key), c.GasMeter(), stypes.TransientGasConfig()) } // CacheContext returns a new Context with the multi-store cached and a new @@ -260,9 +261,9 @@ func (c Context) TransientStore(key stypes.StoreKey) stypes.KVStore { // is called. func (c Context) CacheContext() (cc Context, writeCache func()) { // TODO replace with constructor? - crs := c.RootStore().CacheRootStore() - cc = c.WithRootStore(crs).WithEventManager(NewEventManager()) - return cc, crs.Write + cs := c.MultiStore().CacheWrap() + cc = c.WithMultiStore(cs).WithEventManager(NewEventManager()) + return cc, cs.Write } // ContextKey defines a type alias for a stdlib Context key. diff --git a/types/store.go b/types/store.go index 34b537d005ed..37e14722a901 100644 --- a/types/store.go +++ b/types/store.go @@ -7,7 +7,6 @@ import ( "github.com/cosmos/cosmos-sdk/store/types" types2 "github.com/cosmos/cosmos-sdk/store/v2" - "github.com/cosmos/cosmos-sdk/store/v2/flat" "github.com/cosmos/cosmos-sdk/types/kv" ) @@ -24,14 +23,13 @@ type ( // CacheMultiStore = types.CacheMultiStore // CommitMultiStore = types.CommitMultiStore // MultiStorePersistentCache = types.MultiStorePersistentCache - KVStore = types.KVStore - Iterator = types.Iterator - RootStoreConfig = flat.RootStoreConfig - BasicRootStore = types2.BasicRootStore - // RootStore = types2.RootStore - CommitRootStore = types2.CommitRootStore - CacheRootStore = types2.CacheRootStore - RootStorePersistentCache = types2.RootStorePersistentCache + KVStore = types.KVStore + Iterator = types.Iterator + // MultiStoreConfig = multi.StoreConfig + BasicMultiStore = types2.BasicMultiStore + CommitMultiStore = types2.CommitMultiStore + CacheMultiStore = types2.CacheMultiStore + MultiStorePersistentCache = types2.MultiStorePersistentCache ) // StoreDecoderRegistry defines each of the modules store decoders. Used for ImportExport diff --git a/x/auth/middleware/branch_store.go b/x/auth/middleware/branch_store.go index 236d288c122b..6269dfbcc995 100644 --- a/x/auth/middleware/branch_store.go +++ b/x/auth/middleware/branch_store.go @@ -55,15 +55,15 @@ func branchAndRun(ctx context.Context, req tx.Request, fn nextFn) (tx.Response, // a branched multi-store. func branchStore(sdkCtx sdk.Context, tx tmtypes.Tx) (sdk.Context, sdk.CacheMultiStore) { ms := sdkCtx.MultiStore() - msCache := ms.CacheMultiStore() + msCache := ms.CacheWrap() if msCache.TracingEnabled() { - msCache = msCache.SetTracingContext( + msCache.SetTracingContext( sdk.TraceContext( map[string]interface{}{ "txHash": tx.Hash(), }, ), - ).(sdk.CacheMultiStore) + ) } return sdkCtx.WithMultiStore(msCache), msCache diff --git a/x/auth/middleware/run_msgs.go b/x/auth/middleware/run_msgs.go index b9f7e913bfb6..d333e5918995 100644 --- a/x/auth/middleware/run_msgs.go +++ b/x/auth/middleware/run_msgs.go @@ -47,11 +47,6 @@ func (txh runMsgsTxHandler) SimulateTx(ctx context.Context, req tx.Request) (tx. // Handler does not exist for a given message route. Otherwise, a reference to a // Result is returned. The caller must not commit state if an error is returned. func (txh runMsgsTxHandler) runMsgs(sdkCtx sdk.Context, msgs []sdk.Msg, txBytes []byte) (tx.Response, error) { - // Create a new Context based off of the existing Context with a RootStore branch - // in case message processing fails. At this point, the RootStore - // is a branch of a branch. - runMsgCtx, msCache := cacheTxContext(sdkCtx, txBytes) - // Attempt to execute all messages and only update state if all messages pass // and we're in DeliverTx. Note, runMsgs will never return a reference to a // Result if any single message fails or does not have a registered Handler. @@ -120,22 +115,3 @@ func (txh runMsgsTxHandler) runMsgs(sdkCtx sdk.Context, msgs []sdk.Msg, txBytes MsgResponses: msgResponses, }, nil } - -// cacheTxContext returns a new context based off of the provided context with -// a branched multi-store. -func cacheTxContext(sdkCtx sdk.Context, txBytes []byte) (sdk.Context, sdk.CacheRootStore) { - ms := sdkCtx.RootStore() - // TODO: https://github.com/cosmos/cosmos-sdk/issues/2824 - msCache := ms.CacheRootStore() - if msCache.TracingEnabled() { - msCache.SetTraceContext( - sdk.TraceContext( - map[string]interface{}{ - "txHash": fmt.Sprintf("%X", tmhash.Sum(txBytes)), - }, - ), - ) - } - - return sdkCtx.WithRootStore(msCache), msCache -} diff --git a/x/auth/module_test.go b/x/auth/module_test.go index 6f527c587e49..a034459718bf 100644 --- a/x/auth/module_test.go +++ b/x/auth/module_test.go @@ -8,14 +8,14 @@ import ( tmjson "github.com/tendermint/tendermint/libs/json" "github.com/tendermint/tendermint/libs/log" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - dbm "github.com/tendermint/tm-db" + "github.com/cosmos/cosmos-sdk/db/memdb" "github.com/cosmos/cosmos-sdk/simapp" "github.com/cosmos/cosmos-sdk/x/auth/types" ) func TestItCreatesModuleAccountOnInitBlock(t *testing.T) { - db := dbm.NewMemDB() + db := memdb.NewDB() encCdc := simapp.MakeTestEncodingConfig() app := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, simapp.DefaultNodeHome, 5, encCdc, simapp.EmptyAppOptions{}) diff --git a/x/capability/genesis_test.go b/x/capability/genesis_test.go index 34a09960e750..e7853aa05e14 100644 --- a/x/capability/genesis_test.go +++ b/x/capability/genesis_test.go @@ -3,8 +3,8 @@ package capability_test import ( "github.com/tendermint/tendermint/libs/log" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - dbm "github.com/tendermint/tm-db" + "github.com/cosmos/cosmos-sdk/db/memdb" "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" @@ -33,7 +33,7 @@ func (suite *CapabilityTestSuite) TestGenesis() { // create new app that does not share persistent or in-memory state // and initialize app from exported genesis state above. - db := dbm.NewMemDB() + db := memdb.NewDB() encCdc := simapp.MakeTestEncodingConfig() newApp := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, simapp.DefaultNodeHome, 5, encCdc, simapp.EmptyAppOptions{}) diff --git a/x/capability/keeper/keeper_test.go b/x/capability/keeper/keeper_test.go index 9757461db71d..a191f7329c84 100644 --- a/x/capability/keeper/keeper_test.go +++ b/x/capability/keeper/keeper_test.go @@ -279,7 +279,7 @@ func (suite KeeperTestSuite) TestRevertCapability() { ms := suite.ctx.MultiStore() - msCache := ms.CacheMultiStore() + msCache := ms.CacheWrap() cacheCtx := suite.ctx.WithMultiStore(msCache) capName := "revert" diff --git a/x/distribution/module_test.go b/x/distribution/module_test.go index 70e2e50ea8b0..e9a4583aedd4 100644 --- a/x/distribution/module_test.go +++ b/x/distribution/module_test.go @@ -8,15 +8,15 @@ import ( tmjson "github.com/tendermint/tendermint/libs/json" "github.com/tendermint/tendermint/libs/log" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - dbm "github.com/tendermint/tm-db" + "github.com/cosmos/cosmos-sdk/db/memdb" "github.com/cosmos/cosmos-sdk/simapp" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/distribution/types" ) func TestItCreatesModuleAccountOnInitBlock(t *testing.T) { - db := dbm.NewMemDB() + db := memdb.NewDB() encCdc := simapp.MakeTestEncodingConfig() app := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, simapp.DefaultNodeHome, 5, encCdc, simapp.EmptyAppOptions{}) diff --git a/x/gov/genesis_test.go b/x/gov/genesis_test.go index a3eef13f691a..25d5c5186a8c 100644 --- a/x/gov/genesis_test.go +++ b/x/gov/genesis_test.go @@ -8,8 +8,8 @@ import ( abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - dbm "github.com/tendermint/tm-db" + "github.com/cosmos/cosmos-sdk/db/memdb" "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" @@ -76,7 +76,7 @@ func TestImportExportQueues(t *testing.T) { panic(err) } - db := dbm.NewMemDB() + db := memdb.NewDB() app2 := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, simapp.DefaultNodeHome, 0, simapp.MakeTestEncodingConfig(), simapp.EmptyAppOptions{}) app2.InitChain( diff --git a/x/gov/module_test.go b/x/gov/module_test.go index c43f570dbac5..97a27819143d 100644 --- a/x/gov/module_test.go +++ b/x/gov/module_test.go @@ -8,15 +8,15 @@ import ( tmjson "github.com/tendermint/tendermint/libs/json" "github.com/tendermint/tendermint/libs/log" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - dbm "github.com/tendermint/tm-db" + "github.com/cosmos/cosmos-sdk/db/memdb" "github.com/cosmos/cosmos-sdk/simapp" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/gov/types" ) func TestItCreatesModuleAccountOnInitBlock(t *testing.T) { - db := dbm.NewMemDB() + db := memdb.NewDB() encCdc := simapp.MakeTestEncodingConfig() app := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, simapp.DefaultNodeHome, 5, encCdc, simapp.EmptyAppOptions{}) diff --git a/x/mint/module_test.go b/x/mint/module_test.go index ecf0a1511ba1..9b99e826d1f9 100644 --- a/x/mint/module_test.go +++ b/x/mint/module_test.go @@ -8,15 +8,15 @@ import ( tmjson "github.com/tendermint/tendermint/libs/json" "github.com/tendermint/tendermint/libs/log" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - dbm "github.com/tendermint/tm-db" + "github.com/cosmos/cosmos-sdk/db/memdb" "github.com/cosmos/cosmos-sdk/simapp" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/mint/types" ) func TestItCreatesModuleAccountOnInitBlock(t *testing.T) { - db := dbm.NewMemDB() + db := memdb.NewDB() encCdc := simapp.MakeTestEncodingConfig() app := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, simapp.DefaultNodeHome, 5, encCdc, simapp.EmptyAppOptions{}) diff --git a/x/params/types/subspace_test.go b/x/params/types/subspace_test.go index fa1d1a36ecb4..bf7d16cc6c5f 100644 --- a/x/params/types/subspace_test.go +++ b/x/params/types/subspace_test.go @@ -10,11 +10,11 @@ import ( "github.com/stretchr/testify/suite" "github.com/tendermint/tendermint/libs/log" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/db/memdb" "github.com/cosmos/cosmos-sdk/simapp" - "github.com/cosmos/cosmos-sdk/store" + "github.com/cosmos/cosmos-sdk/store/v2/multi" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/params/types" ) @@ -29,12 +29,13 @@ type SubspaceTestSuite struct { } func (suite *SubspaceTestSuite) SetupTest() { - db := dbm.NewMemDB() + db := memdb.NewDB() - ms := store.NewCommitMultiStore(db) - ms.MountStoreWithDB(key, storetypes.StoreTypeIAVL, db) - ms.MountStoreWithDB(tkey, storetypes.StoreTypeTransient, db) - suite.NoError(ms.LoadLatestVersion()) + config := multi.DefaultStoreConfig() + suite.NoError(config.RegisterSubstore(key.Name(), storetypes.StoreTypePersistent)) + suite.NoError(config.RegisterSubstore(tkey.Name(), storetypes.StoreTypeTransient)) + ms, err := multi.NewStore(db, config) + suite.NoError(err) encCfg := simapp.MakeTestEncodingConfig() ss := types.NewSubspace(encCfg.Codec, encCfg.Amino, key, tkey, "testsubspace") diff --git a/x/staking/module_test.go b/x/staking/module_test.go index 8784893634b6..34627ac3a286 100644 --- a/x/staking/module_test.go +++ b/x/staking/module_test.go @@ -8,22 +8,22 @@ import ( tmjson "github.com/tendermint/tendermint/libs/json" "github.com/tendermint/tendermint/libs/log" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - dbm "github.com/tendermint/tm-db" + "github.com/cosmos/cosmos-sdk/db/memdb" "github.com/cosmos/cosmos-sdk/simapp" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/staking/types" ) func TestItCreatesModuleAccountOnInitBlock(t *testing.T) { - db := dbm.NewMemDB() + db := memdb.NewDB() encCdc := simapp.MakeTestEncodingConfig() app := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, simapp.DefaultNodeHome, 5, encCdc, simapp.EmptyAppOptions{}) genesisState := simapp.GenesisStateWithSingleValidator(t, app) stateBytes, err := tmjson.Marshal(genesisState) require.NoError(t, err) - + app.InitChain( abcitypes.RequestInitChain{ AppStateBytes: stateBytes, diff --git a/x/upgrade/abci_test.go b/x/upgrade/abci_test.go index 915f5c36cafe..89bb659197be 100644 --- a/x/upgrade/abci_test.go +++ b/x/upgrade/abci_test.go @@ -11,8 +11,8 @@ import ( abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - dbm "github.com/tendermint/tm-db" + "github.com/cosmos/cosmos-sdk/db/memdb" "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -35,7 +35,7 @@ var s TestSuite func setupTest(t *testing.T, height int64, skip map[int64]bool) TestSuite { - db := dbm.NewMemDB() + db := memdb.NewDB() app := simapp.NewSimappWithCustomOptions(t, false, simapp.SetupOptions{ Logger: log.NewNopLogger(), SkipUpgradeHeights: skip, diff --git a/x/upgrade/types/storeloader.go b/x/upgrade/types/storeloader.go index 8ec9a1469146..4306dab62992 100644 --- a/x/upgrade/types/storeloader.go +++ b/x/upgrade/types/storeloader.go @@ -3,13 +3,13 @@ package types import ( "github.com/cosmos/cosmos-sdk/baseapp" storetypes "github.com/cosmos/cosmos-sdk/store/v2" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/store/v2/multi" ) // UpgradeStoreOption is used to prepare baseapp with a fixed StoreOption. // This is useful for custom upgrade loading logic. func UpgradeStoreOption(upgradeHeight uint64, storeUpgrades *storetypes.StoreUpgrades) baseapp.StoreOption { - return func(config *sdk.RootStoreConfig, loadHeight uint64) error { + return func(config *multi.StoreConfig, loadHeight uint64) error { // Check if the current commit version and upgrade height matches if upgradeHeight == loadHeight+1 { if len(storeUpgrades.Renamed) > 0 || len(storeUpgrades.Deleted) > 0 || len(storeUpgrades.Added) > 0 { diff --git a/x/upgrade/types/storeloader_test.go b/x/upgrade/types/storeloader_test.go index 341a2ffe30da..e35a03c8d708 100644 --- a/x/upgrade/types/storeloader_test.go +++ b/x/upgrade/types/storeloader_test.go @@ -11,21 +11,16 @@ import ( abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/baseapp" + dbm "github.com/cosmos/cosmos-sdk/db" + "github.com/cosmos/cosmos-sdk/db/memdb" "github.com/cosmos/cosmos-sdk/server" - "github.com/cosmos/cosmos-sdk/store/rootmulti" storetypes "github.com/cosmos/cosmos-sdk/store/types" + "github.com/cosmos/cosmos-sdk/store/v2/multi" sdk "github.com/cosmos/cosmos-sdk/types" ) -func useUpgradeLoader(height int64, upgrades *storetypes.StoreUpgrades) func(*baseapp.BaseApp) { - return func(app *baseapp.BaseApp) { - app.SetStoreLoader(UpgradeStoreLoader(height, upgrades)) - } -} - func defaultLogger() log.Logger { writer := zerolog.ConsoleWriter{Out: os.Stderr} return server.ZeroLogWrapper{ @@ -33,37 +28,38 @@ func defaultLogger() log.Logger { } } -func initStore(t *testing.T, db dbm.DB, storeKey string, k, v []byte) { - rs := rootmulti.NewStore(db) - rs.SetPruning(storetypes.PruneNothing) +func initStore(t *testing.T, db dbm.DBConnection, config multi.StoreConfig, storeKey string, k, v []byte) { key := sdk.NewKVStoreKey(storeKey) - rs.MountStoreWithDB(key, storetypes.StoreTypeIAVL, nil) - err := rs.LoadLatestVersion() - require.Nil(t, err) + rs, err := multi.NewStore(db, config) + require.NoError(t, err) + rs.SetPruning(storetypes.PruneNothing) require.Equal(t, int64(0), rs.LastCommitID().Version) // write some data in substore - kv, _ := rs.GetStore(key).(storetypes.KVStore) + kv := rs.GetKVStore(key) require.NotNil(t, kv) kv.Set(k, v) commitID := rs.Commit() require.Equal(t, int64(1), commitID.Version) + require.NoError(t, rs.Close()) } -func checkStore(t *testing.T, db dbm.DB, ver int64, storeKey string, k, v []byte) { - rs := rootmulti.NewStore(db) - rs.SetPruning(storetypes.PruneNothing) +func checkStore(t *testing.T, db dbm.DBConnection, config multi.StoreConfig, ver int64, storeKey string, k, v []byte) { key := sdk.NewKVStoreKey(storeKey) - rs.MountStoreWithDB(key, storetypes.StoreTypeIAVL, nil) - err := rs.LoadLatestVersion() - require.Nil(t, err) + rs, err := multi.NewStore(db, config) + require.NoError(t, err) + rs.SetPruning(storetypes.PruneNothing) require.Equal(t, ver, rs.LastCommitID().Version) - // query data in substore - kv, _ := rs.GetStore(key).(storetypes.KVStore) - - require.NotNil(t, kv) - require.Equal(t, v, kv.Get(k)) + if v != nil { + kv := rs.GetKVStore(key) + require.NotNil(t, kv) + require.Equal(t, v, kv.Get(k)) + } else { + // v == nil indicates the substore was moved and no longer exists + require.Panics(t, func() { _ = rs.GetKVStore(key) }) + } + require.NoError(t, rs.Close()) } // Test that we can make commits and then reload old versions. @@ -89,7 +85,7 @@ func TestSetLoader(t *testing.T) { require.NoError(t, err) cases := map[string]struct { - setLoader func(*baseapp.BaseApp) + setLoader baseapp.AppOption origStoreKey string loadStoreKey string }{ @@ -99,7 +95,7 @@ func TestSetLoader(t *testing.T) { loadStoreKey: "foo", }, "rename with inline opts": { - setLoader: useUpgradeLoader(upgradeHeight, &storetypes.StoreUpgrades{ + setLoader: UpgradeStoreOption(uint64(upgradeHeight), &storetypes.StoreUpgrades{ Renamed: []storetypes.StoreRename{{ OldKey: "foo", NewKey: "bar", @@ -116,47 +112,54 @@ func TestSetLoader(t *testing.T) { for name, tc := range cases { tc := tc t.Run(name, func(t *testing.T) { - // prepare a db with some data - db := dbm.NewMemDB() + origConfig := multi.DefaultStoreConfig() + loadConfig := multi.DefaultStoreConfig() + require.NoError(t, origConfig.RegisterSubstore(tc.origStoreKey, storetypes.StoreTypePersistent)) + require.NoError(t, loadConfig.RegisterSubstore(tc.loadStoreKey, storetypes.StoreTypePersistent)) - initStore(t, db, tc.origStoreKey, k, v) + // prepare a db with some data + db := memdb.NewDB() + initStore(t, db, origConfig, tc.origStoreKey, k, v) // load the app with the existing db - opts := []func(*baseapp.BaseApp){baseapp.SetPruning(storetypes.PruneNothing)} - + opts := []baseapp.AppOption{ + baseapp.SetPruning(storetypes.PruneNothing), + baseapp.SetSubstores(sdk.NewKVStoreKey(tc.origStoreKey)), + } origapp := baseapp.NewBaseApp(t.Name(), defaultLogger(), db, opts...) - origapp.MountStores(sdk.NewKVStoreKey(tc.origStoreKey)) - err := origapp.LoadLatestVersion() - require.Nil(t, err) + require.NoError(t, origapp.Init()) for i := int64(2); i <= upgradeHeight-1; i++ { origapp.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: i}}) res := origapp.Commit() require.NotNil(t, res.Data) } + require.NoError(t, origapp.CloseStore()) + // load the new app with the original app db + opts = []baseapp.AppOption{ + baseapp.SetPruning(storetypes.PruneNothing), + baseapp.SetSubstores(sdk.NewKVStoreKey(tc.loadStoreKey)), + } if tc.setLoader != nil { opts = append(opts, tc.setLoader) } - - // load the new app with the original app db app := baseapp.NewBaseApp(t.Name(), defaultLogger(), db, opts...) - app.MountStores(sdk.NewKVStoreKey(tc.loadStoreKey)) - err = app.LoadLatestVersion() - require.Nil(t, err) + require.NoError(t, app.Init()) // "execute" one block app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: upgradeHeight}}) res := app.Commit() require.NotNil(t, res.Data) + require.NoError(t, app.CloseStore()) // checking the case of the store being renamed if tc.setLoader != nil { - checkStore(t, db, upgradeHeight, tc.origStoreKey, k, nil) + checkStore(t, db, loadConfig, upgradeHeight, tc.origStoreKey, k, nil) } // check db is properly updated - checkStore(t, db, upgradeHeight, tc.loadStoreKey, k, v) + checkStore(t, db, loadConfig, upgradeHeight, tc.loadStoreKey, k, v) }) } } From ab531ff293881dad3e79af2a8e2004d146156add Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Thu, 20 Jan 2022 13:10:54 -0600 Subject: [PATCH 05/78] changelog + docs --- CHANGELOG.md | 1 + docs/core/store.md | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad4c48a0c462..b1572a94d4f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,6 +59,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [\#10311](https://github.com/cosmos/cosmos-sdk/pull/10311) Adds cli to use tips transactions. It adds an `--aux` flag to all CLI tx commands to generate the aux signer data (with optional tip), and a new `tx aux-to-fee` subcommand to let the fee payer gather aux signer data and broadcast the tx * [\#10430](https://github.com/cosmos/cosmos-sdk/pull/10430) ADR-040: Add store/v2 `MultiStore` implementation * [\#10614](https://github.com/cosmos/cosmos-sdk/pull/10614) Support in-place migration ordering +* [\#10174](https://github.com/cosmos/cosmos-sdk/pull/10174) ADR-040: Refactor App to use `v2.MultiStore` ### API Breaking Changes diff --git a/docs/core/store.md b/docs/core/store.md index ae79aa8ddc86..11d16f25b25b 100644 --- a/docs/core/store.md +++ b/docs/core/store.md @@ -234,9 +234,9 @@ When `KVStore.Set` or `KVStore.Delete` methods are called, `listenkv.Store` auto # New Store package (`store/v2`) -The SDK is in the process of transitioning to use the types listed here as the default interface for state storage. At the time of writing, these cannot be used within an application and are not directly compatible with the `CommitMultiStore` and related types. +The SDK is in the process of transitioning to use the types in this package as the default interface for state storage. Note that these types are not all directly compatible with the types in `store/types`. -These types use the new `db` sub-module of Cosmos-SDK (`github.com/cosmos/cosmos-sdk/db`), rather than `tmdb` (`github.com/tendermint/tm-db`). +This package uses the new `db` sub-module of Cosmos-SDK (`github.com/cosmos/cosmos-sdk/db`), rather than `tmdb` (`github.com/tendermint/tm-db`). See [ADR-040](../architecture/adr-040-storage-and-smt-state-commitments.md) for the motivations and design specifications of the change. @@ -246,7 +246,7 @@ An interface providing only the basic CRUD functionality (`Get`, `Set`, `Has`, a ## MultiStore -This is the new interface (or, set of interfaces) for the main client store, replacing the role of `store/types.MultiStore` (v1). There are a few significant differences in behavior compared with v1: +`BaseApp` and derived types now use this as the interface for the main client store, replacing the role of `store/types.MultiStore` (v1). There are a few significant differences in behavior compared with v1: * Commits are atomic and are performed on the entire store state; individual substores cannot be committed separately and cannot have different version numbers. * The store's current version and version history track that of the backing `db.DBConnection`. Past versions are accessible read-only. * The set of valid substores is defined at initialization and cannot be updated dynamically in an existing store instance. From 929f5addd051ba24c40228d77881f3a76e5535d8 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Thu, 20 Jan 2022 14:34:21 -0600 Subject: [PATCH 06/78] fixes group keeper: remove redundant iter.Close() --- simapp/utils.go | 2 +- store/rootmulti/store_test.go | 2 +- store/v2/utils.go | 19 +++++++++++++++++++ x/group/keeper/genesis_test.go | 4 ++-- x/group/keeper/grpc_query.go | 1 - x/group/keeper/invariants_test.go | 18 +++++++++--------- x/simulation/log.go | 2 +- 7 files changed, 33 insertions(+), 15 deletions(-) create mode 100644 store/v2/utils.go diff --git a/simapp/utils.go b/simapp/utils.go index 60612719f46d..00ba1c5a62f8 100644 --- a/simapp/utils.go +++ b/simapp/utils.go @@ -40,7 +40,7 @@ func SetupSimulation(dirPrefix, dbName string) (simtypes.Config, dbm.DBConnectio return simtypes.Config{}, nil, "", nil, false, err } - db, err := badgerdb.NewDB(dbName) + db, err := badgerdb.NewDB(dir) if err != nil { return simtypes.Config{}, nil, "", nil, false, err } diff --git a/store/rootmulti/store_test.go b/store/rootmulti/store_test.go index c1d2909eefaa..a187fef762d0 100644 --- a/store/rootmulti/store_test.go +++ b/store/rootmulti/store_test.go @@ -296,7 +296,7 @@ func TestMultistoreLoadWithUpgrade(t *testing.T) { require.Equal(t, v4, rl4.Get(k4)) // check commitInfo in storage - ci, err := getCommitInfo(db, 2) + ci, err = getCommitInfo(db, 2) require.NoError(t, err) require.Equal(t, int64(2), ci.Version) require.Equal(t, 3, len(ci.StoreInfos), ci.StoreInfos) diff --git a/store/v2/utils.go b/store/v2/utils.go new file mode 100644 index 000000000000..7cd2f5ca9133 --- /dev/null +++ b/store/v2/utils.go @@ -0,0 +1,19 @@ +package types + +import ( + "fmt" +) + +func StoreKeyToType(key StoreKey) (typ StoreType, err error) { + switch key.(type) { + case *KVStoreKey: + typ = StoreTypePersistent + case *MemoryStoreKey: + typ = StoreTypeMemory + case *TransientStoreKey: + typ = StoreTypeTransient + default: + err = fmt.Errorf("unrecognized store key type: %T", key) + } + return +} diff --git a/x/group/keeper/genesis_test.go b/x/group/keeper/genesis_test.go index 59644ea987bb..e6ed2e461e8b 100644 --- a/x/group/keeper/genesis_test.go +++ b/x/group/keeper/genesis_test.go @@ -10,10 +10,10 @@ import ( "github.com/stretchr/testify/suite" "github.com/tendermint/tendermint/libs/log" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/cosmos/cosmos-sdk/db/memdb" "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" @@ -45,7 +45,7 @@ var ( func (s *GenesisTestSuite) SetupSuite() { checkTx := false - db := dbm.NewMemDB() + db := memdb.NewDB() encCdc := simapp.MakeTestEncodingConfig() app := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, simapp.DefaultNodeHome, 5, encCdc, simapp.EmptyAppOptions{}) diff --git a/x/group/keeper/grpc_query.go b/x/group/keeper/grpc_query.go index 5027c14f5913..4c037f3f5261 100644 --- a/x/group/keeper/grpc_query.go +++ b/x/group/keeper/grpc_query.go @@ -269,7 +269,6 @@ func (q Keeper) GroupsByMember(goCtx context.Context, request *group.QueryGroups if err != nil { return nil, err } - defer iter.Close() var members []*group.GroupMember pageRes, err := orm.Paginate(iter, request.Pagination, &members) diff --git a/x/group/keeper/invariants_test.go b/x/group/keeper/invariants_test.go index 853862935c43..5ff5b7a74ea9 100644 --- a/x/group/keeper/invariants_test.go +++ b/x/group/keeper/invariants_test.go @@ -8,13 +8,12 @@ import ( "github.com/tendermint/tendermint/libs/log" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec/types" - "github.com/cosmos/cosmos-sdk/store" - - storetypes "github.com/cosmos/cosmos-sdk/store/types" + "github.com/cosmos/cosmos-sdk/db/memdb" + storetypes "github.com/cosmos/cosmos-sdk/store/v2" + "github.com/cosmos/cosmos-sdk/store/v2/multi" "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/group" @@ -40,11 +39,12 @@ func (s *invariantTestSuite) SetupSuite() { group.RegisterInterfaces(interfaceRegistry) cdc := codec.NewProtoCodec(interfaceRegistry) key := sdk.NewKVStoreKey(group.ModuleName) - db := dbm.NewMemDB() - cms := store.NewCommitMultiStore(db) - cms.MountStoreWithDB(key, storetypes.StoreTypeIAVL, db) - _ = cms.LoadLatestVersion() - sdkCtx := sdk.NewContext(cms, tmproto.Header{}, false, log.NewNopLogger()) + db := memdb.NewDB() + config := multi.DefaultStoreConfig() + s.Require().NoError(config.RegisterSubstore(key.Name(), storetypes.StoreTypePersistent)) + ms, err := multi.NewStore(db, config) + s.Require().NoError(err) + sdkCtx := sdk.NewContext(ms, tmproto.Header{}, false, log.NewNopLogger()) s.ctx = sdkCtx s.cdc = cdc diff --git a/x/simulation/log.go b/x/simulation/log.go index ba22b2af4a7e..96e84264fd35 100644 --- a/x/simulation/log.go +++ b/x/simulation/log.go @@ -63,7 +63,7 @@ func createLogFile() *os.File { if err != nil { panic(err) } - fmt.Printf("Logs to writing to %s\n", filePath) + fmt.Printf("Writing logs to %s\n", filePath) return f } From 25d0a9fc0c8b6de8a6cfaecc34c24ade68855462 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Thu, 20 Jan 2022 18:46:11 -0600 Subject: [PATCH 07/78] changelog fix --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2802b339819a..1d0e7377409f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,7 +56,6 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [\#10507](https://github.com/cosmos/cosmos-sdk/pull/10507) Add middleware for tx priority. * [\#10311](https://github.com/cosmos/cosmos-sdk/pull/10311) Adds cli to use tips transactions. It adds an `--aux` flag to all CLI tx commands to generate the aux signer data (with optional tip), and a new `tx aux-to-fee` subcommand to let the fee payer gather aux signer data and broadcast the tx * [\#10430](https://github.com/cosmos/cosmos-sdk/pull/10430) ADR-040: Add store/v2 `MultiStore` implementation -* [\#10614](https://github.com/cosmos/cosmos-sdk/pull/10614) Support in-place migration ordering * [\#10174](https://github.com/cosmos/cosmos-sdk/pull/10174) ADR-040: Refactor App to use `v2.MultiStore` ### API Breaking Changes From 355f1815cc026f4fda7b460e08401d5a9dd0ca44 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Thu, 20 Jan 2022 19:01:57 -0600 Subject: [PATCH 08/78] cleanup --- baseapp/baseapp.go | 93 +++++++++++++++----------------------- baseapp/options.go | 2 +- server/export_test.go | 1 - server/mock/app.go | 3 ++ server/mock/store.go | 5 -- server/mock/store_test.go | 1 - server/util.go | 4 +- simapp/sim_bench_test.go | 3 +- simapp/sim_test.go | 3 +- simapp/test_helpers.go | 5 -- simapp/utils.go | 7 ++- testutil/context.go | 3 +- types/store.go | 19 ++------ x/params/types/subspace.go | 3 +- 14 files changed, 53 insertions(+), 99 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index f677daa947be..abe956fce0b1 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -3,7 +3,6 @@ package baseapp import ( "context" "fmt" - // "reflect" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" @@ -11,10 +10,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec/types" dbm "github.com/cosmos/cosmos-sdk/db" - // dbutil "github.com/cosmos/cosmos-sdk/internal/db" "github.com/cosmos/cosmos-sdk/snapshots" - // "github.com/cosmos/cosmos-sdk/store" - // "github.com/cosmos/cosmos-sdk/store/rootmulti" stypes "github.com/cosmos/cosmos-sdk/store/v2" "github.com/cosmos/cosmos-sdk/store/v2/multi" sdk "github.com/cosmos/cosmos-sdk/types" @@ -26,6 +22,9 @@ const ( runTxModeReCheck // Recheck a (pending) transaction after a commit runTxModeSimulate // Simulate a transaction runTxModeDeliver // Deliver a transaction + + OptionOrderDefault = iota + OptionOrderAfterStore ) var ( @@ -36,32 +35,44 @@ type ( // Enum mode for app.runTx runTxMode uint8 - // StoreLoader defines a customizable function to control how we load the CommitMultiStore + // StoreConfig is an alias for the config parameter type of the CommitMultiStore. + StoreConfig = multi.StoreConfig + // StoreOption provides a functional callback to modify StoreConfig. + // The callback is passed the loaded height as uint64. + StoreOption func(*StoreConfig, uint64) error + // StoreConstructor defines a customizable function to control how we load the CommitMultiStore // from disk. This is useful for state migration, when loading a datastore written with // an older version of the software. In particular, if a module changed the substore key name // (or removed a substore) between two versions of the software. - // StoreLoader func(ms sdk.CommitMultiStore) error - - // StoreLoader func(initialVersion uint64) (sdk.CommitMultiStore, error) - // StoreLoader func(multi.StoreConfig) (sdk.CommitMultiStore, error) - // StoreLoader func(*multi.StoreConfig, uint64) (sdk.CommitMultiStore, error) - StoreConfig = multi.StoreConfig - StoreOption func(*StoreConfig, uint64) error - StoreConstructor func(dbm.DBConnection, multi.StoreConfig) (sdk.CommitMultiStore, error) + StoreConstructor func(dbm.DBConnection, StoreConfig) (sdk.CommitMultiStore, error) + + // AppOption provides a configuration option for a BaseApp + AppOption interface { + Apply(*BaseApp) + Order() OptionOrder + } + // OptionOrder represents the required ordering for order dependent options + OptionOrder int + // AppOptionFunc wraps a functional option for BaseApp + AppOptionFunc func(*BaseApp) + // AppOptionOrdered wraps an order-dependent functional option + AppOptionOrdered struct { + AppOptionFunc + order OptionOrder + } ) // BaseApp reflects the ABCI application implementation. type BaseApp struct { // nolint: maligned // initialized on creation - logger log.Logger - name string // application name from abci.Info - db dbm.DBConnection - storeCtor StoreConstructor - storeOpts []StoreOption // options to configure root store - store sdk.CommitMultiStore // Main (uncached) state - // storeLoader StoreLoader // function to handle store loading - queryRouter sdk.QueryRouter // router for redirecting query calls - grpcQueryRouter *GRPCQueryRouter // router for redirecting gRPC query calls + logger log.Logger + name string // application name from abci.Info + db dbm.DBConnection + storeCtor StoreConstructor + storeOpts []StoreOption // options to configure root store + store sdk.CommitMultiStore // Main (uncached) state + queryRouter sdk.QueryRouter // router for redirecting query calls + grpcQueryRouter *GRPCQueryRouter // router for redirecting gRPC query calls interfaceRegistry types.InterfaceRegistry txHandler tx.Handler // txHandler for {Deliver,Check}Tx and simulations @@ -84,6 +95,9 @@ type BaseApp struct { // nolint: maligned checkState *state // for CheckTx deliverState *state // for DeliverTx + // an inter-block write-through cache provided to the context during deliverState + interBlockCache sdk.MultiStorePersistentCache + // absent validators from begin block voteInfos []abci.VoteInfo @@ -138,27 +152,6 @@ type BaseApp struct { // nolint: maligned abciListeners []ABCIListener } -// OptionOrder represents the required ordering for options that are order dependent -type OptionOrder int - -const ( - OptionOrderDefault = iota - OptionOrderAfterStore -) - -// AppOption is a configuration option for a BaseApp -type AppOption interface { - Apply(*BaseApp) - Order() OptionOrder -} - -type AppOptionFunc func(*BaseApp) - -type AppOptionOrdered struct { - AppOptionFunc - order OptionOrder -} - func (opt AppOptionOrdered) Order() OptionOrder { return opt.order } func (opt AppOptionFunc) Apply(app *BaseApp) { opt(app) } @@ -205,18 +198,6 @@ func NewBaseApp( for _, option := range afterStoreOpts { option.Apply(app) } - - // // TODO: conditional loading of multistore/rootstore - // if loadv2 { - // opts := store.StoreConfig{PersistentCache: app.interBlockCache} - // store, err := multi.NewStore(db, opts) - // if err != nil { - // panic(err) - // } - // } else { - // app.store = NewCommitMultiStore(dbutil.ConnectionAsTmdb(db)) - // } - return app } @@ -267,7 +248,7 @@ func (app *BaseApp) CloseStore() error { } // DefaultStoreConstructor attempts to create a new store, but loads from existing data if present. -func DefaultStoreConstructor(db dbm.DBConnection, config multi.StoreConfig) (stypes.CommitMultiStore, error) { +func DefaultStoreConstructor(db dbm.DBConnection, config StoreConfig) (stypes.CommitMultiStore, error) { return multi.NewStore(db, config) } diff --git a/baseapp/options.go b/baseapp/options.go index 47cb63611e37..63359ea99929 100644 --- a/baseapp/options.go +++ b/baseapp/options.go @@ -84,7 +84,7 @@ func SetSnapshotStore(snapshotStore *snapshots.Store) AppOptionOrdered { } } -// SetSubstores store registers substores according to app configuration +// SetSubstores registers substores according to app configuration func SetSubstores(keys ...storetypes.StoreKey) StoreOption { return func(config *StoreConfig, _ uint64) error { for _, key := range keys { diff --git a/server/export_test.go b/server/export_test.go index fc169603ca1e..bd6bbb41ecdd 100644 --- a/server/export_test.go +++ b/server/export_test.go @@ -33,7 +33,6 @@ func TestExportCmd_ConsensusParams(t *testing.T) { tempDir := t.TempDir() _, ctx, _, cmd := setupApp(t, tempDir) - // require.NoError(t, app.CloseStore()) output := &bytes.Buffer{} cmd.SetOut(output) diff --git a/server/mock/app.go b/server/mock/app.go index d4c3fa27a8ff..6d41097ce28d 100644 --- a/server/mock/app.go +++ b/server/mock/app.go @@ -63,6 +63,9 @@ func NewApp(rootDir string, logger log.Logger) (abci.Application, error) { }, ) baseApp.SetTxHandler(txHandler) + if err = baseApp.Init(); err != nil { + return nil, err + } return baseApp, nil } diff --git a/server/mock/store.go b/server/mock/store.go index 7c18c71d24d1..10afda1fc634 100644 --- a/server/mock/store.go +++ b/server/mock/store.go @@ -3,7 +3,6 @@ package mock import ( "io" - // storetypes "github.com/cosmos/cosmos-sdk/store/types" storetypes "github.com/cosmos/cosmos-sdk/store/v2" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -74,10 +73,6 @@ func (ms multiStore) GetKVStore(key storetypes.StoreKey) sdk.KVStore { return ms.kv[key] } -// func (ms multiStore) GetStoreType() storetypes.StoreType { -// panic("not implemented") -// } - func (ms multiStore) SetInitialVersion(version uint64) error { panic("not implemented") } diff --git a/server/mock/store_test.go b/server/mock/store_test.go index 211603675f7b..f1cf5d2fba29 100644 --- a/server/mock/store_test.go +++ b/server/mock/store_test.go @@ -5,7 +5,6 @@ import ( "github.com/stretchr/testify/require" - // storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" ) diff --git a/server/util.go b/server/util.go index 2f20f7a540d1..435649730096 100644 --- a/server/util.go +++ b/server/util.go @@ -373,9 +373,7 @@ func addrToIP(addr net.Addr) net.IP { } func openDB(rootDir string) (dbm.DBConnection, error) { - // dataDir := filepath.Join(rootDir, "data") - // return sdk.NewLevelDB("application", dataDir) - return badgerdb.NewDB(rootDir) + return badgerdb.NewDB(filepath.Join(rootDir, "data")) } func openTraceWriter(traceWriterFile string) (w io.Writer, err error) { diff --git a/simapp/sim_bench_test.go b/simapp/sim_bench_test.go index 86751d4a8c68..b5592051001f 100644 --- a/simapp/sim_bench_test.go +++ b/simapp/sim_bench_test.go @@ -7,7 +7,6 @@ import ( tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - // "github.com/cosmos/cosmos-sdk/store" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" "github.com/cosmos/cosmos-sdk/x/simulation" ) @@ -58,7 +57,7 @@ func BenchmarkFullAppSimulation(b *testing.B) { } if config.Commit { - // PrintStats(db) // TODO + PrintStats(db) } } diff --git a/simapp/sim_test.go b/simapp/sim_test.go index 4abf070320a5..269160d13153 100644 --- a/simapp/sim_test.go +++ b/simapp/sim_test.go @@ -18,7 +18,6 @@ import ( "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/db/memdb" "github.com/cosmos/cosmos-sdk/simapp/helpers" - // "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" @@ -54,9 +53,9 @@ var fauxMerkleModeOpt = baseapp.AppOptionFunc(func(bapp *baseapp.BaseApp) { // interBlockCacheOpt returns a BaseApp option function that sets the persistent // inter-block write-through cache. +// TODO: implement this cache as enhancement to v2 multistore func interBlockCacheOpt() baseapp.AppOptionFunc { return func(*baseapp.BaseApp) {} - // return baseapp.SetInterBlockCache(store.NewCommitKVStoreCacheManager()) } func TestFullAppSimulation(t *testing.T) { diff --git a/simapp/test_helpers.go b/simapp/test_helpers.go index 3e3d50851ee7..9029b1e14874 100644 --- a/simapp/test_helpers.go +++ b/simapp/test_helpers.go @@ -29,7 +29,6 @@ import ( "github.com/cosmos/cosmos-sdk/simapp/helpers" "github.com/cosmos/cosmos-sdk/simapp/params" "github.com/cosmos/cosmos-sdk/testutil/mock" - // "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/errors" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" @@ -70,10 +69,6 @@ type SetupOptions struct { func setup(withGenesis bool, invCheckPeriod uint) (*SimApp, GenesisState) { encCdc := MakeTestEncodingConfig() - // rstore, err := store.NewCommitRootStore(memdb.NewDB(), store.RootStoreConfig{}) - // if err != nil { - // panic(err) - // } app := NewSimApp(log.NewNopLogger(), memdb.NewDB(), nil, true, map[int64]bool{}, DefaultNodeHome, invCheckPeriod, encCdc, EmptyAppOptions{}) if withGenesis { return app, NewDefaultGenesisState(encCdc.Codec) diff --git a/simapp/utils.go b/simapp/utils.go index 00ba1c5a62f8..05ee80c504e7 100644 --- a/simapp/utils.go +++ b/simapp/utils.go @@ -105,11 +105,10 @@ func CheckExportSimulation( } // PrintStats prints the corresponding statistics from the app DB. +// TODO: implement stats collection for DBConnection func PrintStats(db dbm.DBConnection) { - fmt.Println("\nLevelDB Stats") - fmt.Println("\nTODO") // TODO - // fmt.Println(db.Stats()["leveldb.stats"]) - // fmt.Println("LevelDB cached block size", db.Stats()["leveldb.cachedblock"]) + // stats := db.Stats() + fmt.Println("\nDB Stats: not available") } // GetSimulationLog unmarshals the KVPair's Value to the corresponding type based on the diff --git a/testutil/context.go b/testutil/context.go index a81468f68524..010ca499c60b 100644 --- a/testutil/context.go +++ b/testutil/context.go @@ -5,7 +5,6 @@ import ( tmproto "github.com/tendermint/tendermint/proto/tendermint/types" "github.com/cosmos/cosmos-sdk/db/memdb" - // "github.com/cosmos/cosmos-sdk/store" stypes "github.com/cosmos/cosmos-sdk/store/v2" "github.com/cosmos/cosmos-sdk/store/v2/multi" sdk "github.com/cosmos/cosmos-sdk/types" @@ -20,7 +19,7 @@ func DefaultContext(key, tkey stypes.StoreKey) (ret sdk.Context) { } }() db := memdb.NewDB() - opts := multi.DefaultStoreConfig() + opts := multi.DefaultStoreParams() err = opts.RegisterSubstore(key.Name(), stypes.StoreTypePersistent) if err != nil { return diff --git a/types/store.go b/types/store.go index 37e14722a901..bd0fc666d94e 100644 --- a/types/store.go +++ b/types/store.go @@ -11,21 +11,10 @@ import ( ) type ( - PruningOptions = types.PruningOptions -) - -type ( - Store = types.Store - Committer = types.Committer - CommitStore = types.CommitStore - Queryable = types.Queryable - // MultiStore = types.MultiStore - // CacheMultiStore = types.CacheMultiStore - // CommitMultiStore = types.CommitMultiStore - // MultiStorePersistentCache = types.MultiStorePersistentCache - KVStore = types.KVStore - Iterator = types.Iterator - // MultiStoreConfig = multi.StoreConfig + PruningOptions = types2.PruningOptions + Queryable = types2.Queryable + KVStore = types2.KVStore + Iterator = types2.Iterator BasicMultiStore = types2.BasicMultiStore CommitMultiStore = types2.CommitMultiStore CacheMultiStore = types2.CacheMultiStore diff --git a/x/params/types/subspace.go b/x/params/types/subspace.go index 9c98ce774be7..e528a185eb9e 100644 --- a/x/params/types/subspace.go +++ b/x/params/types/subspace.go @@ -80,8 +80,7 @@ func (s Subspace) kvStore(ctx sdk.Context) sdk.KVStore { func (s Subspace) transientStore(ctx sdk.Context) sdk.KVStore { // append here is safe, appends within a function won't cause // weird side effects when its singlethreaded - ts := ctx.TransientStore(s.tkey) - return prefix.NewStore(ts, append(s.name, '/')) + return prefix.NewStore(ctx.TransientStore(s.tkey), append(s.name, '/')) } // Validate attempts to validate a parameter value by its key. If the key is not From 8cbf4b25f1c15d5145d161be41c8fa8e149de822 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Thu, 20 Jan 2022 19:09:42 -0600 Subject: [PATCH 09/78] nit, StoreConfig =>StoreParams --- baseapp/baseapp.go | 14 +++--- baseapp/baseapp_test.go | 4 +- baseapp/options.go | 8 ++-- simapp/app.go | 2 +- store/v2/multi/store.go | 16 +++---- store/v2/multi/store_test.go | 68 ++++++++++++++--------------- x/group/keeper/invariants_test.go | 2 +- x/params/types/subspace_test.go | 2 +- x/upgrade/types/storeloader.go | 5 +-- x/upgrade/types/storeloader_test.go | 8 ++-- 10 files changed, 64 insertions(+), 65 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index abe956fce0b1..3f53d8f5ae56 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -35,16 +35,16 @@ type ( // Enum mode for app.runTx runTxMode uint8 - // StoreConfig is an alias for the config parameter type of the CommitMultiStore. - StoreConfig = multi.StoreConfig - // StoreOption provides a functional callback to modify StoreConfig. + // StoreParams is an alias for the config parameter type of the CommitMultiStore. + StoreParams = multi.StoreParams + // StoreOption provides a functional callback to modify StoreParams. // The callback is passed the loaded height as uint64. - StoreOption func(*StoreConfig, uint64) error + StoreOption func(*StoreParams, uint64) error // StoreConstructor defines a customizable function to control how we load the CommitMultiStore // from disk. This is useful for state migration, when loading a datastore written with // an older version of the software. In particular, if a module changed the substore key name // (or removed a substore) between two versions of the software. - StoreConstructor func(dbm.DBConnection, StoreConfig) (sdk.CommitMultiStore, error) + StoreConstructor func(dbm.DBConnection, StoreParams) (sdk.CommitMultiStore, error) // AppOption provides a configuration option for a BaseApp AppOption interface { @@ -232,7 +232,7 @@ func (app *BaseApp) loadStore() error { return err } latest := versions.Last() - config := multi.DefaultStoreConfig() + config := multi.DefaultStoreParams() for _, opt := range app.storeOpts { opt(&config, latest) } @@ -248,7 +248,7 @@ func (app *BaseApp) CloseStore() error { } // DefaultStoreConstructor attempts to create a new store, but loads from existing data if present. -func DefaultStoreConstructor(db dbm.DBConnection, config StoreConfig) (stypes.CommitMultiStore, error) { +func DefaultStoreConstructor(db dbm.DBConnection, config StoreParams) (stypes.CommitMultiStore, error) { return multi.NewStore(db, config) } diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index a76ce604b826..de070bbbbc1c 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -339,7 +339,7 @@ var useDefaultConstructor = baseapp.AppOptionFunc(func(app *baseapp.BaseApp) { func initStore(t *testing.T, db dbm.DBConnection, storeKey string, k, v []byte) { key := sdk.NewKVStoreKey(storeKey) - opts := multi.DefaultStoreConfig() + opts := multi.DefaultStoreParams() opts.Pruning = stypes.PruneNothing require.NoError(t, opts.RegisterSubstore(key.Name(), stypes.StoreTypePersistent)) rs, err := multi.NewStore(db, opts) @@ -356,7 +356,7 @@ func initStore(t *testing.T, db dbm.DBConnection, storeKey string, k, v []byte) } func checkStore(t *testing.T, db dbm.DBConnection, ver int64, storeKey string, k, v []byte) { - opts := multi.DefaultStoreConfig() + opts := multi.DefaultStoreParams() opts.Pruning = stypes.PruneNothing key := sdk.NewKVStoreKey(storeKey) require.NoError(t, opts.RegisterSubstore(key.Name(), stypes.StoreTypePersistent)) diff --git a/baseapp/options.go b/baseapp/options.go index 63359ea99929..0d5d698dac71 100644 --- a/baseapp/options.go +++ b/baseapp/options.go @@ -16,7 +16,7 @@ import ( // SetPruning sets a pruning option on the multistore associated with the app func SetPruning(opts sdk.PruningOptions) StoreOption { - return func(config *StoreConfig, _ uint64) error { config.Pruning = opts; return nil } + return func(config *StoreParams, _ uint64) error { config.Pruning = opts; return nil } } // SetMinGasPrices returns an option that sets the minimum gas prices on the app. @@ -59,7 +59,7 @@ func SetIndexEvents(ie []string) AppOptionFunc { // SetInterBlockCache provides a BaseApp option function that sets the // inter-block cache. func SetInterBlockCache(cache sdk.MultiStorePersistentCache) AppOptionFunc { - opt := func(cfg *StoreConfig, v uint64) error { + opt := func(cfg *StoreParams, v uint64) error { cfg.PersistentCache = cache return nil } @@ -86,7 +86,7 @@ func SetSnapshotStore(snapshotStore *snapshots.Store) AppOptionOrdered { // SetSubstores registers substores according to app configuration func SetSubstores(keys ...storetypes.StoreKey) StoreOption { - return func(config *StoreConfig, _ uint64) error { + return func(config *StoreParams, _ uint64) error { for _, key := range keys { typ, err := storetypes.StoreKeyToType(key) if err != nil { @@ -189,7 +189,7 @@ func (app *BaseApp) SetFauxMerkleMode() { // SetCommitMultiStoreTracer sets the store tracer on the BaseApp's underlying // CommitMultiStore. func (app *BaseApp) SetCommitMultiStoreTracer(w io.Writer) { - opt := func(cfg *StoreConfig, v uint64) error { + opt := func(cfg *StoreParams, v uint64) error { cfg.TraceWriter = w return nil } diff --git a/simapp/app.go b/simapp/app.go index 23da852740e0..9bd97b351770 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -231,7 +231,7 @@ func NewSimApp( // not include this key. memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey, "testingkey") // initialize stores - setNamespaces := func(config *baseapp.StoreConfig, ver uint64) error { + setNamespaces := func(config *baseapp.StoreParams, ver uint64) error { for _, key := range keys { typ, err := storev2.StoreKeyToType(key) if err != nil { diff --git a/store/v2/multi/store.go b/store/v2/multi/store.go index 9649ab0d7591..f4c5e300dff3 100644 --- a/store/v2/multi/store.go +++ b/store/v2/multi/store.go @@ -54,8 +54,8 @@ func ErrStoreNotFound(skey string) error { return fmt.Errorf("store does not exist for key: %s", skey) } -// StoreConfig is used to define a schema and other options and pass them to the MultiStore constructor. -type StoreConfig struct { +// StoreParams is used to define a schema and other options and pass them to the MultiStore constructor. +type StoreParams struct { // Version pruning options for backing DBs. Pruning types.PruningOptions // The minimum allowed version number. @@ -76,7 +76,7 @@ type StoreSchema map[string]types.StoreType // Store is the main persistent store type implementing CommitMultiStore. // Substores consist of an SMT-based state commitment store and state storage. -// Substores must be reserved in the StoreConfig or defined as part of a StoreUpgrade in order to be valid. +// Substores must be reserved in the StoreParams or defined as part of a StoreUpgrade in order to be valid. // Note: // The state commitment data and proof are structured in the same basic pattern as the MultiStore, but use an SMT rather than IAVL tree: // * The state commitment store of each substore consists of a independent SMT. @@ -92,7 +92,7 @@ type Store struct { tran *transient.Store mtx sync.RWMutex - // Copied from StoreConfig + // Copied from StoreParams Pruning types.PruningOptions InitialVersion uint64 // if *traceListenMixin @@ -147,10 +147,10 @@ func newTraceListenMixin() *traceListenMixin { return &traceListenMixin{listeners: map[string][]types.WriteListener{}} } -// DefaultStoreConfig returns a MultiStore config with an empty schema, a single backing DB, +// DefaultStoreParams returns a MultiStore config with an empty schema, a single backing DB, // pruning with PruneDefault, no listeners and no tracer. -func DefaultStoreConfig() StoreConfig { - return StoreConfig{ +func DefaultStoreParams() StoreParams { + return StoreParams{ Pruning: types.PruneDefault, SchemaBuilder: SchemaBuilder{ StoreSchema: StoreSchema{}, @@ -213,7 +213,7 @@ func readSavedSchema(bucket dbm.DBReader) (*SchemaBuilder, error) { // NewStore constructs a MultiStore directly from a database. // Creates a new store if no data exists; otherwise loads existing data. -func NewStore(db dbm.DBConnection, opts StoreConfig) (ret *Store, err error) { +func NewStore(db dbm.DBConnection, opts StoreParams) (ret *Store, err error) { versions, err := db.Versions() if err != nil { return diff --git a/store/v2/multi/store_test.go b/store/v2/multi/store_test.go index f8e9de24853f..906f8c87bc41 100644 --- a/store/v2/multi/store_test.go +++ b/store/v2/multi/store_test.go @@ -32,14 +32,14 @@ var ( skey_3b = types.NewKVStoreKey("store3b") ) -func simpleStoreConfig(t *testing.T) StoreConfig { - opts := DefaultStoreConfig() +func simpleStoreParams(t *testing.T) StoreParams { + opts := DefaultStoreParams() require.NoError(t, opts.RegisterSubstore(skey_1.Name(), types.StoreTypePersistent)) return opts } -func storeConfig123(t *testing.T) StoreConfig { - opts := DefaultStoreConfig() +func storeConfig123(t *testing.T) StoreParams { + opts := DefaultStoreParams() opts.Pruning = types.PruneNothing require.NoError(t, opts.RegisterSubstore(skey_1.Name(), types.StoreTypePersistent)) require.NoError(t, opts.RegisterSubstore(skey_2.Name(), types.StoreTypePersistent)) @@ -48,7 +48,7 @@ func storeConfig123(t *testing.T) StoreConfig { } func newSubStoreWithData(t *testing.T, db dbm.DBConnection, storeData map[string]string) (*Store, types.KVStore) { - root, err := NewStore(db, simpleStoreConfig(t)) + root, err := NewStore(db, simpleStoreParams(t)) require.NoError(t, err) store := root.GetKVStore(skey_1) @@ -94,23 +94,23 @@ func TestGetSetHasDelete(t *testing.T) { func TestConstructors(t *testing.T) { db := memdb.NewDB() - store, err := NewStore(db, simpleStoreConfig(t)) + store, err := NewStore(db, simpleStoreParams(t)) require.NoError(t, err) _ = store.GetKVStore(skey_1) store.Commit() require.NoError(t, store.Close()) t.Run("fail to load if InitialVersion > lowest existing version", func(t *testing.T) { - opts := StoreConfig{InitialVersion: 5, Pruning: types.PruneNothing} + opts := StoreParams{InitialVersion: 5, Pruning: types.PruneNothing} store, err = NewStore(db, opts) require.Error(t, err) db.Close() }) t.Run("can't load store when db.Versions fails", func(t *testing.T) { - store, err = NewStore(dbVersionsFails{memdb.NewDB()}, DefaultStoreConfig()) + store, err = NewStore(dbVersionsFails{memdb.NewDB()}, DefaultStoreParams()) require.Error(t, err) - store, err = NewStore(db, StoreConfig{StateCommitmentDB: dbVersionsFails{memdb.NewDB()}}) + store, err = NewStore(db, StoreParams{StateCommitmentDB: dbVersionsFails{memdb.NewDB()}}) require.Error(t, err) }) @@ -118,24 +118,24 @@ func TestConstructors(t *testing.T) { merkledb := memdb.NewDB() w := db.Writer() t.Run("can't use a DB with open writers", func(t *testing.T) { - store, err = NewStore(db, DefaultStoreConfig()) + store, err = NewStore(db, DefaultStoreParams()) require.Error(t, err) w.Discard() w = merkledb.Writer() - store, err = NewStore(db, StoreConfig{StateCommitmentDB: merkledb}) + store, err = NewStore(db, StoreParams{StateCommitmentDB: merkledb}) require.Error(t, err) w.Discard() }) t.Run("can't use DBs with different version history", func(t *testing.T) { merkledb.SaveNextVersion() - store, err = NewStore(db, StoreConfig{StateCommitmentDB: merkledb}) + store, err = NewStore(db, StoreParams{StateCommitmentDB: merkledb}) require.Error(t, err) }) merkledb.Close() t.Run("can't load existing store if we can't access root hash", func(t *testing.T) { - store, err = NewStore(db, simpleStoreConfig(t)) + store, err = NewStore(db, simpleStoreParams(t)) require.NoError(t, err) store.Commit() require.NoError(t, store.Close()) @@ -146,10 +146,10 @@ func TestConstructors(t *testing.T) { w.Delete(s1RootKey) w.Commit() db.SaveNextVersion() - store, err = NewStore(db, DefaultStoreConfig()) + store, err = NewStore(db, DefaultStoreParams()) require.Error(t, err) // ...or, because of an error - store, err = NewStore(dbRWCrudFails{db}, DefaultStoreConfig()) + store, err = NewStore(dbRWCrudFails{db}, DefaultStoreParams()) require.Error(t, err) }) } @@ -216,7 +216,7 @@ func TestIterators(t *testing.T) { } func TestCommit(t *testing.T) { - testBasic := func(opts StoreConfig) { + testBasic := func(opts StoreParams) { db := memdb.NewDB() store, err := NewStore(db, opts) require.NoError(t, err) @@ -246,7 +246,7 @@ func TestCommit(t *testing.T) { require.NotEqual(t, previd.Version, id.Version) } } - basicOpts := simpleStoreConfig(t) + basicOpts := simpleStoreParams(t) basicOpts.Pruning = types.PruneNothing t.Run("sanity tests for Merkle hashing", func(t *testing.T) { testBasic(basicOpts) @@ -260,7 +260,7 @@ func TestCommit(t *testing.T) { testFailedCommit := func(t *testing.T, store *Store, db dbm.DBConnection, - opts StoreConfig) { + opts StoreParams) { if db == nil { db = store.stateDB } @@ -285,7 +285,7 @@ func TestCommit(t *testing.T) { require.NoError(t, store.Close()) } - opts := simpleStoreConfig(t) + opts := simpleStoreParams(t) opts.Pruning = types.PruneNothing // Ensure Store's commit is rolled back in each failure case... @@ -323,7 +323,7 @@ func TestCommit(t *testing.T) { testFailedCommit(t, store, nil, opts) }) - opts = simpleStoreConfig(t) + opts = simpleStoreParams(t) t.Run("recover after stateDB.Versions error triggers failure", func(t *testing.T) { db := memdb.NewDB() store, err := NewStore(db, opts) @@ -358,7 +358,7 @@ func TestCommit(t *testing.T) { }) t.Run("first commit version matches InitialVersion", func(t *testing.T) { - opts = simpleStoreConfig(t) + opts = simpleStoreParams(t) opts.InitialVersion = 5 opts.Pruning = types.PruneNothing opts.StateCommitmentDB = memdb.NewDB() @@ -368,14 +368,14 @@ func TestCommit(t *testing.T) { }) // test improbable failures to fill out test coverage - opts = simpleStoreConfig(t) + opts = simpleStoreParams(t) store, err := NewStore(memdb.NewDB(), opts) require.NoError(t, err) store.Commit() store.stateDB = dbVersionsFails{store.stateDB} require.Panics(t, func() { store.LastCommitID() }) - opts = simpleStoreConfig(t) + opts = simpleStoreParams(t) opts.StateCommitmentDB = memdb.NewDB() store, err = NewStore(memdb.NewDB(), opts) require.NoError(t, err) @@ -406,7 +406,7 @@ func TestPruning(t *testing.T) { for tci, tc := range testCases { dbs := []dbm.DBConnection{memdb.NewDB(), memdb.NewDB()} - opts := simpleStoreConfig(t) + opts := simpleStoreParams(t) opts.Pruning = tc.PruningOptions opts.StateCommitmentDB = dbs[1] store, err := NewStore(dbs[0], opts) @@ -440,7 +440,7 @@ func TestPruning(t *testing.T) { 20: []uint64{5, 10, 15, 20}, } db := memdb.NewDB() - opts := simpleStoreConfig(t) + opts := simpleStoreParams(t) opts.Pruning = types.PruningOptions{0, 5, 10} store, err := NewStore(db, opts) require.NoError(t, err) @@ -496,7 +496,7 @@ func TestQuery(t *testing.T) { valExpSub2, err := KVs2.Marshal() require.NoError(t, err) - store, err := NewStore(memdb.NewDB(), simpleStoreConfig(t)) + store, err := NewStore(memdb.NewDB(), simpleStoreParams(t)) require.NoError(t, err) cid := store.Commit() ver := cid.Version @@ -570,7 +570,7 @@ func TestQuery(t *testing.T) { }) // querying an empty store will fail - store2, err := NewStore(memdb.NewDB(), simpleStoreConfig(t)) + store2, err := NewStore(memdb.NewDB(), simpleStoreParams(t)) require.NoError(t, err) qres = store2.Query(queryHeight0) require.True(t, qres.IsErr()) @@ -625,7 +625,7 @@ func TestQuery(t *testing.T) { testProve() store.Close() - opts := simpleStoreConfig(t) + opts := simpleStoreParams(t) opts.StateCommitmentDB = memdb.NewDB() store, err = NewStore(memdb.NewDB(), opts) require.NoError(t, err) @@ -636,8 +636,8 @@ func TestQuery(t *testing.T) { }) } -func TestStoreConfig(t *testing.T) { - opts := DefaultStoreConfig() +func TestStoreParams(t *testing.T) { + opts := DefaultStoreParams() // Fail with invalid types require.Error(t, opts.RegisterSubstore(skey_1.Name(), types.StoreTypeDB)) require.Error(t, opts.RegisterSubstore(skey_1.Name(), types.StoreTypeSMT)) @@ -651,7 +651,7 @@ func TestStoreConfig(t *testing.T) { } func TestMultiStoreBasic(t *testing.T) { - opts := DefaultStoreConfig() + opts := DefaultStoreParams() err := opts.RegisterSubstore(skey_1.Name(), types.StoreTypePersistent) require.NoError(t, err) db := memdb.NewDB() @@ -801,7 +801,7 @@ func TestMultiStoreMigration(t *testing.T) { require.Error(t, err) // pass in a schema reflecting the migrations - migratedOpts := DefaultStoreConfig() + migratedOpts := DefaultStoreParams() err = migratedOpts.RegisterSubstore(skey_1.Name(), types.StoreTypePersistent) require.NoError(t, err) err = migratedOpts.RegisterSubstore(skey_2b.Name(), types.StoreTypePersistent) @@ -861,7 +861,7 @@ func TestTrace(t *testing.T) { expected_IterValue := "{\"operation\":\"iterValue\",\"key\":\"\",\"value\":\"dGVzdC12YWx1ZQ==\",\"metadata\":{\"blockHeight\":64}}\n" db := memdb.NewDB() - opts := simpleStoreConfig(t) + opts := simpleStoreParams(t) require.NoError(t, opts.RegisterSubstore(skey_2.Name(), types.StoreTypeMemory)) require.NoError(t, opts.RegisterSubstore(skey_3.Name(), types.StoreTypeTransient)) @@ -939,7 +939,7 @@ func TestListeners(t *testing.T) { var marshaller = codec.NewProtoCodec(interfaceRegistry) db := memdb.NewDB() - opts := simpleStoreConfig(t) + opts := simpleStoreParams(t) require.NoError(t, opts.RegisterSubstore(skey_2.Name(), types.StoreTypeMemory)) require.NoError(t, opts.RegisterSubstore(skey_3.Name(), types.StoreTypeTransient)) diff --git a/x/group/keeper/invariants_test.go b/x/group/keeper/invariants_test.go index 5ff5b7a74ea9..8809aa9f5730 100644 --- a/x/group/keeper/invariants_test.go +++ b/x/group/keeper/invariants_test.go @@ -40,7 +40,7 @@ func (s *invariantTestSuite) SetupSuite() { cdc := codec.NewProtoCodec(interfaceRegistry) key := sdk.NewKVStoreKey(group.ModuleName) db := memdb.NewDB() - config := multi.DefaultStoreConfig() + config := multi.DefaultStoreParams() s.Require().NoError(config.RegisterSubstore(key.Name(), storetypes.StoreTypePersistent)) ms, err := multi.NewStore(db, config) s.Require().NoError(err) diff --git a/x/params/types/subspace_test.go b/x/params/types/subspace_test.go index bf7d16cc6c5f..068ee3026a11 100644 --- a/x/params/types/subspace_test.go +++ b/x/params/types/subspace_test.go @@ -31,7 +31,7 @@ type SubspaceTestSuite struct { func (suite *SubspaceTestSuite) SetupTest() { db := memdb.NewDB() - config := multi.DefaultStoreConfig() + config := multi.DefaultStoreParams() suite.NoError(config.RegisterSubstore(key.Name(), storetypes.StoreTypePersistent)) suite.NoError(config.RegisterSubstore(tkey.Name(), storetypes.StoreTypeTransient)) ms, err := multi.NewStore(db, config) diff --git a/x/upgrade/types/storeloader.go b/x/upgrade/types/storeloader.go index 4306dab62992..2d533c7f045d 100644 --- a/x/upgrade/types/storeloader.go +++ b/x/upgrade/types/storeloader.go @@ -3,17 +3,16 @@ package types import ( "github.com/cosmos/cosmos-sdk/baseapp" storetypes "github.com/cosmos/cosmos-sdk/store/v2" - "github.com/cosmos/cosmos-sdk/store/v2/multi" ) // UpgradeStoreOption is used to prepare baseapp with a fixed StoreOption. // This is useful for custom upgrade loading logic. func UpgradeStoreOption(upgradeHeight uint64, storeUpgrades *storetypes.StoreUpgrades) baseapp.StoreOption { - return func(config *multi.StoreConfig, loadHeight uint64) error { + return func(par *baseapp.StoreParams, loadHeight uint64) error { // Check if the current commit version and upgrade height matches if upgradeHeight == loadHeight+1 { if len(storeUpgrades.Renamed) > 0 || len(storeUpgrades.Deleted) > 0 || len(storeUpgrades.Added) > 0 { - config.Upgrades = append(config.Upgrades, *storeUpgrades) + par.Upgrades = append(par.Upgrades, *storeUpgrades) } } return nil diff --git a/x/upgrade/types/storeloader_test.go b/x/upgrade/types/storeloader_test.go index e35a03c8d708..8305337c47fd 100644 --- a/x/upgrade/types/storeloader_test.go +++ b/x/upgrade/types/storeloader_test.go @@ -28,7 +28,7 @@ func defaultLogger() log.Logger { } } -func initStore(t *testing.T, db dbm.DBConnection, config multi.StoreConfig, storeKey string, k, v []byte) { +func initStore(t *testing.T, db dbm.DBConnection, config multi.StoreParams, storeKey string, k, v []byte) { key := sdk.NewKVStoreKey(storeKey) rs, err := multi.NewStore(db, config) require.NoError(t, err) @@ -44,7 +44,7 @@ func initStore(t *testing.T, db dbm.DBConnection, config multi.StoreConfig, stor require.NoError(t, rs.Close()) } -func checkStore(t *testing.T, db dbm.DBConnection, config multi.StoreConfig, ver int64, storeKey string, k, v []byte) { +func checkStore(t *testing.T, db dbm.DBConnection, config multi.StoreParams, ver int64, storeKey string, k, v []byte) { key := sdk.NewKVStoreKey(storeKey) rs, err := multi.NewStore(db, config) require.NoError(t, err) @@ -112,8 +112,8 @@ func TestSetLoader(t *testing.T) { for name, tc := range cases { tc := tc t.Run(name, func(t *testing.T) { - origConfig := multi.DefaultStoreConfig() - loadConfig := multi.DefaultStoreConfig() + origConfig := multi.DefaultStoreParams() + loadConfig := multi.DefaultStoreParams() require.NoError(t, origConfig.RegisterSubstore(tc.origStoreKey, storetypes.StoreTypePersistent)) require.NoError(t, loadConfig.RegisterSubstore(tc.loadStoreKey, storetypes.StoreTypePersistent)) From 4e431ebedf7abdd1a4671a3e7817051c94663829 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Thu, 20 Jan 2022 20:16:43 -0600 Subject: [PATCH 10/78] store doc --- store/v2/multi/store.go | 14 ++++++++------ store/v2/smt/store.go | 4 ++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/store/v2/multi/store.go b/store/v2/multi/store.go index f4c5e300dff3..8f7414a0ae3c 100644 --- a/store/v2/multi/store.go +++ b/store/v2/multi/store.go @@ -34,6 +34,9 @@ var ( ) var ( + ErrVersionDoesNotExist = errors.New("version does not exist") + ErrMaximumHeight = errors.New("maximum block height reached") + // Root prefixes merkleRootKey = []byte{0} // Key for root hash of namespace tree schemaPrefix = []byte{1} // Prefix for store keys (namespaces) @@ -45,9 +48,6 @@ var ( indexPrefix = []byte{2} // Prefix for Store reverse index merkleNodePrefix = []byte{3} // Prefix for Merkle tree nodes merkleValuePrefix = []byte{4} // Prefix for Merkle value mappings - - ErrVersionDoesNotExist = errors.New("version does not exist") - ErrMaximumHeight = errors.New("maximum block height reached") ) func ErrStoreNotFound(skey string) error { @@ -63,11 +63,13 @@ type StoreParams struct { // The backing DB to use for the state commitment Merkle tree data. // If nil, Merkle data is stored in the state storage DB under a separate prefix. StateCommitmentDB dbm.DBConnection - + // Contains the store schema and methods to modify it SchemaBuilder + // Inter-block persistent cache to use. TODO: not implemented PersistentCache types.MultiStorePersistentCache - Upgrades []types.StoreUpgrades - + // Any pending upgrades to apply on loading. + Upgrades []types.StoreUpgrades + // Contains The trace context and listeners that can also be set from store methods. *traceListenMixin } diff --git a/store/v2/smt/store.go b/store/v2/smt/store.go index b63d0e65ecd5..f6ef494eb772 100644 --- a/store/v2/smt/store.go +++ b/store/v2/smt/store.go @@ -5,7 +5,7 @@ import ( "errors" dbm "github.com/cosmos/cosmos-sdk/db" - "github.com/cosmos/cosmos-sdk/store/types" + types "github.com/cosmos/cosmos-sdk/store/v2" "github.com/lazyledger/smt" tmcrypto "github.com/tendermint/tendermint/proto/tendermint/crypto" @@ -21,7 +21,7 @@ var ( errValueNil = errors.New("value is nil") ) -// Store Implements types.KVStore and CommitKVStore. +// Store Implements types.BasicKVStore. type Store struct { tree *smt.SparseMerkleTree } From 861b24ceec72ba6978bc0a1944d19b392a2ca7a6 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Thu, 20 Jan 2022 20:18:20 -0600 Subject: [PATCH 11/78] store rearrange --- store/v2/multi/cache_store.go | 7 +++++++ store/v2/multi/store.go | 21 --------------------- store/v2/multi/view_store.go | 14 ++++++++++++++ 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/store/v2/multi/cache_store.go b/store/v2/multi/cache_store.go index 6822382927bc..53baf2dcead5 100644 --- a/store/v2/multi/cache_store.go +++ b/store/v2/multi/cache_store.go @@ -5,6 +5,13 @@ import ( types "github.com/cosmos/cosmos-sdk/store/v2" ) +// Branched state +type cacheStore struct { + source types.BasicMultiStore + substores map[string]types.CacheKVStore + *traceListenMixin +} + func newCacheStore(bs types.BasicMultiStore) *cacheStore { return &cacheStore{ source: bs, diff --git a/store/v2/multi/store.go b/store/v2/multi/store.go index 8f7414a0ae3c..cfaf7e06792d 100644 --- a/store/v2/multi/store.go +++ b/store/v2/multi/store.go @@ -111,27 +111,6 @@ type substore struct { stateCommitmentStore *smt.Store } -// Branched state -type cacheStore struct { - source types.BasicMultiStore - substores map[string]types.CacheKVStore - *traceListenMixin -} - -// Read-only store for querying past versions -type viewStore struct { - stateView dbm.DBReader - stateCommitmentView dbm.DBReader - substoreCache map[string]*viewSubstore - schema StoreSchema -} - -type viewSubstore struct { - dataBucket dbm.DBReader - indexBucket dbm.DBReader - stateCommitmentStore *smt.Store -} - // Builder type used to create a valid schema with no prefix conflicts type SchemaBuilder struct { StoreSchema diff --git a/store/v2/multi/view_store.go b/store/v2/multi/view_store.go index 40155416f3fd..c51b1d065275 100644 --- a/store/v2/multi/view_store.go +++ b/store/v2/multi/view_store.go @@ -17,6 +17,20 @@ import ( var ErrReadOnly = errors.New("cannot modify read-only store") +// Read-only store for querying past versions +type viewStore struct { + stateView dbm.DBReader + stateCommitmentView dbm.DBReader + substoreCache map[string]*viewSubstore + schema StoreSchema +} + +type viewSubstore struct { + dataBucket dbm.DBReader + indexBucket dbm.DBReader + stateCommitmentStore *smt.Store +} + func (vs *viewStore) GetKVStore(skey types.StoreKey) types.KVStore { key := skey.Name() if _, has := vs.schema[key]; !has { From f45ce0f534c8ddbdcdc70002728ef8a4672b8b71 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Thu, 27 Jan 2022 22:59:01 +0800 Subject: [PATCH 12/78] godoc nits --- baseapp/baseapp_test.go | 2 +- store/v2/multi/store.go | 2 +- types/context.go | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index de070bbbbc1c..afb0e3892653 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -325,7 +325,7 @@ func TestLoadVersion(t *testing.T) { commitID2 := stypes.CommitID{Version: 2, Hash: res.Data} app.CloseStore() - // reload with LoadLatestVersion + // reload with latest version app = baseapp.NewBaseApp(name, logger, db, pruningOpt) app.SetStoreConstructor(baseapp.DefaultStoreConstructor) err = app.Init() diff --git a/store/v2/multi/store.go b/store/v2/multi/store.go index cfaf7e06792d..d9da3b5fe77a 100644 --- a/store/v2/multi/store.go +++ b/store/v2/multi/store.go @@ -643,7 +643,7 @@ func (rs *Store) GetVersion(version int64) (types.BasicMultiStore, error) { return rs.getView(version) } -// CacheMultiStore implements BasicMultiStore. +// CacheWrap implements BasicMultiStore. func (rs *Store) CacheWrap() types.CacheMultiStore { return newCacheStore(rs) } diff --git a/types/context.go b/types/context.go index 137804a32712..8e48418d2b24 100644 --- a/types/context.go +++ b/types/context.go @@ -259,7 +259,6 @@ func (c Context) TransientStore(key stypes.StoreKey) stypes.KVStore { // EventManager. The cached context is written to the context when writeCache // is called. func (c Context) CacheContext() (cc Context, writeCache func()) { - // TODO replace with constructor? cs := c.MultiStore().CacheWrap() cc = c.WithMultiStore(cs).WithEventManager(NewEventManager()) return cc, cs.Write From 79bea1dc86716c515be30516386187b27b07afe8 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Fri, 28 Jan 2022 14:08:20 +0800 Subject: [PATCH 13/78] rm StoreConstructor not needed after all --- baseapp/baseapp.go | 18 ++++-------- baseapp/baseapp_test.go | 61 +---------------------------------------- baseapp/options.go | 9 ------ 3 files changed, 6 insertions(+), 82 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 3f53d8f5ae56..955a45d22ec4 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -39,12 +39,11 @@ type ( StoreParams = multi.StoreParams // StoreOption provides a functional callback to modify StoreParams. // The callback is passed the loaded height as uint64. + // This can be used to control how we load the CommitMultiStore from disk. This is useful for + // state migration, when loading a datastore written with an older version of the software. + // In particular, if a module changed the substore key name (or removed a substore) between + // two versions of the software. StoreOption func(*StoreParams, uint64) error - // StoreConstructor defines a customizable function to control how we load the CommitMultiStore - // from disk. This is useful for state migration, when loading a datastore written with - // an older version of the software. In particular, if a module changed the substore key name - // (or removed a substore) between two versions of the software. - StoreConstructor func(dbm.DBConnection, StoreParams) (sdk.CommitMultiStore, error) // AppOption provides a configuration option for a BaseApp AppOption interface { @@ -68,7 +67,6 @@ type BaseApp struct { // nolint: maligned logger log.Logger name string // application name from abci.Info db dbm.DBConnection - storeCtor StoreConstructor storeOpts []StoreOption // options to configure root store store sdk.CommitMultiStore // Main (uncached) state queryRouter sdk.QueryRouter // router for redirecting query calls @@ -176,7 +174,6 @@ func NewBaseApp( logger: logger, name: name, db: db, - storeCtor: DefaultStoreConstructor, queryRouter: NewQueryRouter(), grpcQueryRouter: NewGRPCQueryRouter(), fauxMerkleMode: false, @@ -236,7 +233,7 @@ func (app *BaseApp) loadStore() error { for _, opt := range app.storeOpts { opt(&config, latest) } - app.store, err = app.storeCtor(app.db, config) + app.store, err = multi.NewStore(app.db, config) if err != nil { return fmt.Errorf("failed to load store: %w", err) } @@ -247,11 +244,6 @@ func (app *BaseApp) CloseStore() error { return app.store.Close() } -// DefaultStoreConstructor attempts to create a new store, but loads from existing data if present. -func DefaultStoreConstructor(db dbm.DBConnection, config StoreParams) (stypes.CommitMultiStore, error) { - return multi.NewStore(db, config) -} - // LastCommitID returns the last CommitID of the multistore. func (app *BaseApp) LastCommitID() stypes.CommitID { return app.store.LastCommitID() diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index afb0e3892653..1b44e5751ee1 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -327,16 +327,11 @@ func TestLoadVersion(t *testing.T) { // reload with latest version app = baseapp.NewBaseApp(name, logger, db, pruningOpt) - app.SetStoreConstructor(baseapp.DefaultStoreConstructor) err = app.Init() require.Nil(t, err) testLoadVersionHelper(t, app, int64(2), commitID2) } -var useDefaultConstructor = baseapp.AppOptionFunc(func(app *baseapp.BaseApp) { - app.SetStoreConstructor(baseapp.DefaultStoreConstructor) -}) - func initStore(t *testing.T, db dbm.DBConnection, storeKey string, k, v []byte) { key := sdk.NewKVStoreKey(storeKey) opts := multi.DefaultStoreParams() @@ -360,7 +355,7 @@ func checkStore(t *testing.T, db dbm.DBConnection, ver int64, storeKey string, k opts.Pruning = stypes.PruneNothing key := sdk.NewKVStoreKey(storeKey) require.NoError(t, opts.RegisterSubstore(key.Name(), stypes.StoreTypePersistent)) - rs, err := baseapp.DefaultStoreConstructor(db, opts) + rs, err := multi.NewStore(db, opts) require.NoError(t, err) require.Equal(t, ver, rs.LastCommitID().Version) @@ -371,60 +366,6 @@ func checkStore(t *testing.T, db dbm.DBConnection, ver int64, storeKey string, k require.NoError(t, rs.Close()) } -// Test that we can make commits and then reload old versions. -// Test that LoadLatestVersion actually does. -func TestSetConstructor(t *testing.T) { - cases := map[string]struct { - setConstructor baseapp.AppOption - origStoreKey string - loadStoreKey string - }{ - "don't set constructor": { - origStoreKey: "foo", - loadStoreKey: "foo", - }, - "default constructor": { - setConstructor: useDefaultConstructor, - origStoreKey: "foo", - loadStoreKey: "foo", - }, - } - - k := []byte("key") - v := []byte("value") - - for name, tc := range cases { - tc := tc - t.Run(name, func(t *testing.T) { - // prepare a db with some data - db := memdb.NewDB() - initStore(t, db, tc.origStoreKey, k, v) - - // load the app with the existing db - opts := []baseapp.AppOption{ - baseapp.SetPruning(stypes.PruneNothing), - baseapp.SetSubstores(sdk.NewKVStoreKey(tc.loadStoreKey)), - } - if tc.setConstructor != nil { - opts = append(opts, tc.setConstructor) - } - app := baseapp.NewBaseApp(t.Name(), defaultLogger(), db, opts...) - err := app.Init() - require.Nil(t, err) - - // "execute" one block - app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: 2}}) - res := app.Commit() - require.NotNil(t, res.Data) - require.NoError(t, app.CloseStore()) - - // check db is properly updated - checkStore(t, db, 2, tc.loadStoreKey, k, v) - checkStore(t, db, 2, tc.loadStoreKey, []byte("foo"), nil) - }) - } -} - func TestVersionSetterGetter(t *testing.T) { logger := defaultLogger() pruningOpt := baseapp.SetPruning(stypes.PruneDefault) diff --git a/baseapp/options.go b/baseapp/options.go index 0d5d698dac71..0823161b5e05 100644 --- a/baseapp/options.go +++ b/baseapp/options.go @@ -196,15 +196,6 @@ func (app *BaseApp) SetCommitMultiStoreTracer(w io.Writer) { app.storeOpts = append(app.storeOpts, opt) } -// SetStoreLoader allows us to customize the root store initialization. -func (app *BaseApp) SetStoreConstructor(ctor StoreConstructor) { - if app.sealed { - panic("SetStoreConstructor() on sealed BaseApp") - } - - app.storeCtor = ctor -} - // SetSnapshotStore sets the snapshot store. func (app *BaseApp) SetSnapshotStore(snapshotStore *snapshots.Store) { if app.sealed { From e91211192a92224c370e9a0758e51e55f284b05c Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Thu, 27 Jan 2022 22:58:31 +0800 Subject: [PATCH 14/78] compatibility wrapper for original MultiStore interface --- store/v2/multi/compat.go | 149 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 store/v2/multi/compat.go diff --git a/store/v2/multi/compat.go b/store/v2/multi/compat.go new file mode 100644 index 000000000000..ab809c06ce98 --- /dev/null +++ b/store/v2/multi/compat.go @@ -0,0 +1,149 @@ +package multi + +import ( + "fmt" + "io" + + dbm "github.com/tendermint/tm-db" + + v1 "github.com/cosmos/cosmos-sdk/store/types" + v2 "github.com/cosmos/cosmos-sdk/store/v2" +) + +var ( + _ v1.CommitMultiStore = (*compatStore)(nil) + _ v1.CacheMultiStore = (*compatCacheStore)(nil) + _ v1.Queryable = (*compatStore)(nil) +) + +type compatStore struct { + *Store +} + +type compatCacheStore struct { + *cacheStore +} + +func WrapStoreAsV1CommitMultiStore(s v2.CommitMultiStore) (v1.CommitMultiStore, error) { + impl, ok := s.(*Store) + if !ok { + return nil, fmt.Errorf("cannot wrap as v1.CommitMultiStore: %T", s) + } + return &compatStore{impl}, nil +} + +func WrapCacheStoreAsV1CacheMultiStore(cs v2.CacheMultiStore) (v1.CacheMultiStore, error) { + impl, ok := cs.(*cacheStore) + if !ok { + return nil, fmt.Errorf("cannot wrap as v1.CacheMultiStore: %T", cs) + } + return &compatCacheStore{impl}, nil +} + +func (st *compatStore) GetStoreType() v1.StoreType { + return v1.StoreTypeMulti +} + +func (st *compatStore) CacheWrap() v1.CacheWrap { + return st.CacheMultiStore() +} + +// TODO: v1 MultiStore ignores args, do we as well? +func (st *compatStore) CacheWrapWithTrace(io.Writer, v1.TraceContext) v1.CacheWrap { + return st.CacheWrap() +} +func (st *compatStore) CacheWrapWithListeners(v1.StoreKey, []v1.WriteListener) v1.CacheWrap { + return st.CacheWrap() +} + +func (st *compatStore) CacheMultiStore() v1.CacheMultiStore { + return &compatCacheStore{newCacheStore(st.Store)} +} +func (st *compatStore) CacheMultiStoreWithVersion(version int64) (v1.CacheMultiStore, error) { + view, err := st.GetVersion(version) + if err != nil { + return nil, err + } + return &compatCacheStore{newCacheStore(view)}, nil +} + +func (st *compatStore) GetStore(k v1.StoreKey) v1.Store { + return st.GetKVStore(k) +} + +func (st *compatStore) GetCommitStore(key v1.StoreKey) v1.CommitStore { + panic("unsupported: GetCommitStore") +} +func (st *compatStore) GetCommitKVStore(key v1.StoreKey) v1.CommitKVStore { + panic("unsupported: GetCommitKVStore") +} + +func (st *compatStore) SetTracer(w io.Writer) v1.MultiStore { + st.Store.SetTracer(w) + return st +} +func (st *compatStore) SetTracingContext(tc v1.TraceContext) v1.MultiStore { + st.Store.SetTracingContext(tc) + return st +} + +func (st *compatStore) MountStoreWithDB(key v1.StoreKey, typ v1.StoreType, db dbm.DB) { + panic("unsupported: MountStoreWithDB") +} + +func (st *compatStore) LoadLatestVersion() error { + return nil // this store is always at the latest version +} +func (st *compatStore) LoadLatestVersionAndUpgrade(upgrades *v1.StoreUpgrades) error { + panic("unsupported: LoadLatestVersionAndUpgrade") +} +func (st *compatStore) LoadVersionAndUpgrade(ver int64, upgrades *v1.StoreUpgrades) error { + panic("unsupported: LoadLatestVersionAndUpgrade") +} + +func (st *compatStore) LoadVersion(ver int64) error { + // TODO + // cache a viewStore representing "current" version? + return nil +} + +func (st *compatStore) SetInterBlockCache(v1.MultiStorePersistentCache) { + panic("unsupported: SetInterBlockCache") +} +func (st *compatStore) SetInitialVersion(version int64) error { + if version < 0 { + return fmt.Errorf("invalid version") + } + return st.Store.SetInitialVersion(uint64(version)) +} +func (st *compatStore) SetIAVLCacheSize(size int) { + panic("unsupported: SetIAVLCacheSize") +} + +func (cs *compatCacheStore) GetStoreType() v1.StoreType { return v1.StoreTypeMulti } +func (cs *compatCacheStore) CacheWrap() v1.CacheWrap { + return cs.CacheMultiStore() +} +func (cs *compatCacheStore) CacheWrapWithTrace(w io.Writer, tc v1.TraceContext) v1.CacheWrap { + return cs.CacheWrap() +} +func (cs *compatCacheStore) CacheWrapWithListeners(storeKey v1.StoreKey, listeners []v1.WriteListener) v1.CacheWrap { + return cs.CacheWrap() +} +func (cs *compatCacheStore) CacheMultiStore() v1.CacheMultiStore { + return &compatCacheStore{newCacheStore(cs.cacheStore)} +} +func (cs *compatCacheStore) CacheMultiStoreWithVersion(int64) (v1.CacheMultiStore, error) { + return nil, fmt.Errorf("cannot branch cached multi-store with a version") +} + +func (cs *compatCacheStore) GetStore(k v1.StoreKey) v1.Store { return cs.GetKVStore(k) } + +func (cs *compatCacheStore) SetTracer(w io.Writer) v1.MultiStore { + cs.cacheStore.SetTracer(w) + return cs +} +func (cs *compatCacheStore) SetTracingContext(tc v1.TraceContext) v1.MultiStore { + cs.cacheStore.SetTracingContext(tc) + return cs +} From 704570b3d6b5316f26a3c0f74d7fe7ba11970b0b Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Mon, 7 Feb 2022 13:10:10 +0800 Subject: [PATCH 15/78] rename BasicMultiStore -> MultiStore --- docs/core/store.md | 6 +++--- server/mock/store.go | 2 +- store/v2/multi/cache_store.go | 10 +++++----- store/v2/multi/doc.go | 2 +- store/v2/multi/store.go | 8 ++++---- store/v2/types.go | 12 +++++------- types/store.go | 2 +- 7 files changed, 20 insertions(+), 22 deletions(-) diff --git a/docs/core/store.md b/docs/core/store.md index 11d16f25b25b..30903e45059b 100644 --- a/docs/core/store.md +++ b/docs/core/store.md @@ -254,13 +254,13 @@ An interface providing only the basic CRUD functionality (`Get`, `Set`, `Has`, a ### `CommitMultiStore` This is the main interface for persisent application state, analogous to the original `CommitMultiStore`. - * Past version views are accessed with `GetVersion`, which returns a `BasicMultiStore`. + * Past version views are accessed with `GetVersion`, which returns a `MultiStore`. * Substores are accessed with `GetKVStore`. Trying to get a substore that was not defined at initialization will cause a panic. * `Close` must be called to release the DB resources being used by the store. -### `BasicMultiStore` +### `MultiStore` -A minimal interface that only allows accessing substores. Note: substores returned by `BasicMultiStore.GetKVStore` are read-only and will panic on `Set` or `Delete` calls. +A minimal interface that only allows accessing substores. Note: substores returned by `MultiStore.GetKVStore` are read-only and will panic on `Set` or `Delete` calls. ### Implementation (`root.Store`) diff --git a/server/mock/store.go b/server/mock/store.go index 10afda1fc634..aa17399cd885 100644 --- a/server/mock/store.go +++ b/server/mock/store.go @@ -87,7 +87,7 @@ func (ms multiStore) Restore( panic("not implemented") } -func (ms multiStore) GetVersion(int64) (storetypes.BasicMultiStore, error) { +func (ms multiStore) GetVersion(int64) (storetypes.MultiStore, error) { panic("not implemented") } diff --git a/store/v2/multi/cache_store.go b/store/v2/multi/cache_store.go index 53baf2dcead5..a1c281a59e8c 100644 --- a/store/v2/multi/cache_store.go +++ b/store/v2/multi/cache_store.go @@ -7,12 +7,12 @@ import ( // Branched state type cacheStore struct { - source types.BasicMultiStore + source types.MultiStore substores map[string]types.CacheKVStore *traceListenMixin } -func newCacheStore(bs types.BasicMultiStore) *cacheStore { +func newCacheStore(bs types.MultiStore) *cacheStore { return &cacheStore{ source: bs, substores: map[string]types.CacheKVStore{}, @@ -20,7 +20,7 @@ func newCacheStore(bs types.BasicMultiStore) *cacheStore { } } -// GetKVStore implements BasicMultiStore. +// GetKVStore implements MultiStore. func (cs *cacheStore) GetKVStore(skey types.StoreKey) types.KVStore { key := skey.Name() sub, has := cs.substores[key] @@ -40,7 +40,7 @@ func (cs *cacheStore) Write() { } } -// CacheMultiStore implements BasicMultiStore. +// CacheMultiStore implements MultiStore. // This recursively wraps the CacheMultiStore in another cache store. func (cs *cacheStore) CacheWrap() types.CacheMultiStore { return newCacheStore(cs) @@ -54,7 +54,7 @@ type noopCacheStore struct { func (noopCacheStore) Write() {} // pretend basic store is cache store -func BasicAsCacheStore(bs types.BasicMultiStore) types.CacheMultiStore { +func BasicAsCacheStore(bs types.MultiStore) types.CacheMultiStore { return noopCacheStore{newCacheStore(bs)} } diff --git a/store/v2/multi/doc.go b/store/v2/multi/doc.go index a977d4d5bd75..02b817d48b40 100644 --- a/store/v2/multi/doc.go +++ b/store/v2/multi/doc.go @@ -1,5 +1,5 @@ // This package provides concrete implementations of the store/v2 "MultiStore" types, including -// CommitMultiStore, CacheMultiStore, and BasicMultiStore (as read-only stores at past versions). +// CommitMultiStore, CacheMultiStore, and MultiStore (as read-only stores at past versions). // // Substores are declared as part of a schema within StoreOptions. // The schema cannot be changed once a CommitMultiStore is initialized, and changes to the schema must be done diff --git a/store/v2/multi/store.go b/store/v2/multi/store.go index d9da3b5fe77a..4c8219f4b076 100644 --- a/store/v2/multi/store.go +++ b/store/v2/multi/store.go @@ -29,7 +29,7 @@ var ( _ types.Queryable = (*Store)(nil) _ types.CommitMultiStore = (*Store)(nil) _ types.CacheMultiStore = (*cacheStore)(nil) - _ types.BasicMultiStore = (*viewStore)(nil) + _ types.MultiStore = (*viewStore)(nil) _ types.KVStore = (*substore)(nil) ) @@ -405,7 +405,7 @@ func substorePrefix(key string) []byte { return append(contentPrefix, key...) } -// GetKVStore implements BasicMultiStore. +// GetKVStore implements MultiStore. func (rs *Store) GetKVStore(skey types.StoreKey) types.KVStore { key := skey.Name() var parent types.KVStore @@ -639,11 +639,11 @@ func (rs *Store) SetInitialVersion(version uint64) error { } // GetVersion implements CommitMultiStore. -func (rs *Store) GetVersion(version int64) (types.BasicMultiStore, error) { +func (rs *Store) GetVersion(version int64) (types.MultiStore, error) { return rs.getView(version) } -// CacheWrap implements BasicMultiStore. +// CacheWrap implements MultiStore. func (rs *Store) CacheWrap() types.CacheMultiStore { return newCacheStore(rs) } diff --git a/store/v2/types.go b/store/v2/types.go index 9a18ab42cef3..12a453ab511f 100644 --- a/store/v2/types.go +++ b/store/v2/types.go @@ -61,8 +61,8 @@ var ( AssertValidValue = v1.AssertValidValue ) -// BasicMultiStore defines a minimal interface for accessing root state. -type BasicMultiStore interface { +// MultiStore defines a minimal interface for accessing root state. +type MultiStore interface { // Returns a KVStore which has access only to the namespace of the StoreKey. // Panics if the key is not found in the schema. GetKVStore(StoreKey) KVStore @@ -70,8 +70,6 @@ type BasicMultiStore interface { CacheWrap() CacheMultiStore } -type MultiStore = BasicMultiStore - // mixin interface for trace and listen methods type rootStoreTraceListen interface { TracingEnabled() bool @@ -84,14 +82,14 @@ type rootStoreTraceListen interface { // CommitMultiStore defines a complete interface for persistent root state, including // (read-only) access to past versions, pruning, trace/listen, and state snapshots. type CommitMultiStore interface { - BasicMultiStore + MultiStore rootStoreTraceListen Committer snapshottypes.Snapshotter // Gets a read-only view of the store at a specific version. // Returns an error if the version is not found. - GetVersion(int64) (BasicMultiStore, error) + GetVersion(int64) (MultiStore, error) // Closes the store and all backing transactions. Close() error // Defines the minimum version number that can be saved by this store. @@ -100,7 +98,7 @@ type CommitMultiStore interface { // CacheMultiStore defines a branch of the root state which can be written back to the source store. type CacheMultiStore interface { - BasicMultiStore + MultiStore rootStoreTraceListen // Returns a branched whose modifications are later merged back in. diff --git a/types/store.go b/types/store.go index bd0fc666d94e..d6efede042d5 100644 --- a/types/store.go +++ b/types/store.go @@ -15,7 +15,7 @@ type ( Queryable = types2.Queryable KVStore = types2.KVStore Iterator = types2.Iterator - BasicMultiStore = types2.BasicMultiStore + MultiStore = types2.MultiStore CommitMultiStore = types2.CommitMultiStore CacheMultiStore = types2.CacheMultiStore MultiStorePersistentCache = types2.MultiStorePersistentCache From b36c8f511433dd1b4c0aba95fdc06919959683eb Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Mon, 7 Feb 2022 13:23:21 +0800 Subject: [PATCH 16/78] patch multistore to be consistent with v1, upgrades shouldn't be a slice fix query and tests --- store/v2/multi/store.go | 19 ++---- store/v2/multi/store_test.go | 123 ++++++++++++++++++++++------------- store/v2/types.go | 4 +- 3 files changed, 85 insertions(+), 61 deletions(-) diff --git a/store/v2/multi/store.go b/store/v2/multi/store.go index 4c8219f4b076..0d70dc3d6bc7 100644 --- a/store/v2/multi/store.go +++ b/store/v2/multi/store.go @@ -60,7 +60,7 @@ type StoreParams struct { Pruning types.PruningOptions // The minimum allowed version number. InitialVersion uint64 - // The backing DB to use for the state commitment Merkle tree data. + // The optional backing DB to use for the state commitment Merkle tree data. // If nil, Merkle data is stored in the state storage DB under a separate prefix. StateCommitmentDB dbm.DBConnection // Contains the store schema and methods to modify it @@ -68,7 +68,7 @@ type StoreParams struct { // Inter-block persistent cache to use. TODO: not implemented PersistentCache types.MultiStorePersistentCache // Any pending upgrades to apply on loading. - Upgrades []types.StoreUpgrades + Upgrades *types.StoreUpgrades // Contains The trace context and listeners that can also be set from store methods. *traceListenMixin } @@ -96,7 +96,7 @@ type Store struct { // Copied from StoreParams Pruning types.PruningOptions - InitialVersion uint64 // if + InitialVersion uint64 *traceListenMixin PersistentCache types.MultiStorePersistentCache @@ -308,8 +308,8 @@ func NewStore(db dbm.DBConnection, opts StoreParams) (ret *Store, err error) { writeSchema(reg) } else { // Apply migrations to the schema - for _, upgrades := range opts.Upgrades { - err = reg.MigrateSchema(upgrades) + if opts.Upgrades != nil { + err = reg.MigrateSchema(*opts.Upgrades) if err != nil { return } @@ -318,13 +318,11 @@ func NewStore(db dbm.DBConnection, opts StoreParams) (ret *Store, err error) { err = errors.New("loaded schema does not match configured schema") return } - for _, upgrades := range opts.Upgrades { - err = migrateData(ret, upgrades) + if opts.Upgrades != nil { + err = migrateData(ret, *opts.Upgrades) if err != nil { return } - } - if len(opts.Upgrades) != 0 { writeSchema(reg) } } @@ -709,9 +707,6 @@ func (rs *Store) Query(req abci.RequestQuery) (res abci.ResponseQuery) { return sdkerrors.QueryResult(sdkerrors.Wrapf(err, "failed to access height"), false) } - if _, has := rs.schema[storeName]; !has { - return sdkerrors.QueryResult(sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "no such store: %s", storeName), false) - } substore, err := view.getSubstore(storeName) if err != nil { return sdkerrors.QueryResult(sdkerrors.Wrapf(err, "failed to access store: %s", storeName), false) diff --git a/store/v2/multi/store_test.go b/store/v2/multi/store_test.go index 906f8c87bc41..112bf55d4341 100644 --- a/store/v2/multi/store_test.go +++ b/store/v2/multi/store_test.go @@ -500,12 +500,11 @@ func TestQuery(t *testing.T) { require.NoError(t, err) cid := store.Commit() ver := cid.Version - query := abci.RequestQuery{Path: queryPath(skey_1, "/key"), Data: k1, Height: ver} - querySub := abci.RequestQuery{Path: queryPath(skey_1, "/subspace"), Data: ksub, Height: ver} + querySubspace := abci.RequestQuery{Path: queryPath(skey_1, "/subspace"), Data: ksub, Height: ver} queryHeight0 := abci.RequestQuery{Path: queryPath(skey_1, "/key"), Data: k1} // query subspace before anything set - qres := store.Query(querySub) + qres := store.Query(querySubspace) require.True(t, qres.IsOK(), qres.Log) require.Equal(t, valExpSubEmpty, qres.Value) @@ -516,25 +515,27 @@ func TestQuery(t *testing.T) { sub.Set(k2, v2) t.Run("basic queries", func(t *testing.T) { + query1 := abci.RequestQuery{Path: queryPath(skey_1, "/key"), Data: k1, Height: ver} + // set data without commit, doesn't show up - qres = store.Query(query) + qres = store.Query(query1) require.True(t, qres.IsOK(), qres.Log) require.Nil(t, qres.Value) // commit it, but still don't see on old version cid = store.Commit() - qres = store.Query(query) + qres = store.Query(query1) require.True(t, qres.IsOK(), qres.Log) require.Nil(t, qres.Value) // but yes on the new version - query.Height = cid.Version - qres = store.Query(query) + query1.Height = cid.Version + qres = store.Query(query1) require.True(t, qres.IsOK(), qres.Log) require.Equal(t, v1, qres.Value) // and for the subspace - querySub.Height = cid.Version - qres = store.Query(querySub) + querySubspace.Height = cid.Version + qres = store.Query(querySubspace) require.True(t, qres.IsOK(), qres.Log) require.Equal(t, valExpSub1, qres.Value) @@ -543,13 +544,13 @@ func TestQuery(t *testing.T) { cid = store.Commit() // query will return old values, as height is fixed - qres = store.Query(query) + qres = store.Query(query1) require.True(t, qres.IsOK(), qres.Log) require.Equal(t, v1, qres.Value) // update to latest height in the query and we are happy - query.Height = cid.Version - qres = store.Query(query) + query1.Height = cid.Version + qres = store.Query(query1) require.True(t, qres.IsOK(), qres.Log) require.Equal(t, v3, qres.Value) // try other key @@ -558,43 +559,71 @@ func TestQuery(t *testing.T) { require.True(t, qres.IsOK(), qres.Log) require.Equal(t, v2, qres.Value) // and for the subspace - querySub.Height = cid.Version - qres = store.Query(querySub) + querySubspace.Height = cid.Version + qres = store.Query(querySubspace) require.True(t, qres.IsOK(), qres.Log) require.Equal(t, valExpSub2, qres.Value) + }) + + t.Run("different versions", func(t *testing.T) { + stateDB := memdb.NewDB() // default (height 0) will show latest-1 qres = store.Query(queryHeight0) require.True(t, qres.IsOK(), qres.Log) require.Equal(t, v1, qres.Value) - }) - // querying an empty store will fail - store2, err := NewStore(memdb.NewDB(), simpleStoreParams(t)) - require.NoError(t, err) - qres = store2.Query(queryHeight0) - require.True(t, qres.IsErr()) + // querying an empty store will fail + store, err = NewStore(stateDB, simpleStoreParams(t)) + require.NoError(t, err) + qres = store.Query(queryHeight0) + require.True(t, qres.IsErr()) - // default shows latest, if latest-1 does not exist - store2.GetKVStore(skey_1).Set(k1, v1) - store2.Commit() - qres = store2.Query(queryHeight0) - require.True(t, qres.IsOK(), qres.Log) - require.Equal(t, v1, qres.Value) - store2.Close() + // default (height=0) shows latest, if latest-1 does not exist + store.GetKVStore(skey_1).Set(k1, v1) + cid = store.Commit() + qres = store.Query(queryHeight0) + require.True(t, qres.IsOK(), qres.Log) + require.Equal(t, v1, qres.Value) + require.NoError(t, store.Close()) + + // querying past version succeeds after rename + opts := DefaultStoreParams() + require.NoError(t, opts.RegisterSubstore(skey_2.Name(), types.StoreTypePersistent)) + opts.Upgrades = &types.StoreUpgrades{ + Renamed: []types.StoreRename{types.StoreRename{skey_1.Name(), skey_2.Name()}}, + } + store, err = NewStore(stateDB, opts) + require.NoError(t, err) + store.Commit() + query := abci.RequestQuery{Path: queryPath(skey_1, "/key"), Data: k1, Height: cid.Version} + qres = store.Query(query) + require.True(t, qres.IsOK(), qres.Log) + require.NoError(t, store.Close()) + }) t.Run("failed queries", func(t *testing.T) { + stateDB := memdb.NewDB() + + store, err = NewStore(stateDB, simpleStoreParams(t)) + require.NoError(t, err) + store.GetKVStore(skey_1).Set(k1, v1) + store.Commit() + // artificial error cases for coverage (should never happen with prescribed usage) // ensure that height overflow triggers an error require.NoError(t, err) - store2.stateDB = dbVersionsIs{store2.stateDB, dbm.NewVersionManager([]uint64{uint64(math.MaxInt64) + 1})} - qres = store2.Query(queryHeight0) + store.stateDB = dbVersionsIs{stateDB, dbm.NewVersionManager([]uint64{uint64(math.MaxInt64) + 1})} + qres = store.Query(queryHeight0) require.True(t, qres.IsErr()) // failure to access versions triggers an error - store2.stateDB = dbVersionsFails{store.stateDB} - qres = store2.Query(queryHeight0) + store.stateDB = dbVersionsFails{stateDB} + qres = store.Query(queryHeight0) require.True(t, qres.IsErr()) - store2.Close() + require.NoError(t, store.Close()) + + store, err = NewStore(stateDB, simpleStoreParams(t)) + require.NoError(t, err) // query with a nil or empty key fails badquery := abci.RequestQuery{Path: queryPath(skey_1, "/key"), Data: []byte{}} @@ -604,7 +633,11 @@ func TestQuery(t *testing.T) { qres = store.Query(badquery) require.True(t, qres.IsErr()) // querying an invalid height will fail - badquery = abci.RequestQuery{Path: queryPath(skey_1, "/key"), Data: k1, Height: store.LastCommitID().Version + 1} + badquery = abci.RequestQuery{ + Path: queryPath(skey_1, "/key"), + Data: k1, + Height: store.LastCommitID().Version + 1, + } qres = store.Query(badquery) require.True(t, qres.IsErr()) // or an invalid path @@ -623,7 +656,7 @@ func TestQuery(t *testing.T) { require.NotNil(t, qres.ProofOps) } testProve() - store.Close() + require.NoError(t, store.Close()) opts := simpleStoreParams(t) opts.StateCommitmentDB = memdb.NewDB() @@ -632,7 +665,7 @@ func TestQuery(t *testing.T) { store.GetKVStore(skey_1).Set(k1, v1) store.Commit() testProve() - store.Close() + require.NoError(t, store.Close()) }) } @@ -744,24 +777,22 @@ func TestMultiStoreMigration(t *testing.T) { t.Run("basic migration", func(t *testing.T) { // now, let's load with upgrades... - opts.Upgrades = []types.StoreUpgrades{ - types.StoreUpgrades{ - Added: []string{skey_4.Name()}, - Renamed: []types.StoreRename{{ - OldKey: skey_2.Name(), - NewKey: skey_2b.Name(), - }}, - Deleted: []string{skey_3.Name()}, - }, + opts.Upgrades = &types.StoreUpgrades{ + Added: []string{skey_4.Name()}, + Renamed: []types.StoreRename{{ + OldKey: skey_2.Name(), + NewKey: skey_2b.Name(), + }}, + Deleted: []string{skey_3.Name()}, } // store must be loaded with post-migration schema, so this fails store, err = NewStore(db, opts) require.Error(t, err) - opts.MigrateSchema(opts.Upgrades[0]) + opts.MigrateSchema(*opts.Upgrades) store, err = NewStore(db, opts) - // s1 was not changed + // store1 was not changed s1 = store.GetKVStore(skey_1) require.NotNil(t, s1) require.Equal(t, v1, s1.Get(k1)) diff --git a/store/v2/types.go b/store/v2/types.go index 12a453ab511f..1878ef0d585a 100644 --- a/store/v2/types.go +++ b/store/v2/types.go @@ -66,7 +66,7 @@ type MultiStore interface { // Returns a KVStore which has access only to the namespace of the StoreKey. // Panics if the key is not found in the schema. GetKVStore(StoreKey) KVStore - // Returns a branched whose modifications are later merged back in. + // Returns a branched store whose modifications are later merged back in. CacheWrap() CacheMultiStore } @@ -101,8 +101,6 @@ type CacheMultiStore interface { MultiStore rootStoreTraceListen - // Returns a branched whose modifications are later merged back in. - CacheWrap() CacheMultiStore // Write all cached changes back to the source store. Note: this overwrites any intervening changes. Write() } From d035fb844d21e452acc69c1518a508cfba7c2299 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Mon, 7 Feb 2022 14:16:52 +0800 Subject: [PATCH 17/78] patch compat store --- store/v2/multi/compat.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/store/v2/multi/compat.go b/store/v2/multi/compat.go index ab809c06ce98..4a8a2ddf9aa1 100644 --- a/store/v2/multi/compat.go +++ b/store/v2/multi/compat.go @@ -4,7 +4,7 @@ import ( "fmt" "io" - dbm "github.com/tendermint/tm-db" + tmdb "github.com/tendermint/tm-db" v1 "github.com/cosmos/cosmos-sdk/store/types" v2 "github.com/cosmos/cosmos-sdk/store/v2" @@ -12,8 +12,8 @@ import ( var ( _ v1.CommitMultiStore = (*compatStore)(nil) - _ v1.CacheMultiStore = (*compatCacheStore)(nil) _ v1.Queryable = (*compatStore)(nil) + _ v1.CacheMultiStore = (*compatCacheStore)(nil) ) type compatStore struct { @@ -40,6 +40,8 @@ func WrapCacheStoreAsV1CacheMultiStore(cs v2.CacheMultiStore) (v1.CacheMultiStor return &compatCacheStore{impl}, nil } +// commit store + func (st *compatStore) GetStoreType() v1.StoreType { return v1.StoreTypeMulti } @@ -87,7 +89,7 @@ func (st *compatStore) SetTracingContext(tc v1.TraceContext) v1.MultiStore { return st } -func (st *compatStore) MountStoreWithDB(key v1.StoreKey, typ v1.StoreType, db dbm.DB) { +func (st *compatStore) MountStoreWithDB(key v1.StoreKey, typ v1.StoreType, db tmdb.DB) { panic("unsupported: MountStoreWithDB") } @@ -104,7 +106,7 @@ func (st *compatStore) LoadVersionAndUpgrade(ver int64, upgrades *v1.StoreUpgrad func (st *compatStore) LoadVersion(ver int64) error { // TODO // cache a viewStore representing "current" version? - return nil + panic("unsupported: LoadVersion") } func (st *compatStore) SetInterBlockCache(v1.MultiStorePersistentCache) { @@ -120,6 +122,8 @@ func (st *compatStore) SetIAVLCacheSize(size int) { panic("unsupported: SetIAVLCacheSize") } +// cache store + func (cs *compatCacheStore) GetStoreType() v1.StoreType { return v1.StoreTypeMulti } func (cs *compatCacheStore) CacheWrap() v1.CacheWrap { return cs.CacheMultiStore() From bfa7b88f1ad038da8bb90c67a4a0a2ae784431d0 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Mon, 14 Feb 2022 20:54:09 +0800 Subject: [PATCH 18/78] multi store- use StoreKey instances, not strings --- baseapp/baseapp_test.go | 4 +- baseapp/options.go | 2 +- simapp/app.go | 6 +-- store/v2/multi/store.go | 101 ++++++++++++++++++++++------------- store/v2/multi/store_test.go | 59 ++++++++++---------- testutil/context.go | 4 +- 6 files changed, 105 insertions(+), 71 deletions(-) diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 1b44e5751ee1..ad57a1886e26 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -336,7 +336,7 @@ func initStore(t *testing.T, db dbm.DBConnection, storeKey string, k, v []byte) key := sdk.NewKVStoreKey(storeKey) opts := multi.DefaultStoreParams() opts.Pruning = stypes.PruneNothing - require.NoError(t, opts.RegisterSubstore(key.Name(), stypes.StoreTypePersistent)) + require.NoError(t, opts.RegisterSubstore(key, stypes.StoreTypePersistent)) rs, err := multi.NewStore(db, opts) require.NoError(t, err) require.Equal(t, int64(0), rs.LastCommitID().Version) @@ -354,7 +354,7 @@ func checkStore(t *testing.T, db dbm.DBConnection, ver int64, storeKey string, k opts := multi.DefaultStoreParams() opts.Pruning = stypes.PruneNothing key := sdk.NewKVStoreKey(storeKey) - require.NoError(t, opts.RegisterSubstore(key.Name(), stypes.StoreTypePersistent)) + require.NoError(t, opts.RegisterSubstore(key, stypes.StoreTypePersistent)) rs, err := multi.NewStore(db, opts) require.NoError(t, err) require.Equal(t, ver, rs.LastCommitID().Version) diff --git a/baseapp/options.go b/baseapp/options.go index 0823161b5e05..6f92c164eacc 100644 --- a/baseapp/options.go +++ b/baseapp/options.go @@ -92,7 +92,7 @@ func SetSubstores(keys ...storetypes.StoreKey) StoreOption { if err != nil { return err } - if err = config.RegisterSubstore(key.Name(), typ); err != nil { + if err = config.RegisterSubstore(key, typ); err != nil { return err } } diff --git a/simapp/app.go b/simapp/app.go index 9bd97b351770..b4e929666bbc 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -237,7 +237,7 @@ func NewSimApp( if err != nil { return err } - if err = config.RegisterSubstore(key.Name(), typ); err != nil { + if err = config.RegisterSubstore(key, typ); err != nil { return err } } @@ -246,7 +246,7 @@ func NewSimApp( if err != nil { return err } - if err = config.RegisterSubstore(key.Name(), typ); err != nil { + if err = config.RegisterSubstore(key, typ); err != nil { return err } } @@ -255,7 +255,7 @@ func NewSimApp( if err != nil { return err } - if err = config.RegisterSubstore(key.Name(), typ); err != nil { + if err = config.RegisterSubstore(key, typ); err != nil { return err } } diff --git a/store/v2/multi/store.go b/store/v2/multi/store.go index 0d70dc3d6bc7..6421329eca7f 100644 --- a/store/v2/multi/store.go +++ b/store/v2/multi/store.go @@ -50,8 +50,8 @@ var ( merkleValuePrefix = []byte{4} // Prefix for Merkle value mappings ) -func ErrStoreNotFound(skey string) error { - return fmt.Errorf("store does not exist for key: %s", skey) +func ErrStoreNotFound(key string) error { + return fmt.Errorf("store does not exist for key: %s", key) } // StoreParams is used to define a schema and other options and pass them to the MultiStore constructor. @@ -65,6 +65,7 @@ type StoreParams struct { StateCommitmentDB dbm.DBConnection // Contains the store schema and methods to modify it SchemaBuilder + storeKeys // Inter-block persistent cache to use. TODO: not implemented PersistentCache types.MultiStorePersistentCache // Any pending upgrades to apply on loading. @@ -75,6 +76,10 @@ type StoreParams struct { // StoreSchema defineds a mapping of substore keys to store types type StoreSchema map[string]types.StoreType +type StoreKeySchema map[types.StoreKey]types.StoreType + +// storeKeys maps key names to StoreKey instances +type storeKeys map[string]types.StoreKey // Store is the main persistent store type implementing CommitMultiStore. // Substores consist of an SMT-based state commitment store and state storage. @@ -89,10 +94,11 @@ type Store struct { StateCommitmentDB dbm.DBConnection stateCommitmentTxn dbm.DBReadWriter - schema StoreSchema - mem *mem.Store - tran *transient.Store - mtx sync.RWMutex + schema StoreKeySchema + + mem *mem.Store + tran *transient.Store + mtx sync.RWMutex // Copied from StoreParams Pruning types.PruningOptions @@ -119,27 +125,47 @@ type SchemaBuilder struct { // Mixin type that to compose trace & listen state into each root store variant type type traceListenMixin struct { - listeners map[string][]types.WriteListener + listeners map[types.StoreKey][]types.WriteListener TraceWriter io.Writer TraceContext types.TraceContext } func newTraceListenMixin() *traceListenMixin { - return &traceListenMixin{listeners: map[string][]types.WriteListener{}} + return &traceListenMixin{listeners: map[types.StoreKey][]types.WriteListener{}} +} + +func newSchemaBuilder() SchemaBuilder { + return SchemaBuilder{StoreSchema: StoreSchema{}} } // DefaultStoreParams returns a MultiStore config with an empty schema, a single backing DB, // pruning with PruneDefault, no listeners and no tracer. func DefaultStoreParams() StoreParams { return StoreParams{ - Pruning: types.PruneDefault, - SchemaBuilder: SchemaBuilder{ - StoreSchema: StoreSchema{}, - }, + Pruning: types.PruneDefault, + SchemaBuilder: newSchemaBuilder(), + storeKeys: storeKeys{}, traceListenMixin: newTraceListenMixin(), } } +// func (pr *SchemaBuilder) registerName(key string, typ types.StoreType) error { +func (par *StoreParams) RegisterSubstore(skey types.StoreKey, typ types.StoreType) error { + if err := par.registerName(skey.Name(), typ); err != nil { + return err + } + par.storeKeys[skey.Name()] = skey + return nil +} + +func (par *StoreParams) storeKey(key string) (types.StoreKey, error) { + skey, ok := par.storeKeys[key] + if !ok { + return nil, fmt.Errorf("StoreKey instance not mapped: %s", key) + } + return skey, nil +} + // Returns true for valid store types for a MultiStore schema func validSubStoreType(sst types.StoreType) bool { switch sst { @@ -173,7 +199,7 @@ func (this StoreSchema) equal(that StoreSchema) bool { // Parses a schema from the DB func readSavedSchema(bucket dbm.DBReader) (*SchemaBuilder, error) { - ret := SchemaBuilder{StoreSchema: StoreSchema{}} + ret := newSchemaBuilder() it, err := bucket.Iterator(nil, nil) if err != nil { return nil, err @@ -263,7 +289,7 @@ func NewStore(db dbm.DBConnection, opts StoreParams) (ret *Store, err error) { err = util.CombineErrors(err, ret.Close(), "base.Close also failed") } }() - writeSchema := func(reg *SchemaBuilder) { + writeSchema := func(sch StoreSchema) { schemaWriter := prefixdb.NewPrefixWriter(ret.stateTxn, schemaPrefix) var it dbm.Iterator it, err = schemaView.Iterator(nil, nil) @@ -285,7 +311,7 @@ func NewStore(db dbm.DBConnection, opts StoreParams) (ret *Store, err error) { return } // NB. the migrated contents and schema are not committed until the next store.Commit - for skey, typ := range reg.StoreSchema { + for skey, typ := range sch { err = schemaWriter.Set([]byte(skey), []byte{byte(typ)}) if err != nil { return @@ -300,16 +326,11 @@ func NewStore(db dbm.DBConnection, opts StoreParams) (ret *Store, err error) { // If the loaded schema is empty (for new store), just copy the config schema; // Otherwise, migrate, then verify it is identical to the config schema if len(reg.StoreSchema) == 0 { - for k, v := range opts.StoreSchema { - reg.StoreSchema[k] = v - } - reg.reserved = make([]string, len(opts.reserved)) - copy(reg.reserved, opts.reserved) - writeSchema(reg) + writeSchema(opts.StoreSchema) } else { // Apply migrations to the schema if opts.Upgrades != nil { - err = reg.MigrateSchema(*opts.Upgrades) + err = reg.migrateSchema(*opts.Upgrades) if err != nil { return } @@ -323,10 +344,18 @@ func NewStore(db dbm.DBConnection, opts StoreParams) (ret *Store, err error) { if err != nil { return } - writeSchema(reg) + writeSchema(opts.StoreSchema) + } + } + ret.schema = StoreKeySchema{} + for key, typ := range opts.StoreSchema { + var skey types.StoreKey + skey, err = opts.storeKey(key) + if err != nil { + return } + ret.schema[skey] = typ } - ret.schema = reg.StoreSchema return } @@ -407,7 +436,7 @@ func substorePrefix(key string) []byte { func (rs *Store) GetKVStore(skey types.StoreKey) types.KVStore { key := skey.Name() var parent types.KVStore - typ, has := rs.schema[key] + typ, has := rs.schema[skey] if !has { panic(ErrStoreNotFound(key)) } @@ -523,14 +552,14 @@ func (s *Store) Commit() types.CommitID { func (s *Store) getMerkleRoots() (ret map[string][]byte, err error) { ret = map[string][]byte{} for key := range s.schema { - sub, has := s.substoreCache[key] + sub, has := s.substoreCache[key.Name()] if !has { - sub, err = s.getSubstore(key) + sub, err = s.getSubstore(key.Name()) if err != nil { return } } - ret[key] = sub.stateCommitmentStore.Root() + ret[key.Name()] = sub.stateCommitmentStore.Root() } return } @@ -780,7 +809,7 @@ func binarySearch(hay []string, ndl string) (int, bool) { } // Migrates the state of the registry based on the upgrades -func (pr *SchemaBuilder) MigrateSchema(upgrades types.StoreUpgrades) error { +func (pr *SchemaBuilder) migrateSchema(upgrades types.StoreUpgrades) error { for _, key := range upgrades.Deleted { sst, ix, err := pr.storeInfo(key) if err != nil { @@ -802,13 +831,13 @@ func (pr *SchemaBuilder) MigrateSchema(upgrades types.StoreUpgrades) error { } pr.reserved = append(pr.reserved[:ix], pr.reserved[ix+1:]...) delete(pr.StoreSchema, rename.OldKey) - err = pr.RegisterSubstore(rename.NewKey, types.StoreTypePersistent) + err = pr.registerName(rename.NewKey, types.StoreTypePersistent) if err != nil { return err } } for _, key := range upgrades.Added { - err := pr.RegisterSubstore(key, types.StoreTypePersistent) + err := pr.registerName(key, types.StoreTypePersistent) if err != nil { return err } @@ -830,7 +859,8 @@ func (pr *SchemaBuilder) storeInfo(key string) (sst types.StoreType, ix int, err return } -func (pr *SchemaBuilder) RegisterSubstore(key string, typ types.StoreType) error { +// registerName registers a store key by name only +func (pr *SchemaBuilder) registerName(key string, typ types.StoreType) error { if !validSubStoreType(typ) { return fmt.Errorf("StoreType not supported: %v", typ) } @@ -854,13 +884,12 @@ func (pr *SchemaBuilder) RegisterSubstore(key string, typ types.StoreType) error } func (tlm *traceListenMixin) AddListeners(skey types.StoreKey, listeners []types.WriteListener) { - key := skey.Name() - tlm.listeners[key] = append(tlm.listeners[key], listeners...) + tlm.listeners[skey] = append(tlm.listeners[skey], listeners...) } // ListeningEnabled returns if listening is enabled for a specific KVStore func (tlm *traceListenMixin) ListeningEnabled(key types.StoreKey) bool { - if ls, has := tlm.listeners[key.Name()]; has { + if ls, has := tlm.listeners[key]; has { return len(ls) != 0 } return false @@ -881,7 +910,7 @@ func (tlm *traceListenMixin) wrapTraceListen(store types.KVStore, skey types.Sto store = tracekv.NewStore(store, tlm.TraceWriter, tlm.TraceContext) } if tlm.ListeningEnabled(skey) { - store = listenkv.NewStore(store, skey, tlm.listeners[skey.Name()]) + store = listenkv.NewStore(store, skey, tlm.listeners[skey]) } return store } diff --git a/store/v2/multi/store_test.go b/store/v2/multi/store_test.go index 112bf55d4341..650d2b8b1cd8 100644 --- a/store/v2/multi/store_test.go +++ b/store/v2/multi/store_test.go @@ -34,16 +34,16 @@ var ( func simpleStoreParams(t *testing.T) StoreParams { opts := DefaultStoreParams() - require.NoError(t, opts.RegisterSubstore(skey_1.Name(), types.StoreTypePersistent)) + require.NoError(t, opts.RegisterSubstore(skey_1, types.StoreTypePersistent)) return opts } func storeConfig123(t *testing.T) StoreParams { opts := DefaultStoreParams() opts.Pruning = types.PruneNothing - require.NoError(t, opts.RegisterSubstore(skey_1.Name(), types.StoreTypePersistent)) - require.NoError(t, opts.RegisterSubstore(skey_2.Name(), types.StoreTypePersistent)) - require.NoError(t, opts.RegisterSubstore(skey_3.Name(), types.StoreTypePersistent)) + require.NoError(t, opts.RegisterSubstore(skey_1, types.StoreTypePersistent)) + require.NoError(t, opts.RegisterSubstore(skey_2, types.StoreTypePersistent)) + require.NoError(t, opts.RegisterSubstore(skey_3, types.StoreTypePersistent)) return opts } @@ -589,7 +589,7 @@ func TestQuery(t *testing.T) { // querying past version succeeds after rename opts := DefaultStoreParams() - require.NoError(t, opts.RegisterSubstore(skey_2.Name(), types.StoreTypePersistent)) + require.NoError(t, opts.RegisterSubstore(skey_2, types.StoreTypePersistent)) opts.Upgrades = &types.StoreUpgrades{ Renamed: []types.StoreRename{types.StoreRename{skey_1.Name(), skey_2.Name()}}, } @@ -672,20 +672,20 @@ func TestQuery(t *testing.T) { func TestStoreParams(t *testing.T) { opts := DefaultStoreParams() // Fail with invalid types - require.Error(t, opts.RegisterSubstore(skey_1.Name(), types.StoreTypeDB)) - require.Error(t, opts.RegisterSubstore(skey_1.Name(), types.StoreTypeSMT)) + require.Error(t, opts.RegisterSubstore(skey_1, types.StoreTypeDB)) + require.Error(t, opts.RegisterSubstore(skey_1, types.StoreTypeSMT)) // Ensure that no prefix conflicts are allowed - require.NoError(t, opts.RegisterSubstore(skey_1.Name(), types.StoreTypePersistent)) - require.NoError(t, opts.RegisterSubstore(skey_2.Name(), types.StoreTypeMemory)) - require.NoError(t, opts.RegisterSubstore(skey_3b.Name(), types.StoreTypeTransient)) - require.Error(t, opts.RegisterSubstore(skey_1b.Name(), types.StoreTypePersistent)) - require.Error(t, opts.RegisterSubstore(skey_2b.Name(), types.StoreTypePersistent)) - require.Error(t, opts.RegisterSubstore(skey_3.Name(), types.StoreTypePersistent)) + require.NoError(t, opts.RegisterSubstore(skey_1, types.StoreTypePersistent)) + require.NoError(t, opts.RegisterSubstore(skey_2, types.StoreTypeMemory)) + require.NoError(t, opts.RegisterSubstore(skey_3b, types.StoreTypeTransient)) + require.Error(t, opts.RegisterSubstore(skey_1b, types.StoreTypePersistent)) + require.Error(t, opts.RegisterSubstore(skey_2b, types.StoreTypePersistent)) + require.Error(t, opts.RegisterSubstore(skey_3, types.StoreTypePersistent)) } func TestMultiStoreBasic(t *testing.T) { opts := DefaultStoreParams() - err := opts.RegisterSubstore(skey_1.Name(), types.StoreTypePersistent) + err := opts.RegisterSubstore(skey_1, types.StoreTypePersistent) require.NoError(t, err) db := memdb.NewDB() store, err := NewStore(db, opts) @@ -777,7 +777,7 @@ func TestMultiStoreMigration(t *testing.T) { t.Run("basic migration", func(t *testing.T) { // now, let's load with upgrades... - opts.Upgrades = &types.StoreUpgrades{ + upgrades := &types.StoreUpgrades{ Added: []string{skey_4.Name()}, Renamed: []types.StoreRename{{ OldKey: skey_2.Name(), @@ -785,12 +785,20 @@ func TestMultiStoreMigration(t *testing.T) { }}, Deleted: []string{skey_3.Name()}, } + // store must be loaded with post-migration schema, so this fails + opts := storeConfig123(t) + opts.Upgrades = upgrades store, err = NewStore(db, opts) require.Error(t, err) - opts.MigrateSchema(*opts.Upgrades) + opts = DefaultStoreParams() + opts.Upgrades = upgrades + require.NoError(t, opts.RegisterSubstore(skey_1, types.StoreTypePersistent)) + require.NoError(t, opts.RegisterSubstore(skey_2b, types.StoreTypePersistent)) + require.NoError(t, opts.RegisterSubstore(skey_4, types.StoreTypePersistent)) store, err = NewStore(db, opts) + require.NoError(t, err) // store1 was not changed s1 = store.GetKVStore(skey_1) @@ -833,14 +841,11 @@ func TestMultiStoreMigration(t *testing.T) { // pass in a schema reflecting the migrations migratedOpts := DefaultStoreParams() - err = migratedOpts.RegisterSubstore(skey_1.Name(), types.StoreTypePersistent) - require.NoError(t, err) - err = migratedOpts.RegisterSubstore(skey_2b.Name(), types.StoreTypePersistent) - require.NoError(t, err) - err = migratedOpts.RegisterSubstore(skey_4.Name(), types.StoreTypePersistent) - require.NoError(t, err) + require.NoError(t, migratedOpts.RegisterSubstore(skey_1, types.StoreTypePersistent)) + require.NoError(t, migratedOpts.RegisterSubstore(skey_2b, types.StoreTypePersistent)) + require.NoError(t, migratedOpts.RegisterSubstore(skey_4, types.StoreTypePersistent)) store, err = NewStore(db, migratedOpts) - require.Nil(t, err) + require.NoError(t, err) require.Equal(t, migratedID, store.LastCommitID()) // query this new store @@ -893,8 +898,8 @@ func TestTrace(t *testing.T) { db := memdb.NewDB() opts := simpleStoreParams(t) - require.NoError(t, opts.RegisterSubstore(skey_2.Name(), types.StoreTypeMemory)) - require.NoError(t, opts.RegisterSubstore(skey_3.Name(), types.StoreTypeTransient)) + require.NoError(t, opts.RegisterSubstore(skey_2, types.StoreTypeMemory)) + require.NoError(t, opts.RegisterSubstore(skey_3, types.StoreTypeTransient)) store, err := NewStore(db, opts) require.NoError(t, err) @@ -971,8 +976,8 @@ func TestListeners(t *testing.T) { db := memdb.NewDB() opts := simpleStoreParams(t) - require.NoError(t, opts.RegisterSubstore(skey_2.Name(), types.StoreTypeMemory)) - require.NoError(t, opts.RegisterSubstore(skey_3.Name(), types.StoreTypeTransient)) + require.NoError(t, opts.RegisterSubstore(skey_2, types.StoreTypeMemory)) + require.NoError(t, opts.RegisterSubstore(skey_3, types.StoreTypeTransient)) store, err := NewStore(db, opts) require.NoError(t, err) diff --git a/testutil/context.go b/testutil/context.go index 010ca499c60b..781f73f8aa53 100644 --- a/testutil/context.go +++ b/testutil/context.go @@ -20,11 +20,11 @@ func DefaultContext(key, tkey stypes.StoreKey) (ret sdk.Context) { }() db := memdb.NewDB() opts := multi.DefaultStoreParams() - err = opts.RegisterSubstore(key.Name(), stypes.StoreTypePersistent) + err = opts.RegisterSubstore(key, stypes.StoreTypePersistent) if err != nil { return } - err = opts.RegisterSubstore(tkey.Name(), stypes.StoreTypeTransient) + err = opts.RegisterSubstore(tkey, stypes.StoreTypeTransient) if err != nil { return } From ccb908da4ce9b5e7d44d946f09a60edb9d10e9b9 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Mon, 7 Feb 2022 14:52:18 +0800 Subject: [PATCH 19/78] wrap v1 MultiStore in v2 interface; use in baseapp --- baseapp/baseapp.go | 2 +- baseapp/baseapp_test.go | 8 +-- internal/db/legacy_adapter.go | 104 ++++++++++++++++++++++++++++ store/v2/multi/v1asv2.go | 90 ++++++++++++++++++++++++ testutil/context.go | 2 +- x/group/keeper/invariants_test.go | 2 +- x/params/types/subspace_test.go | 2 +- x/upgrade/types/storeloader.go | 2 +- x/upgrade/types/storeloader_test.go | 4 +- 9 files changed, 205 insertions(+), 11 deletions(-) create mode 100644 internal/db/legacy_adapter.go create mode 100644 store/v2/multi/v1asv2.go diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 955a45d22ec4..cb712c8d3fe2 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -233,7 +233,7 @@ func (app *BaseApp) loadStore() error { for _, opt := range app.storeOpts { opt(&config, latest) } - app.store, err = multi.NewStore(app.db, config) + app.store, err = multi.NewV1MultiStoreAsV2(app.db, config) if err != nil { return fmt.Errorf("failed to load store: %w", err) } diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index ad57a1886e26..86ccba7a7ad4 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -337,7 +337,7 @@ func initStore(t *testing.T, db dbm.DBConnection, storeKey string, k, v []byte) opts := multi.DefaultStoreParams() opts.Pruning = stypes.PruneNothing require.NoError(t, opts.RegisterSubstore(key, stypes.StoreTypePersistent)) - rs, err := multi.NewStore(db, opts) + rs, err := multi.NewV1MultiStoreAsV2(db, opts) require.NoError(t, err) require.Equal(t, int64(0), rs.LastCommitID().Version) @@ -355,7 +355,7 @@ func checkStore(t *testing.T, db dbm.DBConnection, ver int64, storeKey string, k opts.Pruning = stypes.PruneNothing key := sdk.NewKVStoreKey(storeKey) require.NoError(t, opts.RegisterSubstore(key, stypes.StoreTypePersistent)) - rs, err := multi.NewStore(db, opts) + rs, err := multi.NewV1MultiStoreAsV2(db, opts) require.NoError(t, err) require.Equal(t, ver, rs.LastCommitID().Version) @@ -423,7 +423,7 @@ func TestLoadVersionPruning(t *testing.T) { // CacheMultiStoreWithVersion returned no error on missing version (?) for _, v := range []int64{1, 2, 4} { _, err = app.Store().GetVersion(v) - require.Error(t, err, "version=%v", v) + require.NoError(t, err, "version=%v", v) } for _, v := range []int64{3, 5, 6, 7} { @@ -571,7 +571,7 @@ func TestInitChainer(t *testing.T) { app.Commit() res = app.Query(query) - require.True(t, res.IsOK()) + require.True(t, res.IsOK(), res.Log) require.Equal(t, int64(1), app.LastBlockHeight()) require.Equal(t, value, res.Value) require.NoError(t, app.CloseStore()) diff --git a/internal/db/legacy_adapter.go b/internal/db/legacy_adapter.go new file mode 100644 index 000000000000..1b6f5b7d81e3 --- /dev/null +++ b/internal/db/legacy_adapter.go @@ -0,0 +1,104 @@ +package db + +import ( + "errors" + + dbm "github.com/cosmos/cosmos-sdk/db" + + tmdb "github.com/tendermint/tm-db" +) + +// An adapter used to wrap objects supporting cosmos-sdk/db interface so that they support +// the tm-db interface. +// +// This serves as a transitional step in introducing the new DB interface while maintaining +// compatibility with existing code that expects the old interface. +type tmdbAdapter struct { + dbm.DBReadWriter + db dbm.DBConnection +} +type tmdbBatchAdapter struct { + *tmdbAdapter + written bool +} + +var ( + _ tmdb.DB = (*tmdbAdapter)(nil) +) + +// ConnectionAsTmdb returns a tmdb.DB which wraps a DBConnection. +func ConnectionAsTmdb(db dbm.DBConnection) *tmdbAdapter { return &tmdbAdapter{db.ReadWriter(), db} } + +func (d *tmdbAdapter) Close() error { return d.db.Close() } + +func (d *tmdbAdapter) sync() error { + err := d.DBReadWriter.Commit() + if err != nil { + return err + } + d.DBReadWriter = d.db.ReadWriter() + return nil +} +func (d *tmdbAdapter) DeleteSync(k []byte) error { + err := d.DBReadWriter.Delete(k) + if err != nil { + return err + } + return d.sync() +} +func (d *tmdbAdapter) SetSync(k, v []byte) error { + err := d.DBReadWriter.Set(k, v) + if err != nil { + return err + } + return d.sync() +} + +func (d *tmdbAdapter) Iterator(s, e []byte) (tmdb.Iterator, error) { + it, err := d.DBReadWriter.Iterator(s, e) + if err != nil { + return nil, err + } + return DBToStoreIterator(it), nil +} +func (d *tmdbAdapter) ReverseIterator(s, e []byte) (tmdb.Iterator, error) { + it, err := d.DBReadWriter.ReverseIterator(s, e) + if err != nil { + return nil, err + } + return DBToStoreIterator(it), nil +} + +// NewBatch returns a tmdb.Batch which wraps a DBWriter. +func (d *tmdbAdapter) NewBatch() tmdb.Batch { + return &tmdbBatchAdapter{d, false} +} +func (d *tmdbAdapter) Print() error { return nil } +func (d *tmdbAdapter) Stats() map[string]string { return nil } + +func (d *tmdbBatchAdapter) Set(k, v []byte) error { + if d.written { + return errors.New("Batch already written") + } + return d.tmdbAdapter.Set(k, v) +} +func (d *tmdbBatchAdapter) Delete(k []byte) error { + if d.written { + return errors.New("Batch already written") + } + return d.tmdbAdapter.Delete(k) +} +func (d *tmdbBatchAdapter) WriteSync() error { + if d.written { + return errors.New("Batch already written") + } + d.written = true + return d.sync() +} +func (d *tmdbBatchAdapter) Write() error { + if d.written { + return errors.New("Batch already written") + } + return d.WriteSync() +} +func (d *tmdbBatchAdapter) Close() error { return nil } diff --git a/store/v2/multi/v1asv2.go b/store/v2/multi/v1asv2.go new file mode 100644 index 000000000000..97008270529f --- /dev/null +++ b/store/v2/multi/v1asv2.go @@ -0,0 +1,90 @@ +package multi + +import ( + "io" + + "github.com/cosmos/cosmos-sdk/db" + dbutil "github.com/cosmos/cosmos-sdk/internal/db" + "github.com/cosmos/cosmos-sdk/store/rootmulti" + v1 "github.com/cosmos/cosmos-sdk/store/types" + v2 "github.com/cosmos/cosmos-sdk/store/v2" +) + +var ( + _ v2.CommitMultiStore = store1as2{} + _ v2.Queryable = store1as2{} + _ v2.CacheMultiStore = cacheStore1as2{} +) + +type store1as2 struct { + *rootmulti.Store +} + +type cacheStore1as2 struct { + v1.CacheMultiStore +} + +// NewV1MultiStoreAsV2 constructs a v1 CommitMultiStore from v2.StoreParams +func NewV1MultiStoreAsV2(database db.DBConnection, opts StoreParams) (v2.CommitMultiStore, error) { + cms := rootmulti.NewStore(dbutil.ConnectionAsTmdb(database)) + for name, typ := range opts.StoreSchema { + switch typ { + case v2.StoreTypePersistent: + typ = v1.StoreTypeIAVL + } + skey, err := opts.storeKey(name) + if err != nil { + return nil, err + } + cms.MountStoreWithDB(skey, typ, nil) + } + cms.SetPruning(opts.Pruning) + err := cms.SetInitialVersion(int64(opts.InitialVersion)) + if err != nil { + return nil, err + } + err = cms.LoadLatestVersionAndUpgrade(opts.Upgrades) + if err != nil { + return nil, err + } + for skey, ls := range opts.listeners { + cms.AddListeners(skey, ls) + } + cms.SetTracer(opts.TraceWriter) + cms.SetTracingContext(opts.TraceContext) + return store1as2{cms}, nil +} + +func WrapStoreAsV2CommitMultiStore(s *rootmulti.Store) v2.CommitMultiStore { + return store1as2{s} +} + +func WrapCacheStoreAsV2CacheMultiStore(cs v1.CacheMultiStore) v2.CacheMultiStore { + return cacheStore1as2{cs} +} + +// commit store + +func (s store1as2) CacheWrap() v2.CacheMultiStore { + return cacheStore1as2{s.CacheMultiStore()} +} +func (s store1as2) GetVersion(ver int64) (v2.MultiStore, error) { + ret, err := s.CacheMultiStoreWithVersion(ver) + return cacheStore1as2{ret}, err +} +func (s store1as2) Close() error { return nil } +func (s store1as2) SetInitialVersion(ver uint64) error { + return s.Store.SetInitialVersion(int64(ver)) +} + +func (s store1as2) SetTracer(w io.Writer) { s.SetTracer(w) } +func (s store1as2) SetTracingContext(tc v2.TraceContext) { s.SetTracingContext(tc) } + +// cache store + +func (s cacheStore1as2) CacheWrap() v2.CacheMultiStore { + return cacheStore1as2{s.CacheMultiStore.CacheMultiStore()} +} + +func (s cacheStore1as2) SetTracer(w io.Writer) { s.SetTracer(w) } +func (s cacheStore1as2) SetTracingContext(tc v2.TraceContext) { s.SetTracingContext(tc) } diff --git a/testutil/context.go b/testutil/context.go index 781f73f8aa53..b2eb0f4d1996 100644 --- a/testutil/context.go +++ b/testutil/context.go @@ -28,7 +28,7 @@ func DefaultContext(key, tkey stypes.StoreKey) (ret sdk.Context) { if err != nil { return } - rs, err := multi.NewStore(db, opts) + rs, err := multi.NewV1MultiStoreAsV2(db, opts) if err != nil { return } diff --git a/x/group/keeper/invariants_test.go b/x/group/keeper/invariants_test.go index 8809aa9f5730..519dace8b994 100644 --- a/x/group/keeper/invariants_test.go +++ b/x/group/keeper/invariants_test.go @@ -42,7 +42,7 @@ func (s *invariantTestSuite) SetupSuite() { db := memdb.NewDB() config := multi.DefaultStoreParams() s.Require().NoError(config.RegisterSubstore(key.Name(), storetypes.StoreTypePersistent)) - ms, err := multi.NewStore(db, config) + ms, err := multi.NewV1MultiStoreAsV2(db, config) s.Require().NoError(err) sdkCtx := sdk.NewContext(ms, tmproto.Header{}, false, log.NewNopLogger()) diff --git a/x/params/types/subspace_test.go b/x/params/types/subspace_test.go index 068ee3026a11..6039f26bfc18 100644 --- a/x/params/types/subspace_test.go +++ b/x/params/types/subspace_test.go @@ -34,7 +34,7 @@ func (suite *SubspaceTestSuite) SetupTest() { config := multi.DefaultStoreParams() suite.NoError(config.RegisterSubstore(key.Name(), storetypes.StoreTypePersistent)) suite.NoError(config.RegisterSubstore(tkey.Name(), storetypes.StoreTypeTransient)) - ms, err := multi.NewStore(db, config) + ms, err := multi.NewV1MultiStoreAsV2(db, config) suite.NoError(err) encCfg := simapp.MakeTestEncodingConfig() diff --git a/x/upgrade/types/storeloader.go b/x/upgrade/types/storeloader.go index 2d533c7f045d..0c918e3ef3f3 100644 --- a/x/upgrade/types/storeloader.go +++ b/x/upgrade/types/storeloader.go @@ -12,7 +12,7 @@ func UpgradeStoreOption(upgradeHeight uint64, storeUpgrades *storetypes.StoreUpg // Check if the current commit version and upgrade height matches if upgradeHeight == loadHeight+1 { if len(storeUpgrades.Renamed) > 0 || len(storeUpgrades.Deleted) > 0 || len(storeUpgrades.Added) > 0 { - par.Upgrades = append(par.Upgrades, *storeUpgrades) + par.Upgrades = storeUpgrades } } return nil diff --git a/x/upgrade/types/storeloader_test.go b/x/upgrade/types/storeloader_test.go index 8305337c47fd..898c20e16725 100644 --- a/x/upgrade/types/storeloader_test.go +++ b/x/upgrade/types/storeloader_test.go @@ -30,7 +30,7 @@ func defaultLogger() log.Logger { func initStore(t *testing.T, db dbm.DBConnection, config multi.StoreParams, storeKey string, k, v []byte) { key := sdk.NewKVStoreKey(storeKey) - rs, err := multi.NewStore(db, config) + rs, err := multi.NewV1MultiStoreAsV2(db, config) require.NoError(t, err) rs.SetPruning(storetypes.PruneNothing) require.Equal(t, int64(0), rs.LastCommitID().Version) @@ -46,7 +46,7 @@ func initStore(t *testing.T, db dbm.DBConnection, config multi.StoreParams, stor func checkStore(t *testing.T, db dbm.DBConnection, config multi.StoreParams, ver int64, storeKey string, k, v []byte) { key := sdk.NewKVStoreKey(storeKey) - rs, err := multi.NewStore(db, config) + rs, err := multi.NewV1MultiStoreAsV2(db, config) require.NoError(t, err) rs.SetPruning(storetypes.PruneNothing) require.Equal(t, ver, rs.LastCommitID().Version) From 532a6add3eeb755fb6ff51ffc78f28646dc98dcd Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Tue, 15 Feb 2022 14:42:01 +0800 Subject: [PATCH 20/78] viewstore.CacheStore() replaces CacheMultiStoreWithVersion --- baseapp/abci.go | 6 ++---- baseapp/test_helpers.go | 2 +- store/v2/multi/cache_store.go | 5 ----- store/v2/multi/view_store.go | 4 +++- 4 files changed, 6 insertions(+), 11 deletions(-) diff --git a/baseapp/abci.go b/baseapp/abci.go index d039ee637510..299b21f4e466 100644 --- a/baseapp/abci.go +++ b/baseapp/abci.go @@ -17,7 +17,6 @@ import ( "github.com/cosmos/cosmos-sdk/codec" snapshottypes "github.com/cosmos/cosmos-sdk/snapshots/types" - "github.com/cosmos/cosmos-sdk/store/v2/multi" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/types/tx" @@ -646,11 +645,10 @@ func (app *BaseApp) createQueryContext(height int64, prove bool) (sdk.Context, e "failed to load state at height %d; %s (latest height: %d)", height, err, app.LastBlockHeight(), ) } - cacheMS := multi.BasicAsCacheStore(version) - // branch the commit-multistore for safety ctx := sdk.NewContext( - cacheMS, app.checkState.ctx.BlockHeader(), true, app.logger, + version.CacheWrap(), // branch the commit-multistore for safety + app.checkState.ctx.BlockHeader(), true, app.logger, ).WithMinGasPrices(app.minGasPrices).WithBlockHeight(height) return ctx, nil diff --git a/baseapp/test_helpers.go b/baseapp/test_helpers.go index 28cac0c7308a..37ba5588848c 100644 --- a/baseapp/test_helpers.go +++ b/baseapp/test_helpers.go @@ -98,5 +98,5 @@ func (app *BaseApp) NewContextAt(isCheckTx bool, header tmproto.Header, height i if err != nil { return sdk.Context{}, err } - return sdk.NewContext(multi.BasicAsCacheStore(view), header, isCheckTx, app.logger), nil + return sdk.NewContext(view.CacheWrap(), header, isCheckTx, app.logger), nil } diff --git a/store/v2/multi/cache_store.go b/store/v2/multi/cache_store.go index a1c281a59e8c..5b2fc9014309 100644 --- a/store/v2/multi/cache_store.go +++ b/store/v2/multi/cache_store.go @@ -53,11 +53,6 @@ type noopCacheStore struct { func (noopCacheStore) Write() {} -// pretend basic store is cache store -func BasicAsCacheStore(bs types.MultiStore) types.CacheMultiStore { - return noopCacheStore{newCacheStore(bs)} -} - // pretend commit store is cache store func CommitAsCacheStore(s types.CommitMultiStore) types.CacheMultiStore { return noopCacheStore{newCacheStore(s)} diff --git a/store/v2/multi/view_store.go b/store/v2/multi/view_store.go index c51b1d065275..58c32c4c3ae8 100644 --- a/store/v2/multi/view_store.go +++ b/store/v2/multi/view_store.go @@ -63,8 +63,10 @@ func (vs *viewStore) getSubstore(key string) (*viewSubstore, error) { }, nil } +// CacheWrap implements MultiStore. +// Because this store is a read-only view, the returned store's Write operation is a no-op. func (vs *viewStore) CacheWrap() types.CacheMultiStore { - return newCacheStore(vs) + return noopCacheStore{newCacheStore(vs)} } func (s *viewSubstore) GetStateCommitmentStore() *smt.Store { From 5f6fe12dccd175b560d3a240c83f54615397ddd9 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Tue, 15 Feb 2022 17:38:51 +0800 Subject: [PATCH 21/78] test cleanup --- store/v2/multi/store_test.go | 66 ++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/store/v2/multi/store_test.go b/store/v2/multi/store_test.go index 650d2b8b1cd8..06b715fc6b58 100644 --- a/store/v2/multi/store_test.go +++ b/store/v2/multi/store_test.go @@ -32,13 +32,13 @@ var ( skey_3b = types.NewKVStoreKey("store3b") ) -func simpleStoreParams(t *testing.T) StoreParams { +func storeParams1(t *testing.T) StoreParams { opts := DefaultStoreParams() require.NoError(t, opts.RegisterSubstore(skey_1, types.StoreTypePersistent)) return opts } -func storeConfig123(t *testing.T) StoreParams { +func storeParams123(t *testing.T) StoreParams { opts := DefaultStoreParams() opts.Pruning = types.PruneNothing require.NoError(t, opts.RegisterSubstore(skey_1, types.StoreTypePersistent)) @@ -48,7 +48,7 @@ func storeConfig123(t *testing.T) StoreParams { } func newSubStoreWithData(t *testing.T, db dbm.DBConnection, storeData map[string]string) (*Store, types.KVStore) { - root, err := NewStore(db, simpleStoreParams(t)) + root, err := NewStore(db, storeParams1(t)) require.NoError(t, err) store := root.GetKVStore(skey_1) @@ -94,7 +94,7 @@ func TestGetSetHasDelete(t *testing.T) { func TestConstructors(t *testing.T) { db := memdb.NewDB() - store, err := NewStore(db, simpleStoreParams(t)) + store, err := NewStore(db, storeParams1(t)) require.NoError(t, err) _ = store.GetKVStore(skey_1) store.Commit() @@ -135,7 +135,7 @@ func TestConstructors(t *testing.T) { merkledb.Close() t.Run("can't load existing store if we can't access root hash", func(t *testing.T) { - store, err = NewStore(db, simpleStoreParams(t)) + store, err = NewStore(db, storeParams1(t)) require.NoError(t, err) store.Commit() require.NoError(t, store.Close()) @@ -246,7 +246,7 @@ func TestCommit(t *testing.T) { require.NotEqual(t, previd.Version, id.Version) } } - basicOpts := simpleStoreParams(t) + basicOpts := storeParams1(t) basicOpts.Pruning = types.PruneNothing t.Run("sanity tests for Merkle hashing", func(t *testing.T) { testBasic(basicOpts) @@ -285,7 +285,7 @@ func TestCommit(t *testing.T) { require.NoError(t, store.Close()) } - opts := simpleStoreParams(t) + opts := storeParams1(t) opts.Pruning = types.PruneNothing // Ensure Store's commit is rolled back in each failure case... @@ -323,7 +323,7 @@ func TestCommit(t *testing.T) { testFailedCommit(t, store, nil, opts) }) - opts = simpleStoreParams(t) + opts = storeParams1(t) t.Run("recover after stateDB.Versions error triggers failure", func(t *testing.T) { db := memdb.NewDB() store, err := NewStore(db, opts) @@ -358,7 +358,7 @@ func TestCommit(t *testing.T) { }) t.Run("first commit version matches InitialVersion", func(t *testing.T) { - opts = simpleStoreParams(t) + opts = storeParams1(t) opts.InitialVersion = 5 opts.Pruning = types.PruneNothing opts.StateCommitmentDB = memdb.NewDB() @@ -368,14 +368,14 @@ func TestCommit(t *testing.T) { }) // test improbable failures to fill out test coverage - opts = simpleStoreParams(t) + opts = storeParams1(t) store, err := NewStore(memdb.NewDB(), opts) require.NoError(t, err) store.Commit() store.stateDB = dbVersionsFails{store.stateDB} require.Panics(t, func() { store.LastCommitID() }) - opts = simpleStoreParams(t) + opts = storeParams1(t) opts.StateCommitmentDB = memdb.NewDB() store, err = NewStore(memdb.NewDB(), opts) require.NoError(t, err) @@ -406,7 +406,7 @@ func TestPruning(t *testing.T) { for tci, tc := range testCases { dbs := []dbm.DBConnection{memdb.NewDB(), memdb.NewDB()} - opts := simpleStoreParams(t) + opts := storeParams1(t) opts.Pruning = tc.PruningOptions opts.StateCommitmentDB = dbs[1] store, err := NewStore(dbs[0], opts) @@ -440,7 +440,7 @@ func TestPruning(t *testing.T) { 20: []uint64{5, 10, 15, 20}, } db := memdb.NewDB() - opts := simpleStoreParams(t) + opts := storeParams1(t) opts.Pruning = types.PruningOptions{0, 5, 10} store, err := NewStore(db, opts) require.NoError(t, err) @@ -496,7 +496,7 @@ func TestQuery(t *testing.T) { valExpSub2, err := KVs2.Marshal() require.NoError(t, err) - store, err := NewStore(memdb.NewDB(), simpleStoreParams(t)) + store, err := NewStore(memdb.NewDB(), storeParams1(t)) require.NoError(t, err) cid := store.Commit() ver := cid.Version @@ -574,7 +574,7 @@ func TestQuery(t *testing.T) { require.Equal(t, v1, qres.Value) // querying an empty store will fail - store, err = NewStore(stateDB, simpleStoreParams(t)) + store, err = NewStore(stateDB, storeParams1(t)) require.NoError(t, err) qres = store.Query(queryHeight0) require.True(t, qres.IsErr()) @@ -605,7 +605,7 @@ func TestQuery(t *testing.T) { t.Run("failed queries", func(t *testing.T) { stateDB := memdb.NewDB() - store, err = NewStore(stateDB, simpleStoreParams(t)) + store, err = NewStore(stateDB, storeParams1(t)) require.NoError(t, err) store.GetKVStore(skey_1).Set(k1, v1) store.Commit() @@ -622,7 +622,7 @@ func TestQuery(t *testing.T) { require.True(t, qres.IsErr()) require.NoError(t, store.Close()) - store, err = NewStore(stateDB, simpleStoreParams(t)) + store, err = NewStore(stateDB, storeParams1(t)) require.NoError(t, err) // query with a nil or empty key fails @@ -658,7 +658,7 @@ func TestQuery(t *testing.T) { testProve() require.NoError(t, store.Close()) - opts := simpleStoreParams(t) + opts := storeParams1(t) opts.StateCommitmentDB = memdb.NewDB() store, err = NewStore(memdb.NewDB(), opts) require.NoError(t, err) @@ -703,7 +703,7 @@ func TestMultiStoreBasic(t *testing.T) { func TestGetVersion(t *testing.T) { db := memdb.NewDB() - opts := storeConfig123(t) + opts := storeParams123(t) store, err := NewStore(db, opts) require.NoError(t, err) @@ -738,7 +738,7 @@ func TestGetVersion(t *testing.T) { func TestMultiStoreMigration(t *testing.T) { db := memdb.NewDB() - opts := storeConfig123(t) + opts := storeParams123(t) store, err := NewStore(db, opts) require.NoError(t, err) @@ -787,7 +787,7 @@ func TestMultiStoreMigration(t *testing.T) { } // store must be loaded with post-migration schema, so this fails - opts := storeConfig123(t) + opts := storeParams123(t) opts.Upgrades = upgrades store, err = NewStore(db, opts) require.Error(t, err) @@ -836,7 +836,7 @@ func TestMultiStoreMigration(t *testing.T) { t.Run("reload after migrations", func(t *testing.T) { // fail to load the migrated store with the old schema - store, err = NewStore(db, storeConfig123(t)) + store, err = NewStore(db, storeParams123(t)) require.Error(t, err) // pass in a schema reflecting the migrations @@ -887,23 +887,23 @@ func TestMultiStoreMigration(t *testing.T) { func TestTrace(t *testing.T) { key, value := []byte("test-key"), []byte("test-value") - tctx := types.TraceContext(map[string]interface{}{"blockHeight": 64}) + tc := types.TraceContext(map[string]interface{}{"blockHeight": 64}) - expected_Set := "{\"operation\":\"write\",\"key\":\"dGVzdC1rZXk=\",\"value\":\"dGVzdC12YWx1ZQ==\",\"metadata\":{\"blockHeight\":64}}\n" - expected_Get := "{\"operation\":\"read\",\"key\":\"dGVzdC1rZXk=\",\"value\":\"dGVzdC12YWx1ZQ==\",\"metadata\":{\"blockHeight\":64}}\n" - expected_Get_missing := "{\"operation\":\"read\",\"key\":\"dGVzdC1rZXk=\",\"value\":\"\",\"metadata\":{\"blockHeight\":64}}\n" - expected_Delete := "{\"operation\":\"delete\",\"key\":\"dGVzdC1rZXk=\",\"value\":\"\",\"metadata\":{\"blockHeight\":64}}\n" - expected_IterKey := "{\"operation\":\"iterKey\",\"key\":\"dGVzdC1rZXk=\",\"value\":\"\",\"metadata\":{\"blockHeight\":64}}\n" - expected_IterValue := "{\"operation\":\"iterValue\",\"key\":\"\",\"value\":\"dGVzdC12YWx1ZQ==\",\"metadata\":{\"blockHeight\":64}}\n" + expected_Set := `{"operation":"write","key":"dGVzdC1rZXk=","value":"dGVzdC12YWx1ZQ==","metadata":{"blockHeight":64}}` + "\n" + expected_Get := `{"operation":"read","key":"dGVzdC1rZXk=","value":"dGVzdC12YWx1ZQ==","metadata":{"blockHeight":64}}` + "\n" + expected_Get_missing := `{"operation":"read","key":"dGVzdC1rZXk=","value":"","metadata":{"blockHeight":64}}` + "\n" + expected_Delete := `{"operation":"delete","key":"dGVzdC1rZXk=","value":"","metadata":{"blockHeight":64}}` + "\n" + expected_IterKey := `{"operation":"iterKey","key":"dGVzdC1rZXk=","value":"","metadata":{"blockHeight":64}}` + "\n" + expected_IterValue := `{"operation":"iterValue","key":"","value":"dGVzdC12YWx1ZQ==","metadata":{"blockHeight":64}}` + "\n" db := memdb.NewDB() - opts := simpleStoreParams(t) + opts := storeParams1(t) require.NoError(t, opts.RegisterSubstore(skey_2, types.StoreTypeMemory)) require.NoError(t, opts.RegisterSubstore(skey_3, types.StoreTypeTransient)) store, err := NewStore(db, opts) require.NoError(t, err) - store.SetTracingContext(tctx) + store.SetTracingContext(tc) require.False(t, store.TracingEnabled()) var buf bytes.Buffer @@ -935,8 +935,8 @@ func TestTrace(t *testing.T) { buf.Reset() store.GetKVStore(skey).Delete(key) require.Equal(t, expected_Delete, buf.String()) - } + store.SetTracer(nil) require.False(t, store.TracingEnabled()) require.NoError(t, store.Close()) @@ -975,7 +975,7 @@ func TestListeners(t *testing.T) { var marshaller = codec.NewProtoCodec(interfaceRegistry) db := memdb.NewDB() - opts := simpleStoreParams(t) + opts := storeParams1(t) require.NoError(t, opts.RegisterSubstore(skey_2, types.StoreTypeMemory)) require.NoError(t, opts.RegisterSubstore(skey_3, types.StoreTypeTransient)) From b200867673c1374052c9d829e51e428397aab241 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Tue, 15 Feb 2022 17:37:38 +0800 Subject: [PATCH 22/78] trace context fence backport https://github.com/cosmos/cosmos-sdk/pull/11117 --- store/v2/multi/store.go | 34 +++++++++++++++++++++---- store/v2/multi/store_test.go | 48 ++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 5 deletions(-) diff --git a/store/v2/multi/store.go b/store/v2/multi/store.go index 6421329eca7f..17b6e4536869 100644 --- a/store/v2/multi/store.go +++ b/store/v2/multi/store.go @@ -125,9 +125,10 @@ type SchemaBuilder struct { // Mixin type that to compose trace & listen state into each root store variant type type traceListenMixin struct { - listeners map[types.StoreKey][]types.WriteListener - TraceWriter io.Writer - TraceContext types.TraceContext + listeners map[types.StoreKey][]types.WriteListener + TraceWriter io.Writer + TraceContext types.TraceContext + traceContextMutex sync.RWMutex } func newTraceListenMixin() *traceListenMixin { @@ -902,12 +903,35 @@ func (tlm *traceListenMixin) SetTracer(w io.Writer) { tlm.TraceWriter = w } func (tlm *traceListenMixin) SetTracingContext(tc types.TraceContext) { - tlm.TraceContext = tc + tlm.traceContextMutex.Lock() + defer tlm.traceContextMutex.Unlock() + if tlm.TraceContext != nil { + for k, v := range tc { + tlm.TraceContext[k] = v + } + } else { + tlm.TraceContext = tc + } +} + +func (tlm *traceListenMixin) getTracingContext() types.TraceContext { + tlm.traceContextMutex.Lock() + defer tlm.traceContextMutex.Unlock() + + if tlm.TraceContext == nil { + return nil + } + + ctx := types.TraceContext{} + for k, v := range tlm.TraceContext { + ctx[k] = v + } + return ctx } func (tlm *traceListenMixin) wrapTraceListen(store types.KVStore, skey types.StoreKey) types.KVStore { if tlm.TracingEnabled() { - store = tracekv.NewStore(store, tlm.TraceWriter, tlm.TraceContext) + store = tracekv.NewStore(store, tlm.TraceWriter, tlm.getTracingContext()) } if tlm.ListeningEnabled(skey) { store = listenkv.NewStore(store, skey, tlm.listeners[skey]) diff --git a/store/v2/multi/store_test.go b/store/v2/multi/store_test.go index 06b715fc6b58..5835d0846943 100644 --- a/store/v2/multi/store_test.go +++ b/store/v2/multi/store_test.go @@ -4,6 +4,7 @@ import ( "bytes" "math" "testing" + "time" "github.com/stretchr/testify/require" @@ -942,6 +943,53 @@ func TestTrace(t *testing.T) { require.NoError(t, store.Close()) } +func TestTraceConcurrency(t *testing.T) { + db := memdb.NewDB() + opts := storeConfig123(t) + store, err := NewStore(db, opts) + require.NoError(t, err) + + b := &bytes.Buffer{} + tc := types.TraceContext(map[string]interface{}{"blockHeight": 64}) + + store.SetTracer(b) + store.SetTracingContext(tc) + + cms := store.CacheWrap() + s1 := cms.GetKVStore(skey_1) + require.NotNil(t, s1) + + stop := make(chan struct{}) + stopW := make(chan struct{}) + + go func(stop chan struct{}) { + for { + select { + case <-stop: + return + default: + s1.Set([]byte{1}, []byte{1}) + cms.Write() + } + } + }(stop) + + go func(stop chan struct{}) { + for { + select { + case <-stop: + return + default: + store.SetTracingContext(tc) + } + } + }(stopW) + + time.Sleep(1 * time.Second) + stop <- struct{}{} + stopW <- struct{}{} +} + func TestListeners(t *testing.T) { kvPairs := []types.KVPair{ {Key: []byte{1}, Value: []byte("v1")}, From 9cdd9900a16235a4f1563485f13c9541d06ef311 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Tue, 15 Feb 2022 17:49:53 +0800 Subject: [PATCH 23/78] get all versions backport https://github.com/cosmos/cosmos-sdk/pull/11124 --- store/v2/multi/store.go | 14 ++++++++++++++ store/v2/multi/v1asv2.go | 2 ++ store/v2/types.go | 3 +++ 3 files changed, 19 insertions(+) diff --git a/store/v2/multi/store.go b/store/v2/multi/store.go index 17b6e4536869..1f158ac06d25 100644 --- a/store/v2/multi/store.go +++ b/store/v2/multi/store.go @@ -676,6 +676,20 @@ func (rs *Store) CacheWrap() types.CacheMultiStore { return newCacheStore(rs) } +// GetAllVersions implements CommitMultiStore. +// https://github.com/cosmos/cosmos-sdk/pull/11124 +func (rs *Store) GetAllVersions() []int { + vs, err := rs.stateDB.Versions() + if err != nil { + panic(err) + } + var ret []int + for it := vs.Iterator(); it.Next(); { + ret = append(ret, int(it.Value())) + } + return ret +} + // parsePath expects a format like /[/] // Must start with /, subpath may be empty // Returns error if it doesn't start with / diff --git a/store/v2/multi/v1asv2.go b/store/v2/multi/v1asv2.go index 97008270529f..3bc7fc772f62 100644 --- a/store/v2/multi/v1asv2.go +++ b/store/v2/multi/v1asv2.go @@ -80,6 +80,8 @@ func (s store1as2) SetInitialVersion(ver uint64) error { func (s store1as2) SetTracer(w io.Writer) { s.SetTracer(w) } func (s store1as2) SetTracingContext(tc v2.TraceContext) { s.SetTracingContext(tc) } +func (s store1as2) GetAllVersions() []int { panic("unsupported: GetAllVersions") } + // cache store func (s cacheStore1as2) CacheWrap() v2.CacheMultiStore { diff --git a/store/v2/types.go b/store/v2/types.go index 1878ef0d585a..ba9150c22518 100644 --- a/store/v2/types.go +++ b/store/v2/types.go @@ -94,6 +94,9 @@ type CommitMultiStore interface { Close() error // Defines the minimum version number that can be saved by this store. SetInitialVersion(uint64) error + // Gets all versions in the DB + // https://github.com/cosmos/cosmos-sdk/pull/11124 + GetAllVersions() []int } // CacheMultiStore defines a branch of the root state which can be written back to the source store. From 555be73dc0a3f2e5ed5ac079ca6e89c880e90246 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Fri, 18 Feb 2022 15:08:13 +0800 Subject: [PATCH 24/78] patches --- baseapp/baseapp.go | 1 - go.mod | 1 + go.sum | 7 +++++++ server/mock/store.go | 4 ++++ 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 1745ccfc8545..54dc15a1efe5 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -11,7 +11,6 @@ import ( "github.com/cosmos/cosmos-sdk/codec/types" dbm "github.com/cosmos/cosmos-sdk/db" "github.com/cosmos/cosmos-sdk/snapshots" - snapshottypes "github.com/cosmos/cosmos-sdk/snapshots/types" stypes "github.com/cosmos/cosmos-sdk/store/v2" "github.com/cosmos/cosmos-sdk/store/v2/multi" sdk "github.com/cosmos/cosmos-sdk/types" diff --git a/go.mod b/go.mod index 546f8d4790c8..da2a7f6b1a38 100644 --- a/go.mod +++ b/go.mod @@ -88,6 +88,7 @@ require ( github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.0.1 // indirect + github.com/google/flatbuffers v2.0.0+incompatible // indirect github.com/google/go-cmp v0.5.7 // indirect github.com/google/orderedcode v0.0.1 // indirect github.com/google/uuid v1.3.0 // indirect diff --git a/go.sum b/go.sum index 211c612ffe17..32e3a8acd176 100644 --- a/go.sum +++ b/go.sum @@ -288,6 +288,7 @@ github.com/cosmos/cosmos-sdk/errors v1.0.0-beta.2/go.mod h1:Gi7pzVRnvZ1N16JAXpLA github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= +github.com/cosmos/gorocksdb v1.2.0/go.mod h1:aaKvKItm514hKfNJpUJXnnOWeBnk2GL4+Qw9NHizILw= github.com/cosmos/iavl v0.17.3 h1:s2N819a2olOmiauVa0WAhoIJq9EhSXE9HDBAoR9k+8Y= github.com/cosmos/iavl v0.17.3/go.mod h1:prJoErZFABYZGDHka1R6Oay4z9PrNeFFiMKHDAMOi4w= github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76 h1:DdzS1m6o/pCqeZ8VOAit/gyATedRgjvkVI+UCrLpyuU= @@ -323,6 +324,8 @@ github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFM github.com/dgraph-io/badger/v2 v2.2007.2/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= +github.com/dgraph-io/badger/v3 v3.2103.2 h1:dpyM5eCJAtQCBcMCZcT4UBZchuTJgCywerHHgmxfxM8= +github.com/dgraph-io/badger/v3 v3.2103.2/go.mod h1:RHo4/GmYcKKh5Lxu63wLEMHJ70Pac2JqZRYGhlyAo2M= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.0.3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI= @@ -535,6 +538,9 @@ github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9 github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg= github.com/google/certificate-transparency-go v1.1.1/go.mod h1:FDKqPvSXawb2ecErVRrD+nfy23RCzyl7eqVCEmlT1Zs= github.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/flatbuffers v2.0.0+incompatible h1:dicJ2oXwypfwUGnB2/TYWYEKiuk9eYQlQO/AnOHl5mI= +github.com/google/flatbuffers v2.0.0+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -1653,6 +1659,7 @@ golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211105183446-c75c47738b0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211113001501-0c823b97ae02/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/server/mock/store.go b/server/mock/store.go index aa17399cd885..46c7de4b8eea 100644 --- a/server/mock/store.go +++ b/server/mock/store.go @@ -91,6 +91,10 @@ func (ms multiStore) GetVersion(int64) (storetypes.MultiStore, error) { panic("not implemented") } +func (ms multiStore) GetAllVersions() []int { + panic("not implemented") +} + func (ms multiStore) Close() error { panic("not implemented") } From 83cd10947cd3db1d1a449b569f6bb25b8b45c38b Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Fri, 18 Feb 2022 15:18:54 +0800 Subject: [PATCH 25/78] patches --- baseapp/baseapp_test.go | 2 +- store/v2/multi/store_test.go | 2 +- x/group/keeper/invariants_test.go | 2 +- x/params/types/subspace_test.go | 4 ++-- x/upgrade/types/storeloader_test.go | 24 ++++++++++++------------ 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index e6b4d6e924fa..b245e6084705 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -1730,7 +1730,7 @@ func TestGRPCQueryPulsar(t *testing.T) { ) } - app := setupBaseApp(t, grpcQueryOpt) + app := setupBaseApp(t, baseapp.AppOptionFunc(grpcQueryOpt)) app.GRPCQueryRouter().SetInterfaceRegistry(codectypes.NewInterfaceRegistry()) app.InitChain(abci.RequestInitChain{}) diff --git a/store/v2/multi/store_test.go b/store/v2/multi/store_test.go index 654d80f03a6a..6affd2d5b5f0 100644 --- a/store/v2/multi/store_test.go +++ b/store/v2/multi/store_test.go @@ -950,7 +950,7 @@ func TestTrace(t *testing.T) { func TestTraceConcurrency(t *testing.T) { db := memdb.NewDB() - opts := storeConfig123(t) + opts := storeParams123(t) store, err := NewStore(db, opts) require.NoError(t, err) diff --git a/x/group/keeper/invariants_test.go b/x/group/keeper/invariants_test.go index 9b430dfb4df7..762bd31e6c37 100644 --- a/x/group/keeper/invariants_test.go +++ b/x/group/keeper/invariants_test.go @@ -41,7 +41,7 @@ func (s *invariantTestSuite) SetupSuite() { key := sdk.NewKVStoreKey(group.ModuleName) db := memdb.NewDB() config := multi.DefaultStoreParams() - s.Require().NoError(config.RegisterSubstore(key.Name(), storetypes.StoreTypePersistent)) + s.Require().NoError(config.RegisterSubstore(key, storetypes.StoreTypePersistent)) ms, err := multi.NewV1MultiStoreAsV2(db, config) s.Require().NoError(err) sdkCtx := sdk.NewContext(ms, tmproto.Header{}, false, log.NewNopLogger()) diff --git a/x/params/types/subspace_test.go b/x/params/types/subspace_test.go index 6039f26bfc18..00c965869f99 100644 --- a/x/params/types/subspace_test.go +++ b/x/params/types/subspace_test.go @@ -32,8 +32,8 @@ func (suite *SubspaceTestSuite) SetupTest() { db := memdb.NewDB() config := multi.DefaultStoreParams() - suite.NoError(config.RegisterSubstore(key.Name(), storetypes.StoreTypePersistent)) - suite.NoError(config.RegisterSubstore(tkey.Name(), storetypes.StoreTypeTransient)) + suite.NoError(config.RegisterSubstore(key, storetypes.StoreTypePersistent)) + suite.NoError(config.RegisterSubstore(tkey, storetypes.StoreTypeTransient)) ms, err := multi.NewV1MultiStoreAsV2(db, config) suite.NoError(err) diff --git a/x/upgrade/types/storeloader_test.go b/x/upgrade/types/storeloader_test.go index 898c20e16725..dfc89db8f45b 100644 --- a/x/upgrade/types/storeloader_test.go +++ b/x/upgrade/types/storeloader_test.go @@ -28,8 +28,7 @@ func defaultLogger() log.Logger { } } -func initStore(t *testing.T, db dbm.DBConnection, config multi.StoreParams, storeKey string, k, v []byte) { - key := sdk.NewKVStoreKey(storeKey) +func initStore(t *testing.T, db dbm.DBConnection, config multi.StoreParams, key storetypes.StoreKey, k, v []byte) { rs, err := multi.NewV1MultiStoreAsV2(db, config) require.NoError(t, err) rs.SetPruning(storetypes.PruneNothing) @@ -44,8 +43,7 @@ func initStore(t *testing.T, db dbm.DBConnection, config multi.StoreParams, stor require.NoError(t, rs.Close()) } -func checkStore(t *testing.T, db dbm.DBConnection, config multi.StoreParams, ver int64, storeKey string, k, v []byte) { - key := sdk.NewKVStoreKey(storeKey) +func checkStore(t *testing.T, db dbm.DBConnection, config multi.StoreParams, ver int64, key storetypes.StoreKey, k, v []byte) { rs, err := multi.NewV1MultiStoreAsV2(db, config) require.NoError(t, err) rs.SetPruning(storetypes.PruneNothing) @@ -84,15 +82,17 @@ func TestSetLoader(t *testing.T) { _, err = os.Stat(upgradeInfoFilePath) require.NoError(t, err) + fooKey := sdk.NewKVStoreKey("foo") + barKey := sdk.NewKVStoreKey("bar") cases := map[string]struct { setLoader baseapp.AppOption - origStoreKey string - loadStoreKey string + origStoreKey storetypes.StoreKey + loadStoreKey storetypes.StoreKey }{ "don't set loader": { setLoader: nil, - origStoreKey: "foo", - loadStoreKey: "foo", + origStoreKey: fooKey, + loadStoreKey: fooKey, }, "rename with inline opts": { setLoader: UpgradeStoreOption(uint64(upgradeHeight), &storetypes.StoreUpgrades{ @@ -101,8 +101,8 @@ func TestSetLoader(t *testing.T) { NewKey: "bar", }}, }), - origStoreKey: "foo", - loadStoreKey: "bar", + origStoreKey: fooKey, + loadStoreKey: barKey, }, } @@ -124,7 +124,7 @@ func TestSetLoader(t *testing.T) { // load the app with the existing db opts := []baseapp.AppOption{ baseapp.SetPruning(storetypes.PruneNothing), - baseapp.SetSubstores(sdk.NewKVStoreKey(tc.origStoreKey)), + baseapp.SetSubstores(tc.origStoreKey), } origapp := baseapp.NewBaseApp(t.Name(), defaultLogger(), db, opts...) require.NoError(t, origapp.Init()) @@ -139,7 +139,7 @@ func TestSetLoader(t *testing.T) { // load the new app with the original app db opts = []baseapp.AppOption{ baseapp.SetPruning(storetypes.PruneNothing), - baseapp.SetSubstores(sdk.NewKVStoreKey(tc.loadStoreKey)), + baseapp.SetSubstores(tc.loadStoreKey), } if tc.setLoader != nil { opts = append(opts, tc.setLoader) From 71a9bb9f2b85c450ea06bd5c2885d4f87c39d425 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Fri, 18 Feb 2022 17:35:15 +0800 Subject: [PATCH 26/78] fix v1asv2 commit --- internal/db/legacy_adapter.go | 44 +++++++++++++++++++----------- store/v2/multi/v1asv2.go | 51 ++++++++++++++++++++--------------- 2 files changed, 59 insertions(+), 36 deletions(-) diff --git a/internal/db/legacy_adapter.go b/internal/db/legacy_adapter.go index 1b6f5b7d81e3..733b407d84c9 100644 --- a/internal/db/legacy_adapter.go +++ b/internal/db/legacy_adapter.go @@ -13,25 +13,26 @@ import ( // // This serves as a transitional step in introducing the new DB interface while maintaining // compatibility with existing code that expects the old interface. -type tmdbAdapter struct { +type TmdbAdapter struct { dbm.DBReadWriter db dbm.DBConnection } type tmdbBatchAdapter struct { - *tmdbAdapter + *TmdbAdapter written bool } var ( - _ tmdb.DB = (*tmdbAdapter)(nil) + _ tmdb.DB = (*TmdbAdapter)(nil) ) // ConnectionAsTmdb returns a tmdb.DB which wraps a DBConnection. -func ConnectionAsTmdb(db dbm.DBConnection) *tmdbAdapter { return &tmdbAdapter{db.ReadWriter(), db} } +func ConnectionAsTmdb(db dbm.DBConnection) *TmdbAdapter { return &TmdbAdapter{db.ReadWriter(), db} } -func (d *tmdbAdapter) Close() error { return d.db.Close() } +func (d *TmdbAdapter) Close() error { d.CloseTx(); return d.db.Close() } +func (d *TmdbAdapter) CloseTx() error { return d.DBReadWriter.Discard() } -func (d *tmdbAdapter) sync() error { +func (d *TmdbAdapter) sync() error { err := d.DBReadWriter.Commit() if err != nil { return err @@ -39,14 +40,14 @@ func (d *tmdbAdapter) sync() error { d.DBReadWriter = d.db.ReadWriter() return nil } -func (d *tmdbAdapter) DeleteSync(k []byte) error { +func (d *TmdbAdapter) DeleteSync(k []byte) error { err := d.DBReadWriter.Delete(k) if err != nil { return err } return d.sync() } -func (d *tmdbAdapter) SetSync(k, v []byte) error { +func (d *TmdbAdapter) SetSync(k, v []byte) error { err := d.DBReadWriter.Set(k, v) if err != nil { return err @@ -54,14 +55,27 @@ func (d *tmdbAdapter) SetSync(k, v []byte) error { return d.sync() } -func (d *tmdbAdapter) Iterator(s, e []byte) (tmdb.Iterator, error) { +func (d *TmdbAdapter) Commit() (uint64, error) { + err := d.DBReadWriter.Commit() + if err != nil { + return 0, err + } + v, err := d.db.SaveNextVersion() + if err != nil { + return 0, err + } + d.DBReadWriter = d.db.ReadWriter() + return v, err +} + +func (d *TmdbAdapter) Iterator(s, e []byte) (tmdb.Iterator, error) { it, err := d.DBReadWriter.Iterator(s, e) if err != nil { return nil, err } return DBToStoreIterator(it), nil } -func (d *tmdbAdapter) ReverseIterator(s, e []byte) (tmdb.Iterator, error) { +func (d *TmdbAdapter) ReverseIterator(s, e []byte) (tmdb.Iterator, error) { it, err := d.DBReadWriter.ReverseIterator(s, e) if err != nil { return nil, err @@ -70,23 +84,23 @@ func (d *tmdbAdapter) ReverseIterator(s, e []byte) (tmdb.Iterator, error) { } // NewBatch returns a tmdb.Batch which wraps a DBWriter. -func (d *tmdbAdapter) NewBatch() tmdb.Batch { +func (d *TmdbAdapter) NewBatch() tmdb.Batch { return &tmdbBatchAdapter{d, false} } -func (d *tmdbAdapter) Print() error { return nil } -func (d *tmdbAdapter) Stats() map[string]string { return nil } +func (d *TmdbAdapter) Print() error { return nil } +func (d *TmdbAdapter) Stats() map[string]string { return nil } func (d *tmdbBatchAdapter) Set(k, v []byte) error { if d.written { return errors.New("Batch already written") } - return d.tmdbAdapter.Set(k, v) + return d.TmdbAdapter.Set(k, v) } func (d *tmdbBatchAdapter) Delete(k []byte) error { if d.written { return errors.New("Batch already written") } - return d.tmdbAdapter.Delete(k) + return d.TmdbAdapter.Delete(k) } func (d *tmdbBatchAdapter) WriteSync() error { if d.written { diff --git a/store/v2/multi/v1asv2.go b/store/v2/multi/v1asv2.go index 3bc7fc772f62..ddbe3994ae2e 100644 --- a/store/v2/multi/v1asv2.go +++ b/store/v2/multi/v1asv2.go @@ -11,13 +11,14 @@ import ( ) var ( - _ v2.CommitMultiStore = store1as2{} - _ v2.Queryable = store1as2{} + _ v2.CommitMultiStore = (*store1as2)(nil) + _ v2.Queryable = (*store1as2)(nil) _ v2.CacheMultiStore = cacheStore1as2{} ) type store1as2 struct { *rootmulti.Store + database *dbutil.TmdbAdapter } type cacheStore1as2 struct { @@ -26,7 +27,8 @@ type cacheStore1as2 struct { // NewV1MultiStoreAsV2 constructs a v1 CommitMultiStore from v2.StoreParams func NewV1MultiStoreAsV2(database db.DBConnection, opts StoreParams) (v2.CommitMultiStore, error) { - cms := rootmulti.NewStore(dbutil.ConnectionAsTmdb(database)) + adapter := dbutil.ConnectionAsTmdb(database) + cms := rootmulti.NewStore(adapter) for name, typ := range opts.StoreSchema { switch typ { case v2.StoreTypePersistent: @@ -52,37 +54,44 @@ func NewV1MultiStoreAsV2(database db.DBConnection, opts StoreParams) (v2.CommitM } cms.SetTracer(opts.TraceWriter) cms.SetTracingContext(opts.TraceContext) - return store1as2{cms}, nil + return &store1as2{cms, adapter}, nil } -func WrapStoreAsV2CommitMultiStore(s *rootmulti.Store) v2.CommitMultiStore { - return store1as2{s} -} - -func WrapCacheStoreAsV2CacheMultiStore(cs v1.CacheMultiStore) v2.CacheMultiStore { - return cacheStore1as2{cs} -} +// MultiStore -// commit store - -func (s store1as2) CacheWrap() v2.CacheMultiStore { +func (s *store1as2) CacheWrap() v2.CacheMultiStore { return cacheStore1as2{s.CacheMultiStore()} } -func (s store1as2) GetVersion(ver int64) (v2.MultiStore, error) { +func (s *store1as2) GetVersion(ver int64) (v2.MultiStore, error) { ret, err := s.CacheMultiStoreWithVersion(ver) return cacheStore1as2{ret}, err } -func (s store1as2) Close() error { return nil } -func (s store1as2) SetInitialVersion(ver uint64) error { + +// CommitMultiStore + +func (s *store1as2) Close() error { + return s.database.CloseTx() +} + +func (s *store1as2) Commit() v2.CommitID { + ret := s.Store.Commit() + _, err := s.database.Commit() + if err != nil { + panic(err) + } + return ret +} + +func (s *store1as2) SetInitialVersion(ver uint64) error { return s.Store.SetInitialVersion(int64(ver)) } -func (s store1as2) SetTracer(w io.Writer) { s.SetTracer(w) } -func (s store1as2) SetTracingContext(tc v2.TraceContext) { s.SetTracingContext(tc) } +func (s *store1as2) SetTracer(w io.Writer) { s.SetTracer(w) } +func (s *store1as2) SetTracingContext(tc v2.TraceContext) { s.SetTracingContext(tc) } -func (s store1as2) GetAllVersions() []int { panic("unsupported: GetAllVersions") } +func (s *store1as2) GetAllVersions() []int { panic("unsupported: GetAllVersions") } -// cache store +// CacheMultiStore func (s cacheStore1as2) CacheWrap() v2.CacheMultiStore { return cacheStore1as2{s.CacheMultiStore.CacheMultiStore()} From 78b0a12060f4924a608dfd840aebbb2d9f8a2917 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Wed, 23 Feb 2022 13:54:42 +0800 Subject: [PATCH 27/78] PR revisions --- baseapp/baseapp.go | 4 +--- baseapp/options.go | 9 +++++---- baseapp/test_helpers.go | 1 + internal/db/{legacy_adapter.go => store_v1_adapter.go} | 0 simapp/app.go | 3 ++- simapp/export.go | 3 +++ simapp/sim_test.go | 6 +++--- x/upgrade/types/storeloader.go | 3 ++- 8 files changed, 17 insertions(+), 12 deletions(-) rename internal/db/{legacy_adapter.go => store_v1_adapter.go} (100%) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 54dc15a1efe5..f584ba84d8e2 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -35,15 +35,13 @@ type ( // Enum mode for app.runTx runTxMode uint8 - // StoreParams is an alias for the config parameter type of the CommitMultiStore. - StoreParams = multi.StoreParams // StoreOption provides a functional callback to modify StoreParams. // The callback is passed the loaded height as uint64. // This can be used to control how we load the CommitMultiStore from disk. This is useful for // state migration, when loading a datastore written with an older version of the software. // In particular, if a module changed the substore key name (or removed a substore) between // two versions of the software. - StoreOption func(*StoreParams, uint64) error + StoreOption func(*multi.StoreParams, uint64) error // AppOption provides a configuration option for a BaseApp AppOption interface { diff --git a/baseapp/options.go b/baseapp/options.go index 6f92c164eacc..49ea81aca67d 100644 --- a/baseapp/options.go +++ b/baseapp/options.go @@ -7,6 +7,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/snapshots" storetypes "github.com/cosmos/cosmos-sdk/store/v2" + "github.com/cosmos/cosmos-sdk/store/v2/multi" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/tx" ) @@ -16,7 +17,7 @@ import ( // SetPruning sets a pruning option on the multistore associated with the app func SetPruning(opts sdk.PruningOptions) StoreOption { - return func(config *StoreParams, _ uint64) error { config.Pruning = opts; return nil } + return func(config *multi.StoreParams, _ uint64) error { config.Pruning = opts; return nil } } // SetMinGasPrices returns an option that sets the minimum gas prices on the app. @@ -59,7 +60,7 @@ func SetIndexEvents(ie []string) AppOptionFunc { // SetInterBlockCache provides a BaseApp option function that sets the // inter-block cache. func SetInterBlockCache(cache sdk.MultiStorePersistentCache) AppOptionFunc { - opt := func(cfg *StoreParams, v uint64) error { + opt := func(cfg *multi.StoreParams, v uint64) error { cfg.PersistentCache = cache return nil } @@ -86,7 +87,7 @@ func SetSnapshotStore(snapshotStore *snapshots.Store) AppOptionOrdered { // SetSubstores registers substores according to app configuration func SetSubstores(keys ...storetypes.StoreKey) StoreOption { - return func(config *StoreParams, _ uint64) error { + return func(config *multi.StoreParams, _ uint64) error { for _, key := range keys { typ, err := storetypes.StoreKeyToType(key) if err != nil { @@ -189,7 +190,7 @@ func (app *BaseApp) SetFauxMerkleMode() { // SetCommitMultiStoreTracer sets the store tracer on the BaseApp's underlying // CommitMultiStore. func (app *BaseApp) SetCommitMultiStoreTracer(w io.Writer) { - opt := func(cfg *StoreParams, v uint64) error { + opt := func(cfg *multi.StoreParams, v uint64) error { cfg.TraceWriter = w return nil } diff --git a/baseapp/test_helpers.go b/baseapp/test_helpers.go index 37ba5588848c..53ca7ebc8c48 100644 --- a/baseapp/test_helpers.go +++ b/baseapp/test_helpers.go @@ -93,6 +93,7 @@ func (app *BaseApp) NewUncachedContext(isCheckTx bool, header tmproto.Header) sd return sdk.NewContext(multi.CommitAsCacheStore(app.store), header, isCheckTx, app.logger) } +// NewContextAt creates a context using a (read-only) store at a given block height. func (app *BaseApp) NewContextAt(isCheckTx bool, header tmproto.Header, height int64) (sdk.Context, error) { view, err := app.store.GetVersion(height) if err != nil { diff --git a/internal/db/legacy_adapter.go b/internal/db/store_v1_adapter.go similarity index 100% rename from internal/db/legacy_adapter.go rename to internal/db/store_v1_adapter.go diff --git a/simapp/app.go b/simapp/app.go index 7691a8c0a85c..196795e6c22a 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -30,6 +30,7 @@ import ( simappparams "github.com/cosmos/cosmos-sdk/simapp/params" "github.com/cosmos/cosmos-sdk/store/streaming" storetypes "github.com/cosmos/cosmos-sdk/store/v2" + "github.com/cosmos/cosmos-sdk/store/v2/multi" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/version" @@ -232,7 +233,7 @@ func NewSimApp( // not include this key. memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey, "testingkey") // initialize stores - setNamespaces := func(config *baseapp.StoreParams, ver uint64) error { + setNamespaces := func(config *multi.StoreParams, ver uint64) error { for _, key := range keys { typ, err := storetypes.StoreKeyToType(key) if err != nil { diff --git a/simapp/export.go b/simapp/export.go index 5eaaddca5725..d2cf26aab86d 100644 --- a/simapp/export.go +++ b/simapp/export.go @@ -21,6 +21,9 @@ func (app *SimApp) ExportAppStateAndValidators( return app.ExportAppStateAndValidatorsAt(forZeroHeight, jailAllowedAddrs, 0) } +// ExportAppStateAndValidatorsAt exports the application state at a given block +// height for a genesis file. +// Passing a height < 1 will export for the latest block height. func (app *SimApp) ExportAppStateAndValidatorsAt( forZeroHeight bool, jailAllowedAddrs []string, height int64, ) (servertypes.ExportedApp, error) { diff --git a/simapp/sim_test.go b/simapp/sim_test.go index f4efd28c9f1f..39ccdd1c99f4 100644 --- a/simapp/sim_test.go +++ b/simapp/sim_test.go @@ -331,9 +331,9 @@ func TestAppStateDeterminism(t *testing.T) { ) require.NoError(t, err) - // if config.Commit { - // PrintStats(db)//TODO - // } + if config.Commit { + PrintStats(db) //TODO + } appHash := app.LastCommitID().Hash appHashList[j] = appHash diff --git a/x/upgrade/types/storeloader.go b/x/upgrade/types/storeloader.go index 0c918e3ef3f3..7ed4aadbdbe8 100644 --- a/x/upgrade/types/storeloader.go +++ b/x/upgrade/types/storeloader.go @@ -3,12 +3,13 @@ package types import ( "github.com/cosmos/cosmos-sdk/baseapp" storetypes "github.com/cosmos/cosmos-sdk/store/v2" + "github.com/cosmos/cosmos-sdk/store/v2/multi" ) // UpgradeStoreOption is used to prepare baseapp with a fixed StoreOption. // This is useful for custom upgrade loading logic. func UpgradeStoreOption(upgradeHeight uint64, storeUpgrades *storetypes.StoreUpgrades) baseapp.StoreOption { - return func(par *baseapp.StoreParams, loadHeight uint64) error { + return func(par *multi.StoreParams, loadHeight uint64) error { // Check if the current commit version and upgrade height matches if upgradeHeight == loadHeight+1 { if len(storeUpgrades.Renamed) > 0 || len(storeUpgrades.Deleted) > 0 || len(storeUpgrades.Added) > 0 { From ddf8d0f6e710cbeccf87922eabc593320afe67ae Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Wed, 23 Feb 2022 23:36:06 +0800 Subject: [PATCH 28/78] fix simd export --- simapp/simd/cmd/root.go | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/simapp/simd/cmd/root.go b/simapp/simd/cmd/root.go index ff24164d0044..1a247af3b93b 100644 --- a/simapp/simd/cmd/root.go +++ b/simapp/simd/cmd/root.go @@ -302,20 +302,14 @@ func (a appCreator) appExport( logger log.Logger, db dbm.DBConnection, traceStore io.Writer, height int64, forZeroHeight bool, jailAllowedAddrs []string, appOpts servertypes.AppOptions) (servertypes.ExportedApp, error) { - var simApp *simapp.SimApp homePath, ok := appOpts.Get(flags.FlagHome).(string) if !ok || homePath == "" { return servertypes.ExportedApp{}, errors.New("application home not set") } + simApp := simapp.NewSimApp(logger, db, traceStore, false, map[int64]bool{}, homePath, uint(1), a.encCfg, appOpts) if height != -1 { - simApp = simapp.NewSimApp(logger, db, traceStore, false, map[int64]bool{}, homePath, uint(1), a.encCfg, appOpts) - - if err := simApp.LoadHeight(height); err != nil { - return servertypes.ExportedApp{}, err - } - } else { - simApp = simapp.NewSimApp(logger, db, traceStore, true, map[int64]bool{}, homePath, uint(1), a.encCfg, appOpts) + return simApp.ExportAppStateAndValidatorsAt(forZeroHeight, jailAllowedAddrs, height) } return simApp.ExportAppStateAndValidators(forZeroHeight, jailAllowedAddrs) From 3ddf675302808677a59fe86f6aee54d65e439e10 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Wed, 23 Feb 2022 23:37:30 +0800 Subject: [PATCH 29/78] open db at data/application/ --- server/util.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/util.go b/server/util.go index 2d6f415b4014..a88a1e4a2da2 100644 --- a/server/util.go +++ b/server/util.go @@ -373,7 +373,8 @@ func addrToIP(addr net.Addr) net.IP { } func openDB(rootDir string) (dbm.DBConnection, error) { - return badgerdb.NewDB(filepath.Join(rootDir, "data")) + dir := filepath.Join(rootDir, "data", "application") + return badgerdb.NewDB(dir) } func openTraceWriter(traceWriterFile string) (w io.Writer, err error) { From def410762c5b157c4faa4f864f9c95510f3f289b Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Wed, 23 Feb 2022 23:40:46 +0800 Subject: [PATCH 30/78] db cleanup --- db/badgerdb/db.go | 14 ++++++++------ db/dbtest/testcases.go | 28 +++++++++++++--------------- db/version_manager.go | 1 - internal/db/store_v1_adapter.go | 27 ++++++++++++--------------- 4 files changed, 33 insertions(+), 37 deletions(-) diff --git a/db/badgerdb/db.go b/db/badgerdb/db.go index 6f0fb1fb3c41..e25a83196425 100644 --- a/db/badgerdb/db.go +++ b/db/badgerdb/db.go @@ -113,6 +113,7 @@ func readVersionsFile(path string) (*versionManager, error) { if err != nil { return nil, err } + file.Close() var ( versions []uint64 lastTs uint64 @@ -129,6 +130,7 @@ func readVersionsFile(path string) (*versionManager, error) { } if version == 0 { // 0 maps to the latest timestamp lastTs = ts + continue } versions = append(versions, version) vmap[version] = ts @@ -143,12 +145,6 @@ func readVersionsFile(path string) (*versionManager, error) { // Write version metadata to CSV file func writeVersionsFile(vm *versionManager, path string) error { - file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0644) - if err != nil { - return err - } - defer file.Close() - w := csv.NewWriter(file) rows := [][]string{ []string{"0", strconv.FormatUint(vm.lastTs, 10)}, } @@ -163,6 +159,12 @@ func writeVersionsFile(vm *versionManager, path string) error { strconv.FormatUint(ts, 10), }) } + file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0644) + if err != nil { + return err + } + defer file.Close() + w := csv.NewWriter(file) return w.WriteAll(rows) } diff --git a/db/dbtest/testcases.go b/db/dbtest/testcases.go index dde135fa36e1..8b219f362e39 100644 --- a/db/dbtest/testcases.go +++ b/db/dbtest/testcases.go @@ -140,19 +140,19 @@ func DoTestIterators(t *testing.T, load Loader) { } require.NoError(t, txn.Commit()) - testRange := func(t *testing.T, iter dbm.Iterator, expected []string) { + type testCase struct { + start, end []byte + expected []string + } + testRange := func(t *testing.T, iter dbm.Iterator, tc testCase) { i := 0 for ; iter.Next(); i++ { - expectedValue := expected[i] + expectedValue := tc.expected[i] value := iter.Value() - require.Equal(t, expectedValue, string(value), "i=%v", i) + require.Equal(t, expectedValue, string(value), + "i=%v case=[[%x] [%x])", i, tc.start, tc.end) } - require.Equal(t, len(expected), i) - } - - type testCase struct { - start, end []byte - expected []string + require.Equal(t, len(tc.expected), i) } view := db.Reader() @@ -165,11 +165,10 @@ func DoTestIterators(t *testing.T, load Loader) { {[]byte{0x00, 0x01}, []byte{0x01}, []string{"0 1", "0 2"}}, {nil, []byte{0x01}, []string{"0", "0 0", "0 1", "0 2"}}, } - for i, tc := range iterCases { - t.Logf("Iterator case %d: [%v, %v)", i, tc.start, tc.end) + for _, tc := range iterCases { it, err := view.Iterator(tc.start, tc.end) require.NoError(t, err) - testRange(t, it, tc.expected) + testRange(t, it, tc) it.Close() } @@ -181,11 +180,10 @@ func DoTestIterators(t *testing.T, load Loader) { {[]byte{0x00, 0x01}, []byte{0x01}, []string{"0 2", "0 1"}}, {nil, []byte{0x01}, []string{"0 2", "0 1", "0 0", "0"}}, } - for i, tc := range reverseCases { - t.Logf("ReverseIterator case %d: [%v, %v)", i, tc.start, tc.end) + for _, tc := range reverseCases { it, err := view.ReverseIterator(tc.start, tc.end) require.NoError(t, err) - testRange(t, it, tc.expected) + testRange(t, it, tc) it.Close() } diff --git a/db/version_manager.go b/db/version_manager.go index b884e7160114..e9cbf01185e7 100644 --- a/db/version_manager.go +++ b/db/version_manager.go @@ -55,7 +55,6 @@ func (vm *VersionManager) Save(target uint64) (uint64, error) { if _, has := vm.versions[target]; has { return 0, fmt.Errorf("version exists: %v", target) } - vm.versions[target] = struct{}{} vm.last = target if len(vm.versions) == 1 { diff --git a/internal/db/store_v1_adapter.go b/internal/db/store_v1_adapter.go index 733b407d84c9..a36f5d023188 100644 --- a/internal/db/store_v1_adapter.go +++ b/internal/db/store_v1_adapter.go @@ -19,7 +19,7 @@ type TmdbAdapter struct { } type tmdbBatchAdapter struct { *TmdbAdapter - written bool + closed bool } var ( @@ -90,29 +90,26 @@ func (d *TmdbAdapter) NewBatch() tmdb.Batch { func (d *TmdbAdapter) Print() error { return nil } func (d *TmdbAdapter) Stats() map[string]string { return nil } +var errClosed = errors.New("batch is closed") + func (d *tmdbBatchAdapter) Set(k, v []byte) error { - if d.written { - return errors.New("Batch already written") + if d.closed { + return errClosed } return d.TmdbAdapter.Set(k, v) } func (d *tmdbBatchAdapter) Delete(k []byte) error { - if d.written { - return errors.New("Batch already written") + if d.closed { + return errClosed } return d.TmdbAdapter.Delete(k) } func (d *tmdbBatchAdapter) WriteSync() error { - if d.written { - return errors.New("Batch already written") + if d.closed { + return errClosed } - d.written = true + d.closed = true return d.sync() } -func (d *tmdbBatchAdapter) Write() error { - if d.written { - return errors.New("Batch already written") - } - return d.WriteSync() -} -func (d *tmdbBatchAdapter) Close() error { return nil } +func (d *tmdbBatchAdapter) Write() error { return d.WriteSync() } +func (d *tmdbBatchAdapter) Close() error { d.closed = true; return nil } From 880a4eeaf0a2e9fb0247941083e81e7e5b7acc03 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Wed, 23 Feb 2022 23:44:29 +0800 Subject: [PATCH 31/78] badgerdb fix write versions immediately, not on close --- db/badgerdb/db.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/db/badgerdb/db.go b/db/badgerdb/db.go index e25a83196425..b741e88f9eb6 100644 --- a/db/badgerdb/db.go +++ b/db/badgerdb/db.go @@ -201,7 +201,10 @@ func (b *BadgerDB) Writer() db.DBWriter { func (b *BadgerDB) Close() error { b.mtx.Lock() defer b.mtx.Unlock() - writeVersionsFile(b.vmgr, filepath.Join(b.db.Opts().Dir, versionsFilename)) + err := writeVersionsFile(b.vmgr, filepath.Join(b.db.Opts().Dir, versionsFilename)) + if err != nil { + return err + } return b.db.Close() } @@ -220,7 +223,11 @@ func (b *BadgerDB) save(target uint64) (uint64, error) { return 0, db.ErrOpenTransactions } b.vmgr = b.vmgr.Copy() - return b.vmgr.Save(target) + v, err := b.vmgr.Save(target) + if err != nil { + return v, err + } + return v, writeVersionsFile(b.vmgr, filepath.Join(b.db.Opts().Dir, versionsFilename)) } // SaveNextVersion implements DBConnection. @@ -245,7 +252,7 @@ func (b *BadgerDB) DeleteVersion(target uint64) error { } b.vmgr = b.vmgr.Copy() b.vmgr.Delete(target) - return nil + return writeVersionsFile(b.vmgr, filepath.Join(b.db.Opts().Dir, versionsFilename)) } func (b *BadgerDB) Revert() error { From 332c3e95fe39344c592b2a58d98632ba68b972e9 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Wed, 2 Mar 2022 16:43:20 +0800 Subject: [PATCH 32/78] change rocksdb build tag to "rocksdb" --- Makefile | 4 ++-- db/rocksdb/batch.go | 2 +- db/rocksdb/db.go | 2 +- db/rocksdb/db_test.go | 2 +- db/rocksdb/iterator.go | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 3ef8241658f6..aaa817f1ec6a 100644 --- a/Makefile +++ b/Makefile @@ -65,8 +65,8 @@ ldflags = -X github.com/cosmos/cosmos-sdk/version.Name=sim \ -X github.com/tendermint/tendermint/version.TMCoreSemVer=$(TMVERSION) ifeq ($(ENABLE_ROCKSDB),true) - BUILD_TAGS += rocksdb_build - test_tags += rocksdb_build + BUILD_TAGS += rocksdb + test_tags += rocksdb else $(warning RocksDB support is disabled; to build and test with RocksDB support, set ENABLE_ROCKSDB=true) endif diff --git a/db/rocksdb/batch.go b/db/rocksdb/batch.go index 7e19cae46d68..69e081f169ee 100644 --- a/db/rocksdb/batch.go +++ b/db/rocksdb/batch.go @@ -1,4 +1,4 @@ -//go:build rocksdb_build +//go:build rocksdb package rocksdb diff --git a/db/rocksdb/db.go b/db/rocksdb/db.go index cb312b625f03..227a73b0600a 100644 --- a/db/rocksdb/db.go +++ b/db/rocksdb/db.go @@ -1,4 +1,4 @@ -//go:build rocksdb_build +//go:build rocksdb package rocksdb diff --git a/db/rocksdb/db_test.go b/db/rocksdb/db_test.go index b6268c1ed586..20983dac120f 100644 --- a/db/rocksdb/db_test.go +++ b/db/rocksdb/db_test.go @@ -1,4 +1,4 @@ -//go:build rocksdb_build +//go:build rocksdb package rocksdb diff --git a/db/rocksdb/iterator.go b/db/rocksdb/iterator.go index e760c7507ed5..cf174019e1c1 100644 --- a/db/rocksdb/iterator.go +++ b/db/rocksdb/iterator.go @@ -1,4 +1,4 @@ -//go:build rocksdb_build +//go:build rocksdb package rocksdb From d7a51f5beb6387edf226038e57f3c5a66fae3a28 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Wed, 2 Mar 2022 16:44:23 +0800 Subject: [PATCH 33/78] badgerdb - handle ErrTxnTooBig --- db/badgerdb/db.go | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/db/badgerdb/db.go b/db/badgerdb/db.go index b741e88f9eb6..5982f7ea2399 100644 --- a/db/badgerdb/db.go +++ b/db/badgerdb/db.go @@ -368,14 +368,34 @@ func (tx *badgerWriter) Set(key, value []byte) error { if err := dbutil.ValidateKv(key, value); err != nil { return err } - return tx.txn.Set(key, value) + err := tx.txn.Set(key, value) + if errors.Is(err, badger.ErrTxnTooBig) { + err = tx.Commit() + if err != nil { + return err + } + newtx := tx.db.ReadWriter().(*badgerWriter) + *tx = *newtx + err = tx.txn.Set(key, value) + } + return err } func (tx *badgerWriter) Delete(key []byte) error { if len(key) == 0 { return db.ErrKeyEmpty } - return tx.txn.Delete(key) + err := tx.txn.Delete(key) + if errors.Is(err, badger.ErrTxnTooBig) { + err = tx.Commit() + if err != nil { + return err + } + newtx := tx.db.ReadWriter().(*badgerWriter) + *tx = *newtx + err = tx.txn.Delete(key) + } + return err } func (tx *badgerWriter) Commit() (err error) { From 5102f9264dbfdc1ca0137646316ea6e0130b61e8 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Wed, 2 Mar 2022 16:59:26 +0800 Subject: [PATCH 34/78] remove multistore reverse index --- store/v2/multi/store.go | 3 --- store/v2/multi/store_test.go | 5 ----- store/v2/multi/sub_store.go | 8 -------- store/v2/multi/view_store.go | 2 -- 4 files changed, 18 deletions(-) diff --git a/store/v2/multi/store.go b/store/v2/multi/store.go index aba499047134..a79ea804233a 100644 --- a/store/v2/multi/store.go +++ b/store/v2/multi/store.go @@ -114,7 +114,6 @@ type substore struct { root *Store name string dataBucket dbm.DBReadWriter - indexBucket dbm.DBReadWriter stateCommitmentStore *smt.Store } @@ -493,7 +492,6 @@ func (rs *Store) getSubstore(key string) (*substore, error) { root: rs, name: key, dataBucket: prefixdb.NewPrefixReadWriter(stateRW, dataPrefix), - indexBucket: prefixdb.NewPrefixReadWriter(stateRW, indexPrefix), stateCommitmentStore: stateCommitmentStore, }, nil } @@ -504,7 +502,6 @@ func (s *substore) refresh(rootHash []byte) { stateRW := prefixdb.NewPrefixReadWriter(s.root.stateTxn, pfx) stateCommitmentRW := prefixdb.NewPrefixReadWriter(s.root.stateCommitmentTxn, pfx) s.dataBucket = prefixdb.NewPrefixReadWriter(stateRW, dataPrefix) - s.indexBucket = prefixdb.NewPrefixReadWriter(stateRW, indexPrefix) s.stateCommitmentStore = loadSMT(stateCommitmentRW, rootHash) } diff --git a/store/v2/multi/store_test.go b/store/v2/multi/store_test.go index 30f1048701ee..e43a6757d2fd 100644 --- a/store/v2/multi/store_test.go +++ b/store/v2/multi/store_test.go @@ -85,11 +85,6 @@ func TestGetSetHasDelete(t *testing.T) { require.Panics(t, func() { store.Set(nil, []byte("value")) }, "Set(nil key) should panic") require.Panics(t, func() { store.Set([]byte{}, []byte("value")) }, "Set(empty key) should panic") require.Panics(t, func() { store.Set([]byte("key"), nil) }, "Set(nil value) should panic") - sub := store.(*substore) - sub.indexBucket = rwCrudFails{sub.indexBucket, nil} - require.Panics(t, func() { - store.Set([]byte("key"), []byte("value")) - }, "Set() when index fails should panic") } func TestConstructors(t *testing.T) { diff --git a/store/v2/multi/sub_store.go b/store/v2/multi/sub_store.go index e6fb38c94350..efd05f63062d 100644 --- a/store/v2/multi/sub_store.go +++ b/store/v2/multi/sub_store.go @@ -1,7 +1,6 @@ package multi import ( - "crypto/sha256" "io" "sync" @@ -47,21 +46,14 @@ func (s *substore) Set(key, value []byte) { panic(err) } s.stateCommitmentStore.Set(key, value) - khash := sha256.Sum256(key) - err = s.indexBucket.Set(khash[:], key) - if err != nil { - panic(err) - } } // Delete implements KVStore. func (s *substore) Delete(key []byte) { - khash := sha256.Sum256(key) s.root.mtx.Lock() defer s.root.mtx.Unlock() s.stateCommitmentStore.Delete(key) - _ = s.indexBucket.Delete(khash[:]) _ = s.dataBucket.Delete(key) } diff --git a/store/v2/multi/view_store.go b/store/v2/multi/view_store.go index c3ea817522be..794c9d7e967e 100644 --- a/store/v2/multi/view_store.go +++ b/store/v2/multi/view_store.go @@ -29,7 +29,6 @@ type viewSubstore struct { root *viewStore name string dataBucket dbm.DBReader - indexBucket dbm.DBReader stateCommitmentStore *smt.Store } @@ -62,7 +61,6 @@ func (vs *viewStore) getSubstore(key string) (*viewSubstore, error) { root: vs, name: key, dataBucket: prefixdb.NewPrefixReader(stateR, dataPrefix), - indexBucket: prefixdb.NewPrefixReader(stateR, indexPrefix), stateCommitmentStore: loadSMT(dbm.ReaderAsReadWriter(stateCommitmentR), rootHash), }, nil } From 81642a595e685d252ac710acd826f32ee7b695d8 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Fri, 4 Mar 2022 18:14:35 +0800 Subject: [PATCH 35/78] rename tmdb adapter --- internal/db/{store_v1_adapter.go => tmdb_adapter.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename internal/db/{store_v1_adapter.go => tmdb_adapter.go} (100%) diff --git a/internal/db/store_v1_adapter.go b/internal/db/tmdb_adapter.go similarity index 100% rename from internal/db/store_v1_adapter.go rename to internal/db/tmdb_adapter.go From b33b1628f664874be7b0186b7796ad8f8742ea5b Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Sat, 5 Mar 2022 01:43:37 +0800 Subject: [PATCH 36/78] GetAllVersions patch not part of store interface; wrong type --- store/v2/multi/store.go | 8 ++++---- store/v2/types.go | 3 --- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/store/v2/multi/store.go b/store/v2/multi/store.go index a79ea804233a..a8b86de7e15d 100644 --- a/store/v2/multi/store.go +++ b/store/v2/multi/store.go @@ -678,16 +678,16 @@ func (rs *Store) CacheWrap() types.CacheMultiStore { return newCacheStore(rs) } -// GetAllVersions implements CommitMultiStore. +// GetAllVersions returns all available versions. // https://github.com/cosmos/cosmos-sdk/pull/11124 -func (rs *Store) GetAllVersions() []int { +func (rs *Store) GetAllVersions() []uint64 { vs, err := rs.stateDB.Versions() if err != nil { panic(err) } - var ret []int + var ret []uint64 for it := vs.Iterator(); it.Next(); { - ret = append(ret, int(it.Value())) + ret = append(ret, it.Value()) } return ret } diff --git a/store/v2/types.go b/store/v2/types.go index d8c6c4eb5a36..3fd04def2c78 100644 --- a/store/v2/types.go +++ b/store/v2/types.go @@ -101,9 +101,6 @@ type CommitMultiStore interface { Close() error // Defines the minimum version number that can be saved by this store. SetInitialVersion(uint64) error - // Gets all versions in the DB - // https://github.com/cosmos/cosmos-sdk/pull/11124 - GetAllVersions() []int } // CacheMultiStore defines a branch of the root state which can be written back to the source store. From df2e47ba8fd52e49aa4c3bd21fa6aabf9774921c Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Sat, 5 Mar 2022 23:41:40 +0800 Subject: [PATCH 37/78] db/adapter - return discard error --- db/adapter.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/db/adapter.go b/db/adapter.go index 66aeee7e309e..1add7a79cf56 100644 --- a/db/adapter.go +++ b/db/adapter.go @@ -18,6 +18,5 @@ func (readerRWAdapter) Delete([]byte) error { } func (rw readerRWAdapter) Commit() error { - rw.Discard() - return nil + return rw.Discard() } From 1dd7de8983b3367b4d7b15c0feb270301171439d Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Thu, 10 Mar 2022 20:57:33 +0800 Subject: [PATCH 38/78] multistore - minor additions test case smtstore.values can be readonly --- store/v2/multi/store_test.go | 20 +++++++++++++------- store/v2/smt/store.go | 2 +- store/v2/smt/store_test.go | 2 ++ 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/store/v2/multi/store_test.go b/store/v2/multi/store_test.go index e43a6757d2fd..3659bb616a49 100644 --- a/store/v2/multi/store_test.go +++ b/store/v2/multi/store_test.go @@ -222,19 +222,25 @@ func TestCommit(t *testing.T) { // Adding one record changes the hash s1 := store.GetKVStore(skey_1) s1.Set([]byte{0}, []byte{0}) - idOne := store.Commit() - require.Equal(t, idNew.Version+1, idOne.Version) - require.NotEqual(t, idNew.Hash, idOne.Hash) + id := store.Commit() + require.Equal(t, idNew.Version+1, id.Version) + require.NotEqual(t, idNew.Hash, id.Hash) // Hash of emptied store is same as new store s1.Delete([]byte{0}) - idEmptied := store.Commit() - require.Equal(t, idNew.Hash, idEmptied.Hash) + id = store.Commit() + require.Equal(t, idNew.Hash, id.Hash) - previd := idOne + // We can set and delete the same key within a transaction + s1.Set([]byte("might"), []byte("delete")) + s1.Delete([]byte("might")) + id = store.Commit() + require.Equal(t, idNew.Hash, id.Hash) + + previd := id for i := byte(1); i < 5; i++ { s1.Set([]byte{i}, []byte{i}) - id := store.Commit() + id = store.Commit() lastid := store.LastCommitID() require.Equal(t, id.Hash, lastid.Hash) require.Equal(t, id.Version, lastid.Version) diff --git a/store/v2/smt/store.go b/store/v2/smt/store.go index 265f6e77dbc6..b8fd11f565d6 100644 --- a/store/v2/smt/store.go +++ b/store/v2/smt/store.go @@ -30,7 +30,7 @@ var ( // Store Implements types.BasicKVStore. type Store struct { tree *smt.SparseMerkleTree - values dbm.DBReadWriter + values dbm.DBReader // Map hashed keys back to preimage preimages dbm.DBReadWriter } diff --git a/store/v2/smt/store_test.go b/store/v2/smt/store_test.go index e02ede93fcc9..e548e8f5dd81 100644 --- a/store/v2/smt/store_test.go +++ b/store/v2/smt/store_test.go @@ -19,6 +19,8 @@ func TestGetSetHasDelete(t *testing.T) { s.Delete([]byte("foo")) assert.Equal(t, false, s.Has([]byte("foo"))) + assert.NotPanics(t, func() { s.Delete([]byte("foo")) }, "Delete of nonexistent key should not panic") + assert.Panics(t, func() { s.Get(nil) }, "Get(nil key) should panic") assert.Panics(t, func() { s.Get([]byte{}) }, "Get(empty key) should panic") assert.Panics(t, func() { s.Has(nil) }, "Has(nil key) should panic") From 4c3446776eaedc5ff4cd1aecc7f9eacea503e513 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Wed, 16 Mar 2022 19:19:21 +0800 Subject: [PATCH 39/78] spelling --- baseapp/abci.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/baseapp/abci.go b/baseapp/abci.go index 6e4aa2e40d2d..f599d215dc62 100644 --- a/baseapp/abci.go +++ b/baseapp/abci.go @@ -262,7 +262,7 @@ func (app *BaseApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx { // DeliverTx implements the ABCI interface and executes a tx in DeliverTx mode. // State only gets persisted if all messages are valid and get executed successfully. -// Otherwise, the ResponseDeliverTx will contain releveant error information. +// Otherwise, the ResponseDeliverTx will contain relevant error information. // Regardless of tx execution outcome, the ResponseDeliverTx will contain relevant // gas execution context. func (app *BaseApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx { From 86929d3264cec55628216d92740c1155b53f1668 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Fri, 25 Mar 2022 18:47:35 +0800 Subject: [PATCH 40/78] badgerdb: fix versions file write --- db/badgerdb/db.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/badgerdb/db.go b/db/badgerdb/db.go index 5982f7ea2399..145cfe18f037 100644 --- a/db/badgerdb/db.go +++ b/db/badgerdb/db.go @@ -159,7 +159,7 @@ func writeVersionsFile(vm *versionManager, path string) error { strconv.FormatUint(ts, 10), }) } - file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0644) + file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { return err } From 4cdd5eeb9061425b903f68a4301702344fe25d1c Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Thu, 24 Mar 2022 21:04:58 +0800 Subject: [PATCH 41/78] version manager - DeleteAbove() & implement w/ slice --- db/version_manager.go | 115 +++++++++++++++++++------------------ db/version_manager_test.go | 20 +++++-- 2 files changed, 75 insertions(+), 60 deletions(-) diff --git a/db/version_manager.go b/db/version_manager.go index e9cbf01185e7..4088010298a5 100644 --- a/db/version_manager.go +++ b/db/version_manager.go @@ -2,46 +2,44 @@ package db import ( "fmt" + "sort" ) // VersionManager encapsulates the current valid versions of a DB and computes // the next version. type VersionManager struct { - versions map[uint64]struct{} - initial, last uint64 + versions []uint64 } var _ VersionSet = (*VersionManager)(nil) // NewVersionManager creates a VersionManager from a slice of version ids. func NewVersionManager(versions []uint64) *VersionManager { - vmap := make(map[uint64]struct{}) - var init, last uint64 - for _, ver := range versions { - vmap[ver] = struct{}{} - if init == 0 || ver < init { - init = ver - } - if ver > last { - last = ver - } - } - return &VersionManager{versions: vmap, initial: init, last: last} + vs := make([]uint64, len(versions)) + copy(vs, versions) + sort.Slice(vs, func(i, j int) bool { return vs[i] < vs[j] }) + return &VersionManager{vs} } // Exists implements VersionSet. func (vm *VersionManager) Exists(version uint64) bool { - _, has := vm.versions[version] + _, has := binarySearch(vm.versions, version) return has } // Last implements VersionSet. func (vm *VersionManager) Last() uint64 { - return vm.last + if len(vm.versions) == 0 { + return 0 + } + return vm.versions[len(vm.versions)-1] } func (vm *VersionManager) Initial() uint64 { - return vm.initial + if len(vm.versions) == 0 { + return 0 + } + return vm.versions[0] } func (vm *VersionManager) Save(target uint64) (uint64, error) { @@ -52,58 +50,48 @@ func (vm *VersionManager) Save(target uint64) (uint64, error) { return 0, fmt.Errorf( "target version cannot be less than next sequential version (%v < %v)", target, next) } - if _, has := vm.versions[target]; has { + if vm.Exists(target) { return 0, fmt.Errorf("version exists: %v", target) } - vm.versions[target] = struct{}{} - vm.last = target - if len(vm.versions) == 1 { - vm.initial = target - } + vm.versions = append(vm.versions, target) return target, nil } -func findLimit(m map[uint64]struct{}, cmp func(uint64, uint64) bool, init uint64) uint64 { - for x, _ := range m { - if cmp(x, init) { - init = x - } +func (vm *VersionManager) Delete(target uint64) { + i, has := binarySearch(vm.versions, target) + if !has { + return } - return init + vm.versions = append(vm.versions[:i], vm.versions[i+1:]...) } -func (vm *VersionManager) Delete(target uint64) { - delete(vm.versions, target) - if target == vm.last { - vm.last = findLimit(vm.versions, func(x, max uint64) bool { return x > max }, 0) +func (vm *VersionManager) DeleteAbove(target uint64) { + var iFrom *int + for i, v := range vm.versions { + if iFrom == nil && v > target { + iFrom = new(int) + *iFrom = i + } } - if target == vm.initial { - vm.initial = findLimit(vm.versions, func(x, min uint64) bool { return x < min }, vm.last) + if iFrom != nil { + vm.versions = vm.versions[:*iFrom] } } type vmIterator struct { - ch <-chan uint64 - open bool - buf uint64 + vmgr *VersionManager + i int } func (vi *vmIterator) Next() bool { - vi.buf, vi.open = <-vi.ch - return vi.open + vi.i++ + return vi.i < len(vi.vmgr.versions) } -func (vi *vmIterator) Value() uint64 { return vi.buf } +func (vi *vmIterator) Value() uint64 { return vi.vmgr.versions[vi.i] } // Iterator implements VersionSet. func (vm *VersionManager) Iterator() VersionIterator { - ch := make(chan uint64) - go func() { - for ver, _ := range vm.versions { - ch <- ver - } - close(ch) - }() - return &vmIterator{ch: ch} + return &vmIterator{vm, -1} } // Count implements VersionSet. @@ -114,18 +102,35 @@ func (vm *VersionManager) Equal(that VersionSet) bool { if vm.Count() != that.Count() { return false } - for it := that.Iterator(); it.Next(); { - if !vm.Exists(it.Value()) { + for i, it := 0, that.Iterator(); it.Next(); { + if vm.versions[i] != it.Value() { return false } + i++ } return true } func (vm *VersionManager) Copy() *VersionManager { - vmap := make(map[uint64]struct{}) - for ver, _ := range vm.versions { - vmap[ver] = struct{}{} + vs := make([]uint64, len(vm.versions)) + copy(vs, vm.versions) + return &VersionManager{vs} +} + +// Returns closest index and whether it's a match +func binarySearch(hay []uint64, ndl uint64) (int, bool) { + var mid int + from, to := 0, len(hay)-1 + for from <= to { + mid = (from + to) / 2 + switch { + case hay[mid] < ndl: + from = mid + 1 + case hay[mid] > ndl: + to = mid - 1 + default: + return mid, true + } } - return &VersionManager{versions: vmap, initial: vm.initial, last: vm.last} + return from, false } diff --git a/db/version_manager_test.go b/db/version_manager_test.go index 6575c7e1758c..0a21b1304f58 100644 --- a/db/version_manager_test.go +++ b/db/version_manager_test.go @@ -42,11 +42,7 @@ func TestVersionManager(t *testing.T) { require.Equal(t, id2, vm.Initial()) require.Equal(t, id3, vm.Last()) - var all []uint64 - for it := vm.Iterator(); it.Next(); { - all = append(all, it.Value()) - } - sort.Slice(all, func(i, j int) bool { return all[i] < all[j] }) + all := allVersions(vm) require.Equal(t, []uint64{id2, id3}, all) vmc := vm.Copy() @@ -56,4 +52,18 @@ func TestVersionManager(t *testing.T) { vm2 := db.NewVersionManager([]uint64{id2, id3}) require.True(t, vm.Equal(vm2)) + + vm = db.NewVersionManager([]uint64{1, 2, 3, 5, 10}) + vm.DeleteAbove(10) + require.Equal(t, []uint64{1, 2, 3, 5, 10}, allVersions(vm)) + vm.DeleteAbove(4) + require.Equal(t, []uint64{1, 2, 3}, allVersions(vm)) +} + +func allVersions(vm *db.VersionManager) (all []uint64) { + for it := vm.Iterator(); it.Next(); { + all = append(all, it.Value()) + } + sort.Slice(all, func(i, j int) bool { return all[i] < all[j] }) + return } From e36e3add1abc57158ba3be1b64556d4ed205aad2 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Thu, 24 Mar 2022 17:46:05 +0800 Subject: [PATCH 42/78] DBConnection.RevertTo --- db/badgerdb/db.go | 37 ++++++++++++++++++++++ db/dbtest/testcases.go | 70 +++++++++++++++++++++++------------------- db/memdb/db.go | 27 +++++++++++++--- db/rocksdb/db.go | 25 +++++++++++++-- 4 files changed, 122 insertions(+), 37 deletions(-) diff --git a/db/badgerdb/db.go b/db/badgerdb/db.go index 145cfe18f037..8643e7356cb6 100644 --- a/db/badgerdb/db.go +++ b/db/badgerdb/db.go @@ -274,6 +274,34 @@ func (b *BadgerDB) Revert() error { return errors.New("bad version history") } } + return b.revert(target) +} + +// RevertTo reverts the DB to a target version +func (b *BadgerDB) RevertTo(ver uint64) error { + b.mtx.RLock() + defer b.mtx.RUnlock() + if b.openWriters > 0 { + return db.ErrOpenTransactions + } + + // Revert from latest commit timestamp to target timestamp + if !b.vmgr.Exists(ver) { + return db.ErrVersionDoesNotExist + } + targetTs, has := b.vmgr.versionTs(ver) + if !has { + return errors.New("bad version history") + } + if err := b.revert(targetTs); err != nil { + return err + } + b.vmgr.DeleteAbove(ver) + return nil +} + +// reverts to a target timestamp +func (b *BadgerDB) revert(target uint64) error { lastTs := b.vmgr.lastTs if target == lastTs { return nil @@ -547,3 +575,12 @@ func (vm *versionManager) Delete(target uint64) { vm.VersionManager.Delete(target) delete(vm.vmap, target) } + +func (vm *versionManager) DeleteAbove(target uint64) { + vm.VersionManager.DeleteAbove(target) + for v, _ := range vm.vmap { + if v > target { + delete(vm.vmap, v) + } + } +} diff --git a/db/dbtest/testcases.go b/db/dbtest/testcases.go index 8b219f362e39..be938f9c030e 100644 --- a/db/dbtest/testcases.go +++ b/db/dbtest/testcases.go @@ -412,6 +412,8 @@ func DoTestRevert(t *testing.T, load Loader, reload bool) { } initContents() + require.Error(t, db.RevertTo(0)) // RevertTo(0) is not allowed - user must use Revert() + require.Error(t, db.RevertTo(10)) // non-existent version require.NoError(t, db.Revert()) view := db.Reader() it, err := view.Iterator(nil, nil) @@ -421,7 +423,7 @@ func DoTestRevert(t *testing.T, load Loader, reload bool) { require.NoError(t, view.Discard()) initContents() - _, err = db.SaveNextVersion() + v1, err := db.SaveNextVersion() require.NoError(t, err) // get snapshot of db state @@ -442,7 +444,7 @@ func DoTestRevert(t *testing.T, load Loader, reload bool) { require.NoError(t, err) for it.Next() { val, has := state[string(it.Key())] - require.True(t, has, "key should not be present: %v => %v", it.Key(), it.Value()) + require.True(t, has, "unexpected key: %v => %v", it.Key(), it.Value()) require.Equal(t, val, it.Value()) count++ } @@ -463,48 +465,54 @@ func DoTestRevert(t *testing.T, load Loader, reload bool) { txn = db.Writer() require.NoError(t, txn.Set([]byte{3}, []byte{30})) require.NoError(t, txn.Set([]byte{8}, []byte{8})) - require.NoError(t, txn.Delete([]byte{9})) + require.NoError(t, txn.Delete([]byte{9})) // redundant delete require.NoError(t, txn.Commit()) } changeContents() - if reload { - db.Close() - db = load(t, dirname) + cases := []func(dbm.DBConnection) error{ + func(db dbm.DBConnection) error { return db.Revert() }, + func(db dbm.DBConnection) error { return db.RevertTo(v1) }, } + for _, revertFunc := range cases { + if reload { + db.Close() + db = load(t, dirname) + } - txn = db.Writer() - require.Error(t, db.Revert()) // can't revert with open writers - txn.Discard() - require.NoError(t, db.Revert()) + txn = db.Writer() + require.Error(t, revertFunc(db)) // can't revert with open writers + txn.Discard() + require.NoError(t, db.Revert()) - if reload { - db.Close() - db = load(t, dirname) - } + if reload { + db.Close() + db = load(t, dirname) + } - checkContents() + checkContents() - // With intermediate versions added & deleted, revert again to v1 - changeContents() - v2, _ := db.SaveNextVersion() + // With intermediate versions added & deleted, revert again to v1 + changeContents() + v2, _ := db.SaveNextVersion() - txn = db.Writer() - require.NoError(t, txn.Delete([]byte{6})) - require.NoError(t, txn.Set([]byte{8}, []byte{9})) - require.NoError(t, txn.Set([]byte{11}, []byte{11})) - txn.Commit() - v3, _ := db.SaveNextVersion() + txn = db.Writer() + require.NoError(t, txn.Delete([]byte{6})) + require.NoError(t, txn.Set([]byte{8}, []byte{9})) + require.NoError(t, txn.Set([]byte{11}, []byte{11})) + txn.Commit() + v3, _ := db.SaveNextVersion() - txn = db.Writer() - require.NoError(t, txn.Set([]byte{12}, []byte{12})) - txn.Commit() + txn = db.Writer() + require.NoError(t, txn.Set([]byte{12}, []byte{12})) + txn.Commit() - db.DeleteVersion(v2) - db.DeleteVersion(v3) - db.Revert() - checkContents() + db.DeleteVersion(v2) + db.DeleteVersion(v3) + revertFunc(db) + checkContents() + } require.NoError(t, db.Close()) } diff --git a/db/memdb/db.go b/db/memdb/db.go index f3f50377c7c2..5e035773d04d 100644 --- a/db/memdb/db.go +++ b/db/memdb/db.go @@ -167,19 +167,38 @@ func (dbm *MemDB) Revert() error { if dbm.openWriters > 0 { return db.ErrOpenTransactions } - last := dbm.vmgr.Last() if last == 0 { dbm.btree = btree.New(bTreeDegree) return nil } + return dbm.revert(last) +} + +func (dbm *MemDB) RevertTo(target uint64) error { + dbm.mtx.RLock() + defer dbm.mtx.RUnlock() + if dbm.openWriters > 0 { + return db.ErrOpenTransactions + } + if !dbm.vmgr.Exists(target) { + return db.ErrVersionDoesNotExist + } + err := dbm.revert(target) + if err != nil { + dbm.vmgr.DeleteAbove(target) + } + return err +} + +func (dbm *MemDB) revert(target uint64) error { var has bool - dbm.btree, has = dbm.saved[last] + dbm.btree, has = dbm.saved[target] if !has { - return fmt.Errorf("bad version history: version %v not saved", last) + return fmt.Errorf("bad version history: version %v not saved", target) } for ver, _ := range dbm.saved { - if ver > last { + if ver > target { delete(dbm.saved, ver) } } diff --git a/db/rocksdb/db.go b/db/rocksdb/db.go index 227a73b0600a..611f47d0dd8a 100644 --- a/db/rocksdb/db.go +++ b/db/rocksdb/db.go @@ -286,6 +286,27 @@ func (mgr *dbManager) Revert() (err error) { if mgr.openWriters > 0 { return db.ErrOpenTransactions } + return mgr.revert(mgr.vmgr.Last()) +} + +func (mgr *dbManager) RevertTo(target uint64) (err error) { + mgr.mtx.RLock() + defer mgr.mtx.RUnlock() + if mgr.openWriters > 0 { + return db.ErrOpenTransactions + } + if !mgr.vmgr.Exists(target) { + return db.ErrVersionDoesNotExist + } + err = mgr.revert(target) + if err != nil { + return + } + mgr.vmgr.DeleteAbove(target) + return +} + +func (mgr *dbManager) revert(target uint64) (err error) { // Close current connection and replace it with a checkpoint (created from the last checkpoint) mgr.current.Close() dbPath := filepath.Join(mgr.dir, currentDBFileName) @@ -293,8 +314,8 @@ func (mgr *dbManager) Revert() (err error) { if err != nil { return } - if last := mgr.vmgr.Last(); last != 0 { - err = mgr.restoreFromCheckpoint(last, dbPath) + if target != 0 { // when target is 0, restore no checkpoints + err = mgr.restoreFromCheckpoint(target, dbPath) if err != nil { return } From f0d31ba4480eee24d847b47566f938effbb856f3 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Fri, 25 Mar 2022 19:17:45 +0800 Subject: [PATCH 43/78] unit test badgerdb.versionmanager --- db/badgerdb/db.go | 1 + db/badgerdb/db_test.go | 13 +++++++ db/dbtest/version_set.go | 69 ++++++++++++++++++++++++++++++++++++++ db/version_manager_test.go | 62 ++-------------------------------- 4 files changed, 86 insertions(+), 59 deletions(-) create mode 100644 db/dbtest/version_set.go diff --git a/db/badgerdb/db.go b/db/badgerdb/db.go index 8643e7356cb6..646cede95219 100644 --- a/db/badgerdb/db.go +++ b/db/badgerdb/db.go @@ -28,6 +28,7 @@ var ( _ db.DBReader = (*badgerTxn)(nil) _ db.DBWriter = (*badgerWriter)(nil) _ db.DBReadWriter = (*badgerWriter)(nil) + _ db.VersionSet = (*versionManager)(nil) ) // BadgerDB is a connection to a BadgerDB key-value database. diff --git a/db/badgerdb/db_test.go b/db/badgerdb/db_test.go index 05d9cc4ba132..5a87e689c0a2 100644 --- a/db/badgerdb/db_test.go +++ b/db/badgerdb/db_test.go @@ -39,3 +39,16 @@ func TestRevert(t *testing.T) { func TestReloadDB(t *testing.T) { dbtest.DoTestReloadDB(t, load) } + +func TestVersionManager(t *testing.T) { + new := func(vs []uint64) db.VersionSet { + vmap := map[uint64]uint64{} + var lastTs uint64 + for _, v := range vs { + vmap[v] = v + lastTs = v + } + return &versionManager{db.NewVersionManager(vs), vmap, lastTs} + } + dbtest.DoTestVersionSet(t, new) +} diff --git a/db/dbtest/version_set.go b/db/dbtest/version_set.go new file mode 100644 index 000000000000..017d90b76676 --- /dev/null +++ b/db/dbtest/version_set.go @@ -0,0 +1,69 @@ +package dbtest + +import ( + "sort" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/db" +) + +// Test that a type satisfies the behavior of VersionSet +func DoTestVersionSet(t *testing.T, new func([]uint64) db.VersionSet) { + vm := db.NewVersionManager(nil) + require.Equal(t, uint64(0), vm.Last()) + require.Equal(t, 0, vm.Count()) + require.True(t, vm.Equal(vm)) + require.False(t, vm.Exists(0)) + + id1, err := vm.Save(0) + require.NoError(t, err) + require.Equal(t, uint64(1), id1) + require.True(t, vm.Exists(id1)) + id2, err := vm.Save(0) + require.NoError(t, err) + require.True(t, vm.Exists(id2)) + id3, err := vm.Save(0) + require.NoError(t, err) + require.True(t, vm.Exists(id3)) + + _, err = vm.Save(id1) // can't save existing id + require.Error(t, err) + + id4, err := vm.Save(0) + require.NoError(t, err) + require.True(t, vm.Exists(id4)) + vm.Delete(id4) + require.False(t, vm.Exists(id4)) + + vm.Delete(id1) + require.False(t, vm.Exists(id1)) + require.Equal(t, id2, vm.Initial()) + require.Equal(t, id3, vm.Last()) + + all := allVersions(vm) + require.Equal(t, []uint64{id2, id3}, all) + + vmc := vm.Copy() + id5, err := vmc.Save(0) + require.NoError(t, err) + require.False(t, vm.Exists(id5)) // true copy is made + + vm2 := db.NewVersionManager([]uint64{id2, id3}) + require.True(t, vm.Equal(vm2)) + + vm = db.NewVersionManager([]uint64{1, 2, 3, 5, 10}) + vm.DeleteAbove(10) + require.Equal(t, []uint64{1, 2, 3, 5, 10}, allVersions(vm)) + vm.DeleteAbove(4) + require.Equal(t, []uint64{1, 2, 3}, allVersions(vm)) +} + +func allVersions(vm *db.VersionManager) (all []uint64) { + for it := vm.Iterator(); it.Next(); { + all = append(all, it.Value()) + } + sort.Slice(all, func(i, j int) bool { return all[i] < all[j] }) + return +} diff --git a/db/version_manager_test.go b/db/version_manager_test.go index 0a21b1304f58..4bd7b64a2cd8 100644 --- a/db/version_manager_test.go +++ b/db/version_manager_test.go @@ -1,69 +1,13 @@ package db_test import ( - "sort" "testing" - "github.com/stretchr/testify/require" - "github.com/cosmos/cosmos-sdk/db" + "github.com/cosmos/cosmos-sdk/db/dbtest" ) -// Test that VersionManager satisfies the behavior of VersionSet func TestVersionManager(t *testing.T) { - vm := db.NewVersionManager(nil) - require.Equal(t, uint64(0), vm.Last()) - require.Equal(t, 0, vm.Count()) - require.True(t, vm.Equal(vm)) - require.False(t, vm.Exists(0)) - - id1, err := vm.Save(0) - require.NoError(t, err) - require.Equal(t, uint64(1), id1) - require.True(t, vm.Exists(id1)) - id2, err := vm.Save(0) - require.NoError(t, err) - require.True(t, vm.Exists(id2)) - id3, err := vm.Save(0) - require.NoError(t, err) - require.True(t, vm.Exists(id3)) - - _, err = vm.Save(id1) // can't save existing id - require.Error(t, err) - - id4, err := vm.Save(0) - require.NoError(t, err) - require.True(t, vm.Exists(id4)) - vm.Delete(id4) - require.False(t, vm.Exists(id4)) - - vm.Delete(id1) - require.False(t, vm.Exists(id1)) - require.Equal(t, id2, vm.Initial()) - require.Equal(t, id3, vm.Last()) - - all := allVersions(vm) - require.Equal(t, []uint64{id2, id3}, all) - - vmc := vm.Copy() - id5, err := vmc.Save(0) - require.NoError(t, err) - require.False(t, vm.Exists(id5)) // true copy is made - - vm2 := db.NewVersionManager([]uint64{id2, id3}) - require.True(t, vm.Equal(vm2)) - - vm = db.NewVersionManager([]uint64{1, 2, 3, 5, 10}) - vm.DeleteAbove(10) - require.Equal(t, []uint64{1, 2, 3, 5, 10}, allVersions(vm)) - vm.DeleteAbove(4) - require.Equal(t, []uint64{1, 2, 3}, allVersions(vm)) -} - -func allVersions(vm *db.VersionManager) (all []uint64) { - for it := vm.Iterator(); it.Next(); { - all = append(all, it.Value()) - } - sort.Slice(all, func(i, j int) bool { return all[i] < all[j] }) - return + new := func(vs []uint64) db.VersionSet { return db.NewVersionManager(vs) } + dbtest.DoTestVersionSet(t, new) } From 620728f9d433a11c6d52d5a96328388d12faa297 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Fri, 25 Mar 2022 19:58:08 +0800 Subject: [PATCH 44/78] refactor multistore: fix state sync --- store/v2alpha1/multi/snapshot.go | 7 +++-- store/v2alpha1/multi/snapshot_test.go | 28 +++++++++++++------ store/v2alpha1/multi/store.go | 40 +++++++++++++++++++-------- 3 files changed, 52 insertions(+), 23 deletions(-) diff --git a/store/v2alpha1/multi/snapshot.go b/store/v2alpha1/multi/snapshot.go index 37f687873954..864c6c70e294 100644 --- a/store/v2alpha1/multi/snapshot.go +++ b/store/v2alpha1/multi/snapshot.go @@ -108,6 +108,7 @@ func (rs *Store) Restore( var subStore *substore var storeSchemaReceived = false + var receivedStoreSchema StoreSchema var snapshotItem snapshottypes.SnapshotItem @@ -123,13 +124,13 @@ loop: switch item := snapshotItem.Item.(type) { case *snapshottypes.SnapshotItem_Schema: - receivedStoreSchema := make(StoreSchema, len(item.Schema.GetKeys())) + receivedStoreSchema = make(StoreSchema, len(item.Schema.GetKeys())) storeSchemaReceived = true for _, sKey := range item.Schema.GetKeys() { receivedStoreSchema[string(sKey)] = types.StoreTypePersistent } - if !rs.schema.equal(receivedStoreSchema) { + if !receivedStoreSchema.matches(rs.schema) { return snapshottypes.SnapshotItem{}, sdkerrors.Wrap(sdkerrors.ErrLogic, "received schema does not match app schema") } @@ -140,7 +141,7 @@ loop: return snapshottypes.SnapshotItem{}, sdkerrors.Wrapf(sdkerrors.ErrLogic, "received store name before store schema %s", storeName) } // checking the store schema exists or not - if _, has := rs.schema[storeName]; !has { + if _, has := receivedStoreSchema[storeName]; !has { return snapshottypes.SnapshotItem{}, sdkerrors.Wrapf(sdkerrors.ErrLogic, "store is missing from schema %s", storeName) } diff --git a/store/v2alpha1/multi/snapshot_test.go b/store/v2alpha1/multi/snapshot_test.go index 0495e95e1e10..d61e330a32f4 100644 --- a/store/v2alpha1/multi/snapshot_test.go +++ b/store/v2alpha1/multi/snapshot_test.go @@ -22,13 +22,25 @@ import ( "github.com/cosmos/cosmos-sdk/store/types" ) -func multiStoreConfig(t *testing.T, stores int) StoreConfig { - opts := DefaultStoreConfig() +var testStoreKeys []types.StoreKey + +func makeStoreKeys(upto int) { + if len(testStoreKeys) >= upto { + return + } + for i := len(testStoreKeys); i < upto; i++ { + skey := types.NewKVStoreKey(fmt.Sprintf("store%d", i)) + testStoreKeys = append(testStoreKeys, skey) + } +} + +func multiStoreConfig(t *testing.T, stores int) StoreParams { + opts := DefaultStoreParams() opts.Pruning = types.PruneNothing + makeStoreKeys(stores) for i := 0; i < stores; i++ { - sKey := types.NewKVStoreKey(fmt.Sprintf("store%d", i)) - require.NoError(t, opts.RegisterSubstore(sKey.Name(), types.StoreTypePersistent)) + require.NoError(t, opts.RegisterSubstore(testStoreKeys[i], types.StoreTypePersistent)) } return opts @@ -42,7 +54,7 @@ func newMultiStoreWithGeneratedData(t *testing.T, db dbm.DBConnection, stores in var sKeys []string for sKey := range store.schema { - sKeys = append(sKeys, sKey) + sKeys = append(sKeys, sKey.Name()) } sort.Slice(sKeys, func(i, j int) bool { @@ -73,7 +85,7 @@ func newMultiStoreWithBasicData(t *testing.T, db dbm.DBConnection, stores int) * require.NoError(t, err) for sKey := range store.schema { - sStore, err := store.getSubstore(sKey) + sStore, err := store.getSubstore(sKey.Name()) require.NoError(t, err) for k, v := range alohaData { sStore.Set([]byte(k), []byte(v)) @@ -215,9 +227,9 @@ func TestMultistoreSnapshotRestore(t *testing.T) { assert.Equal(t, source.LastCommitID(), target.LastCommitID()) for sKey := range source.schema { - sourceSubStore, err := source.getSubstore(sKey) + sourceSubStore, err := source.getSubstore(sKey.Name()) require.NoError(t, err) - targetSubStore, err := target.getSubstore(sKey) + targetSubStore, err := target.getSubstore(sKey.Name()) require.NoError(t, err) require.Equal(t, sourceSubStore, targetSubStore) } diff --git a/store/v2alpha1/multi/store.go b/store/v2alpha1/multi/store.go index 7b9274cc9271..fae729c4ae53 100644 --- a/store/v2alpha1/multi/store.go +++ b/store/v2alpha1/multi/store.go @@ -196,6 +196,22 @@ func (this StoreSchema) equal(that StoreSchema) bool { return true } +func (this StoreSchema) matches(that StoreKeySchema) bool { + if len(this) != len(that) { + return false + } + for key, val := range that { + myval, has := this[key.Name()] + if !has { + return false + } + if val != myval { + return false + } + } + return true +} + // Parses a schema from the DB func readSavedSchema(bucket dbm.DBReader) (*SchemaBuilder, error) { ret := newSchemaBuilder() @@ -855,13 +871,13 @@ func (pr *SchemaBuilder) migrateSchema(upgrades types.StoreUpgrades) error { return nil } -func (pr *SchemaBuilder) storeInfo(key string) (sst types.StoreType, ix int, err error) { - ix, has := binarySearch(pr.reserved, key) +func (reg *SchemaBuilder) storeInfo(key string) (sst types.StoreType, ix int, err error) { + ix, has := binarySearch(reg.reserved, key) if !has { err = fmt.Errorf("prefix does not exist: %v", key) return } - sst, has = pr.StoreSchema[key] + sst, has = reg.StoreSchema[key] if !has { err = fmt.Errorf("prefix is registered but not in schema: %v", key) } @@ -870,26 +886,26 @@ func (pr *SchemaBuilder) storeInfo(key string) (sst types.StoreType, ix int, err } // registerName registers a store key by name only -func (pr *SchemaBuilder) registerName(key string, typ types.StoreType) error { +func (reg *SchemaBuilder) registerName(key string, typ types.StoreType) error { if !validSubStoreType(typ) { return fmt.Errorf("StoreType not supported: %v", typ) } // Find the neighboring reserved prefix, and check for duplicates and conflicts - i, has := binarySearch(pr.reserved, key) + i, has := binarySearch(reg.reserved, key) if has { return fmt.Errorf("prefix already exists: %v", key) } - if i > 0 && strings.HasPrefix(key, pr.reserved[i-1]) { - return fmt.Errorf("prefix conflict: '%v' exists, cannot add '%v'", pr.reserved[i-1], key) + if i > 0 && strings.HasPrefix(key, reg.reserved[i-1]) { + return fmt.Errorf("prefix conflict: '%v' exists, cannot add '%v'", reg.reserved[i-1], key) } - if i < len(pr.reserved) && strings.HasPrefix(pr.reserved[i], key) { - return fmt.Errorf("prefix conflict: '%v' exists, cannot add '%v'", pr.reserved[i], key) + if i < len(reg.reserved) && strings.HasPrefix(reg.reserved[i], key) { + return fmt.Errorf("prefix conflict: '%v' exists, cannot add '%v'", reg.reserved[i], key) } - reserved := pr.reserved[:i] + reserved := reg.reserved[:i] reserved = append(reserved, key) - pr.reserved = append(reserved, pr.reserved[i:]...) - pr.StoreSchema[key] = typ + reg.reserved = append(reserved, reg.reserved[i:]...) + reg.StoreSchema[key] = typ return nil } From 9e2152e489fcec80256c0038d773881f21c45a7a Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Sun, 27 Mar 2022 23:44:36 +0800 Subject: [PATCH 45/78] refactor multistore: fix store migration --- store/v2alpha1/multi/migration.go | 4 ++-- store/v2alpha1/multi/migration_test.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/store/v2alpha1/multi/migration.go b/store/v2alpha1/multi/migration.go index 7481b421ac3b..61b256b6dae2 100644 --- a/store/v2alpha1/multi/migration.go +++ b/store/v2alpha1/multi/migration.go @@ -11,7 +11,7 @@ import ( ) // MigrateFromV1 will migrate the state from iavl to smt -func MigrateFromV1(rootMultiStore *v1Store.Store, store2db dbm.DBConnection, storeConfig StoreConfig) (*Store, error) { +func MigrateFromV1(rootMultiStore *v1Store.Store, store2db dbm.DBConnection, storeConfig StoreParams) (*Store, error) { type namedStore struct { *iavl.Store name string @@ -21,7 +21,7 @@ func MigrateFromV1(rootMultiStore *v1Store.Store, store2db dbm.DBConnection, sto keyName := storeKey.Name() switch store := rootMultiStore.GetStoreByName(keyName).(type) { case *iavl.Store: - err := storeConfig.RegisterSubstore(keyName, types.StoreTypePersistent) + err := storeConfig.RegisterSubstore(storeKey, types.StoreTypePersistent) if err != nil { return nil, err } diff --git a/store/v2alpha1/multi/migration_test.go b/store/v2alpha1/multi/migration_test.go index 09f1f74b5a16..d2ed43ff753e 100644 --- a/store/v2alpha1/multi/migration_test.go +++ b/store/v2alpha1/multi/migration_test.go @@ -70,7 +70,7 @@ func TestMigrationV2(t *testing.T) { // setup a new root store of smt db2 := memdb.NewDB() - storeConfig := DefaultStoreConfig() + storeConfig := DefaultStoreParams() // migrating the iavl store (v1) to smt store (v2) v2Store, err := MigrateFromV1(v1Store, db2, storeConfig) require.NoError(t, err) @@ -98,7 +98,7 @@ func TestMigrateV2ForEmptyStore(t *testing.T) { err := v1Store.LoadLatestVersion() require.Nil(t, err) db2 := memdb.NewDB() - storeConfig := DefaultStoreConfig() + storeConfig := DefaultStoreParams() // migrating the iavl store (v1) to smt store (v2) v2Store, err := MigrateFromV1(v1Store, db2, storeConfig) require.NoError(t, err) From 68da7594b7cd2e62cb1c1fa9ce7d5f44910ca394 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Sun, 27 Mar 2022 23:34:21 +0800 Subject: [PATCH 46/78] fix rename store/{v2 => v2alpha1} --- baseapp/baseapp.go | 4 ++-- baseapp/baseapp_test.go | 4 ++-- baseapp/options.go | 4 ++-- baseapp/test_helpers.go | 2 +- server/mock/store.go | 2 +- simapp/app.go | 4 ++-- store/v2alpha1/multi/compat.go | 2 +- store/v2alpha1/multi/sub_store.go | 2 +- store/v2alpha1/multi/v1asv2.go | 2 +- store/v2alpha1/smt/store.go | 2 +- testutil/context.go | 4 ++-- types/context.go | 2 +- types/store.go | 2 +- x/group/keeper/invariants_test.go | 4 ++-- x/params/types/subspace_test.go | 2 +- x/upgrade/types/storeloader.go | 4 ++-- x/upgrade/types/storeloader_test.go | 2 +- 17 files changed, 24 insertions(+), 24 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index f584ba84d8e2..92651c1cc19f 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -11,8 +11,8 @@ import ( "github.com/cosmos/cosmos-sdk/codec/types" dbm "github.com/cosmos/cosmos-sdk/db" "github.com/cosmos/cosmos-sdk/snapshots" - stypes "github.com/cosmos/cosmos-sdk/store/v2" - "github.com/cosmos/cosmos-sdk/store/v2/multi" + stypes "github.com/cosmos/cosmos-sdk/store/v2alpha1" + "github.com/cosmos/cosmos-sdk/store/v2alpha1/multi" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/tx" ) diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 7013e8b1c44f..5180b9129c52 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -34,8 +34,8 @@ import ( "github.com/cosmos/cosmos-sdk/simapp" "github.com/cosmos/cosmos-sdk/snapshots" snapshottypes "github.com/cosmos/cosmos-sdk/snapshots/types" - stypes "github.com/cosmos/cosmos-sdk/store/v2" - "github.com/cosmos/cosmos-sdk/store/v2/multi" + stypes "github.com/cosmos/cosmos-sdk/store/v2alpha1" + "github.com/cosmos/cosmos-sdk/store/v2alpha1/multi" "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" diff --git a/baseapp/options.go b/baseapp/options.go index ce63aadaf1d6..776e98c455aa 100644 --- a/baseapp/options.go +++ b/baseapp/options.go @@ -6,8 +6,8 @@ import ( "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/snapshots" - storetypes "github.com/cosmos/cosmos-sdk/store/v2" - "github.com/cosmos/cosmos-sdk/store/v2/multi" + storetypes "github.com/cosmos/cosmos-sdk/store/v2alpha1" + "github.com/cosmos/cosmos-sdk/store/v2alpha1/multi" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/tx" ) diff --git a/baseapp/test_helpers.go b/baseapp/test_helpers.go index 53ca7ebc8c48..67b02d5f5fac 100644 --- a/baseapp/test_helpers.go +++ b/baseapp/test_helpers.go @@ -4,7 +4,7 @@ import ( abci "github.com/tendermint/tendermint/abci/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - "github.com/cosmos/cosmos-sdk/store/v2/multi" + "github.com/cosmos/cosmos-sdk/store/v2alpha1/multi" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/types/tx" diff --git a/server/mock/store.go b/server/mock/store.go index 5dffddb8b13b..3b37ef69dcd1 100644 --- a/server/mock/store.go +++ b/server/mock/store.go @@ -6,7 +6,7 @@ import ( protoio "github.com/gogo/protobuf/io" snapshottypes "github.com/cosmos/cosmos-sdk/snapshots/types" - storetypes "github.com/cosmos/cosmos-sdk/store/v2" + storetypes "github.com/cosmos/cosmos-sdk/store/v2alpha1" sdk "github.com/cosmos/cosmos-sdk/types" ) diff --git a/simapp/app.go b/simapp/app.go index b98c8a542ee0..99e885353fc7 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -29,8 +29,8 @@ import ( servertypes "github.com/cosmos/cosmos-sdk/server/types" simappparams "github.com/cosmos/cosmos-sdk/simapp/params" "github.com/cosmos/cosmos-sdk/store/streaming" - storetypes "github.com/cosmos/cosmos-sdk/store/v2" - "github.com/cosmos/cosmos-sdk/store/v2/multi" + storetypes "github.com/cosmos/cosmos-sdk/store/v2alpha1" + "github.com/cosmos/cosmos-sdk/store/v2alpha1/multi" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/version" diff --git a/store/v2alpha1/multi/compat.go b/store/v2alpha1/multi/compat.go index 4a8a2ddf9aa1..7047e69b7c4a 100644 --- a/store/v2alpha1/multi/compat.go +++ b/store/v2alpha1/multi/compat.go @@ -7,7 +7,7 @@ import ( tmdb "github.com/tendermint/tm-db" v1 "github.com/cosmos/cosmos-sdk/store/types" - v2 "github.com/cosmos/cosmos-sdk/store/v2" + v2 "github.com/cosmos/cosmos-sdk/store/v2alpha1" ) var ( diff --git a/store/v2alpha1/multi/sub_store.go b/store/v2alpha1/multi/sub_store.go index efd05f63062d..385f71800cd9 100644 --- a/store/v2alpha1/multi/sub_store.go +++ b/store/v2alpha1/multi/sub_store.go @@ -9,7 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/store/cachekv" "github.com/cosmos/cosmos-sdk/store/listenkv" "github.com/cosmos/cosmos-sdk/store/tracekv" - types "github.com/cosmos/cosmos-sdk/store/v2" + types "github.com/cosmos/cosmos-sdk/store/v2alpha1" ) // Get implements KVStore. diff --git a/store/v2alpha1/multi/v1asv2.go b/store/v2alpha1/multi/v1asv2.go index ddbe3994ae2e..55d11b596fa9 100644 --- a/store/v2alpha1/multi/v1asv2.go +++ b/store/v2alpha1/multi/v1asv2.go @@ -7,7 +7,7 @@ import ( dbutil "github.com/cosmos/cosmos-sdk/internal/db" "github.com/cosmos/cosmos-sdk/store/rootmulti" v1 "github.com/cosmos/cosmos-sdk/store/types" - v2 "github.com/cosmos/cosmos-sdk/store/v2" + v2 "github.com/cosmos/cosmos-sdk/store/v2alpha1" ) var ( diff --git a/store/v2alpha1/smt/store.go b/store/v2alpha1/smt/store.go index b8fd11f565d6..fab3a4a09417 100644 --- a/store/v2alpha1/smt/store.go +++ b/store/v2alpha1/smt/store.go @@ -6,7 +6,7 @@ import ( dbm "github.com/cosmos/cosmos-sdk/db" "github.com/cosmos/cosmos-sdk/db/prefix" - types "github.com/cosmos/cosmos-sdk/store/v2" + types "github.com/cosmos/cosmos-sdk/store/v2alpha1" ics23 "github.com/confio/ics23/go" "github.com/lazyledger/smt" diff --git a/testutil/context.go b/testutil/context.go index b2eb0f4d1996..abb353efb84f 100644 --- a/testutil/context.go +++ b/testutil/context.go @@ -5,8 +5,8 @@ import ( tmproto "github.com/tendermint/tendermint/proto/tendermint/types" "github.com/cosmos/cosmos-sdk/db/memdb" - stypes "github.com/cosmos/cosmos-sdk/store/v2" - "github.com/cosmos/cosmos-sdk/store/v2/multi" + stypes "github.com/cosmos/cosmos-sdk/store/v2alpha1" + "github.com/cosmos/cosmos-sdk/store/v2alpha1/multi" sdk "github.com/cosmos/cosmos-sdk/types" ) diff --git a/types/context.go b/types/context.go index bd2997c6991c..ffa423f89ef8 100644 --- a/types/context.go +++ b/types/context.go @@ -12,7 +12,7 @@ import ( "github.com/cosmos/cosmos-sdk/store/gaskv" stypes "github.com/cosmos/cosmos-sdk/store/types" - stypes2 "github.com/cosmos/cosmos-sdk/store/v2" + stypes2 "github.com/cosmos/cosmos-sdk/store/v2alpha1" ) /* diff --git a/types/store.go b/types/store.go index d6efede042d5..70a9b4c645aa 100644 --- a/types/store.go +++ b/types/store.go @@ -6,7 +6,7 @@ import ( "strings" "github.com/cosmos/cosmos-sdk/store/types" - types2 "github.com/cosmos/cosmos-sdk/store/v2" + types2 "github.com/cosmos/cosmos-sdk/store/v2alpha1" "github.com/cosmos/cosmos-sdk/types/kv" ) diff --git a/x/group/keeper/invariants_test.go b/x/group/keeper/invariants_test.go index a2beaa069ff9..3d54f800079a 100644 --- a/x/group/keeper/invariants_test.go +++ b/x/group/keeper/invariants_test.go @@ -12,8 +12,8 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/db/memdb" - storetypes "github.com/cosmos/cosmos-sdk/store/v2" - "github.com/cosmos/cosmos-sdk/store/v2/multi" + storetypes "github.com/cosmos/cosmos-sdk/store/v2alpha1" + "github.com/cosmos/cosmos-sdk/store/v2alpha1/multi" "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/group" diff --git a/x/params/types/subspace_test.go b/x/params/types/subspace_test.go index 00c965869f99..05dd6221018f 100644 --- a/x/params/types/subspace_test.go +++ b/x/params/types/subspace_test.go @@ -14,7 +14,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/db/memdb" "github.com/cosmos/cosmos-sdk/simapp" - "github.com/cosmos/cosmos-sdk/store/v2/multi" + "github.com/cosmos/cosmos-sdk/store/v2alpha1/multi" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/params/types" ) diff --git a/x/upgrade/types/storeloader.go b/x/upgrade/types/storeloader.go index 7ed4aadbdbe8..6fa859206cff 100644 --- a/x/upgrade/types/storeloader.go +++ b/x/upgrade/types/storeloader.go @@ -2,8 +2,8 @@ package types import ( "github.com/cosmos/cosmos-sdk/baseapp" - storetypes "github.com/cosmos/cosmos-sdk/store/v2" - "github.com/cosmos/cosmos-sdk/store/v2/multi" + storetypes "github.com/cosmos/cosmos-sdk/store/v2alpha1" + "github.com/cosmos/cosmos-sdk/store/v2alpha1/multi" ) // UpgradeStoreOption is used to prepare baseapp with a fixed StoreOption. diff --git a/x/upgrade/types/storeloader_test.go b/x/upgrade/types/storeloader_test.go index dfc89db8f45b..a5e9c5908418 100644 --- a/x/upgrade/types/storeloader_test.go +++ b/x/upgrade/types/storeloader_test.go @@ -17,7 +17,7 @@ import ( "github.com/cosmos/cosmos-sdk/db/memdb" "github.com/cosmos/cosmos-sdk/server" storetypes "github.com/cosmos/cosmos-sdk/store/types" - "github.com/cosmos/cosmos-sdk/store/v2/multi" + "github.com/cosmos/cosmos-sdk/store/v2alpha1/multi" sdk "github.com/cosmos/cosmos-sdk/types" ) From bae2e7ad44e894cf74b8ea1191567b80ace03b62 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Wed, 30 Mar 2022 19:24:15 +0800 Subject: [PATCH 47/78] dbmapstore.Delete should raise smt.InvalidKeyError --- store/v2alpha1/smt/store.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/store/v2alpha1/smt/store.go b/store/v2alpha1/smt/store.go index fab3a4a09417..eb126fa9a27a 100644 --- a/store/v2alpha1/smt/store.go +++ b/store/v2alpha1/smt/store.go @@ -141,3 +141,14 @@ func (ms dbMapStore) Get(key []byte) ([]byte, error) { } return val, nil } + +func (ms dbMapStore) Delete(key []byte) error { + has, err := ms.DBReadWriter.Has(key) + if err != nil { + return err + } + if !has { + return &smt.InvalidKeyError{key} + } + return ms.DBReadWriter.Delete(key) +} From 1295d81024b6217e9cee512f71b3eead5c728dd2 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Thu, 7 Apr 2022 18:33:22 +0800 Subject: [PATCH 48/78] dispatching db constructor --- db/badgerdb/db.go | 8 ++++++ db/creator.go | 55 +++++++++++++++++++++++++++++++++++++ db/memdb/db.go | 7 +++++ db/rocksdb/db.go | 8 ++++++ server/constructors_test.go | 4 +-- server/mock/app.go | 1 - server/util.go | 9 +++--- server/util_test.go | 4 +-- 8 files changed, 86 insertions(+), 10 deletions(-) create mode 100644 db/creator.go diff --git a/db/badgerdb/db.go b/db/badgerdb/db.go index 646cede95219..89b10242ae1f 100644 --- a/db/badgerdb/db.go +++ b/db/badgerdb/db.go @@ -70,6 +70,14 @@ type versionManager struct { lastTs uint64 } +func init() { + creator := func(name string, dir string) (db.DBConnection, error) { + dir = filepath.Join(dir, name) + return NewDB(dir) + } + db.RegisterCreator(db.BadgerDBBackend, creator, false) +} + // NewDB creates or loads a BadgerDB key-value database inside the given directory. // If dir does not exist, it will be created. func NewDB(dir string) (*BadgerDB, error) { diff --git a/db/creator.go b/db/creator.go new file mode 100644 index 000000000000..3e13a4f919ad --- /dev/null +++ b/db/creator.go @@ -0,0 +1,55 @@ +package db + +import ( + "fmt" + "strings" +) + +type BackendType string + +// These are valid backend types. +const ( + // MemDBBackend represents in-memory key value store, which is mostly used + // for testing. + MemDBBackend BackendType = "memdb" + // RocksDBBackend represents rocksdb (uses github.com/cosmos/gorocksdb) + // - EXPERIMENTAL + // - requires gcc + // - use rocksdb build tag (go build -tags rocksdb) + RocksDBBackend BackendType = "rocksdb" + // BadgerDBBackend represents BadgerDB + // - pure Go + // - requires badgerdb build tag + BadgerDBBackend BackendType = "badgerdb" +) + +type DBCreator func(name string, dir string) (DBConnection, error) + +var backends = map[BackendType]DBCreator{} + +func RegisterCreator(backend BackendType, creator DBCreator, force bool) { + _, ok := backends[backend] + if !force && ok { + return + } + backends[backend] = creator +} + +// NewDB creates a new database of type backend with the given name. +func NewDB(name string, backend BackendType, dir string) (DBConnection, error) { + creator, ok := backends[backend] + if !ok { + keys := make([]string, 0, len(backends)) + for k := range backends { + keys = append(keys, string(k)) + } + return nil, fmt.Errorf("unknown db_backend %s, expected one of %v", + backend, strings.Join(keys, ",")) + } + + db, err := creator(name, dir) + if err != nil { + return nil, fmt.Errorf("failed to initialize database: %w", err) + } + return db, nil +} diff --git a/db/memdb/db.go b/db/memdb/db.go index 5e035773d04d..369dc95696ff 100644 --- a/db/memdb/db.go +++ b/db/memdb/db.go @@ -16,6 +16,13 @@ const ( bTreeDegree = 32 ) +func init() { + creator := func(name string, dir string) (db.DBConnection, error) { + return NewDB(), nil + } + db.RegisterCreator(db.MemDBBackend, creator, false) +} + // MemDB is an in-memory database backend using a B-tree for storage. // // For performance reasons, all given and returned keys and values are pointers to the in-memory diff --git a/db/rocksdb/db.go b/db/rocksdb/db.go index 611f47d0dd8a..c22fbc2a3b1c 100644 --- a/db/rocksdb/db.go +++ b/db/rocksdb/db.go @@ -68,6 +68,14 @@ type dbOptions struct { wo *gorocksdb.WriteOptions } +func init() { + creator := func(name string, dir string) (db.DBConnection, error) { + dir = filepath.Join(dir, name) + return NewDB(dir) + } + db.RegisterCreator(db.RocksDBBackend, creator, false) +} + // NewDB creates a new RocksDB key-value database with inside the given directory. // If dir does not exist, it will be created. func NewDB(dir string) (*dbManager, error) { diff --git a/server/constructors_test.go b/server/constructors_test.go index 646311b45720..154c30e37288 100644 --- a/server/constructors_test.go +++ b/server/constructors_test.go @@ -6,12 +6,12 @@ import ( "github.com/stretchr/testify/require" - dbm "github.com/tendermint/tm-db" + dbm "github.com/cosmos/cosmos-sdk/db" ) func Test_openDB(t *testing.T) { t.Parallel() - _, err := openDB(t.TempDir(), dbm.GoLevelDBBackend) + _, err := openDB(t.TempDir(), dbm.BadgerDBBackend) require.NoError(t, err) } diff --git a/server/mock/app.go b/server/mock/app.go index c7f7406861ca..6d41097ce28d 100644 --- a/server/mock/app.go +++ b/server/mock/app.go @@ -9,7 +9,6 @@ import ( abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/types" - dbm "github.com/tendermint/tm-db" bam "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/codec" diff --git a/server/util.go b/server/util.go index 2e101b23ee96..78c92cb5e8db 100644 --- a/server/util.go +++ b/server/util.go @@ -26,7 +26,6 @@ import ( "github.com/cosmos/cosmos-sdk/client/flags" dbm "github.com/cosmos/cosmos-sdk/db" - "github.com/cosmos/cosmos-sdk/db/badgerdb" "github.com/cosmos/cosmos-sdk/server/config" "github.com/cosmos/cosmos-sdk/server/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -365,7 +364,7 @@ func GetAppDBBackend(opts types.AppOptions) dbm.BackendType { if len(rv) != 0 { return dbm.BackendType(rv) } - return dbm.GoLevelDBBackend + return dbm.BadgerDBBackend } func skipInterface(iface net.Interface) bool { @@ -392,9 +391,9 @@ func addrToIP(addr net.Addr) net.IP { return ip } -func openDB(rootDir string) (dbm.DBConnection, error) { - dir := filepath.Join(rootDir, "data", "application") - return badgerdb.NewDB(dir) +func openDB(rootDir string, backendType dbm.BackendType) (dbm.DBConnection, error) { + dataDir := filepath.Join(rootDir, "data") + return dbm.NewDB("application", backendType, dataDir) } func openTraceWriter(traceWriterFile string) (w io.Writer, err error) { diff --git a/server/util_test.go b/server/util_test.go index 3a4cb8138c98..7c1e8cb152ff 100644 --- a/server/util_test.go +++ b/server/util_test.go @@ -14,10 +14,10 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" tmcfg "github.com/tendermint/tendermint/config" - dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" + dbm "github.com/cosmos/cosmos-sdk/db" "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/server/config" servertypes "github.com/cosmos/cosmos-sdk/server/types" @@ -466,7 +466,7 @@ func TestGetAppDBBackend(t *testing.T) { name: "nothing set", dbBack: "", opts: mapGetter{}, - exp: dbm.GoLevelDBBackend, + exp: dbm.BadgerDBBackend, }, { From fcf64ed4fcad33dbc9d7478d9e8c905254f3a4ee Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Thu, 7 Apr 2022 20:05:08 +0800 Subject: [PATCH 49/78] refactor server rollback cmd --- db/types.go | 4 ++++ server/rollback.go | 11 +++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/db/types.go b/db/types.go index 39dc365925e6..8d4395448eee 100644 --- a/db/types.go +++ b/db/types.go @@ -65,6 +65,10 @@ type DBConnection interface { // Returns an error if any open DBWriter transactions exist. Revert() error + // RevertTo reverts the DB state to the given version. Returns ErrVersionDoesNotExist for invalid versions. + // Returns an error if any open DBWriter transactions exist. + RevertTo(uint64) error + // Close closes the database connection. Close() error } diff --git a/server/rollback.go b/server/rollback.go index 1413967d54b7..01df238552e5 100644 --- a/server/rollback.go +++ b/server/rollback.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/store/rootmulti" "github.com/spf13/cobra" tmcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" ) @@ -35,9 +34,13 @@ application. if err != nil { return fmt.Errorf("failed to rollback tendermint state: %w", err) } - // rollback the multistore - cms := rootmulti.NewStore(db) - cms.RollbackToVersion(height) + // rollback the database + if err = db.RevertTo(uint64(height)); err != nil { + panic(err) + } + if err = db.Close(); err != nil { + panic(err) + } fmt.Printf("Rolled back state to height %d and hash %X", height, hash) return nil From 4dd37ca4d906d6bd1bce0485bcf8776c85353d8e Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Thu, 7 Apr 2022 20:06:04 +0800 Subject: [PATCH 50/78] go mod tidy --- go.mod | 1 + 1 file changed, 1 insertion(+) diff --git a/go.mod b/go.mod index 672d091c7894..5ae6d9659e7f 100644 --- a/go.mod +++ b/go.mod @@ -89,6 +89,7 @@ require ( github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.0.1 // indirect + github.com/google/flatbuffers v2.0.0+incompatible // indirect github.com/google/orderedcode v0.0.1 // indirect github.com/google/uuid v1.3.0 // indirect github.com/googleapis/gax-go/v2 v2.1.1 // indirect From 7eb1f9ee1548fbeeed99452c168fcda1f207c01f Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Fri, 8 Apr 2022 21:13:09 +0800 Subject: [PATCH 51/78] fix app-db-backend config --- db/creator.go | 2 +- server/config/config.go | 2 +- simapp/config.go | 6 +++--- types/utils.go | 20 +++----------------- 4 files changed, 8 insertions(+), 22 deletions(-) diff --git a/db/creator.go b/db/creator.go index 3e13a4f919ad..51de15172ed0 100644 --- a/db/creator.go +++ b/db/creator.go @@ -43,7 +43,7 @@ func NewDB(name string, backend BackendType, dir string) (DBConnection, error) { for k := range backends { keys = append(keys, string(k)) } - return nil, fmt.Errorf("unknown db_backend %s, expected one of %v", + return nil, fmt.Errorf("unknown App DB backend %s, expected one of %v", backend, strings.Join(keys, ",")) } diff --git a/server/config/config.go b/server/config/config.go index 8d4c003eae46..64e3aa22bd0d 100644 --- a/server/config/config.go +++ b/server/config/config.go @@ -217,7 +217,7 @@ func DefaultConfig() *Config { MinRetainBlocks: 0, IndexEvents: make([]string, 0), IAVLCacheSize: 781250, // 50 MB - AppDBBackend: "", + AppDBBackend: "badgerdb", }, Telemetry: telemetry.Config{ Enabled: false, diff --git a/simapp/config.go b/simapp/config.go index 48cc053f2533..498c53c4d2b6 100644 --- a/simapp/config.go +++ b/simapp/config.go @@ -22,7 +22,7 @@ var ( FlagCommitValue bool FlagOnOperationValue bool // TODO: Remove in favor of binary search for invariant violation FlagAllInvariantsValue bool - FlagDBBackendValue string + FlagTMDBBackendValue string FlagEnabledValue bool FlagVerboseValue bool @@ -47,7 +47,7 @@ func GetSimulatorFlags() { flag.BoolVar(&FlagCommitValue, "Commit", false, "have the simulation commit") flag.BoolVar(&FlagOnOperationValue, "SimulateEveryOperation", false, "run slow invariants every operation") flag.BoolVar(&FlagAllInvariantsValue, "PrintAllInvariants", false, "print all invariants if a broken invariant is found") - flag.StringVar(&FlagDBBackendValue, "DBBackend", "goleveldb", "custom db backend type") + flag.StringVar(&FlagTMDBBackendValue, "DBBackend", "badgerdb", "custom db backend type") // simulation flags flag.BoolVar(&FlagEnabledValue, "Enabled", false, "enable the simulation") @@ -73,6 +73,6 @@ func NewConfigFromFlags() simulation.Config { Commit: FlagCommitValue, OnOperation: FlagOnOperationValue, AllInvariants: FlagAllInvariantsValue, - DBBackend: FlagDBBackendValue, + DBBackend: FlagTMDBBackendValue, } } diff --git a/types/utils.go b/types/utils.go index dc13af6f9b21..7295dc1e567b 100644 --- a/types/utils.go +++ b/types/utils.go @@ -3,21 +3,20 @@ package types import ( "encoding/binary" "encoding/json" - "fmt" "time" - dbm "github.com/tendermint/tm-db" + "github.com/cosmos/cosmos-sdk/db" ) var ( // This is set at compile time. Could be cleveldb, defaults is goleveldb. DBBackend = "" // Deprecated: Use tendermint config's DBBackend value instead. - backend = dbm.GoLevelDBBackend + backend = db.BadgerDBBackend ) func init() { if len(DBBackend) != 0 { - backend = dbm.BackendType(DBBackend) + backend = db.BackendType(DBBackend) } } @@ -84,19 +83,6 @@ func ParseTimeBytes(bz []byte) (time.Time, error) { return t.UTC().Round(0), nil } -// NewLevelDB instantiate a new LevelDB instance according to DBBackend. -// -// Deprecated: Use NewDB (from "github.com/tendermint/tm-db") instead. Suggested backendType is tendermint config's DBBackend value. -func NewLevelDB(name, dir string) (db dbm.DB, err error) { - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("couldn't create db: %v", r) - } - }() - - return dbm.NewDB(name, backend, dir) -} - // copy bytes func CopyBytes(bz []byte) (ret []byte) { if bz == nil { From 0499da25d8d578b3bea55e1851b73a10abf94496 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Tue, 12 Apr 2022 14:32:02 +0800 Subject: [PATCH 52/78] comment typo --- store/v2alpha1/multi/proof_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/store/v2alpha1/multi/proof_test.go b/store/v2alpha1/multi/proof_test.go index cdccee65da3d..13c69a5d616c 100644 --- a/store/v2alpha1/multi/proof_test.go +++ b/store/v2alpha1/multi/proof_test.go @@ -11,7 +11,7 @@ import ( "github.com/cosmos/cosmos-sdk/store/v2alpha1/smt" ) -// We hash keys produce SMT paths, so reflect that here +// We hash keys to produce SMT paths, so reflect that here func keyPath(prefix, key string) string { hashed := sha256.Sum256([]byte(key)) return prefix + string(hashed[:]) From 924d2ec7c9ed8322366522a69ca437d8f4142638 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Tue, 12 Apr 2022 15:01:27 +0800 Subject: [PATCH 53/78] reorg multistore tests --- store/v2alpha1/multi/store_test.go | 188 ++++++++++++++--------------- 1 file changed, 94 insertions(+), 94 deletions(-) diff --git a/store/v2alpha1/multi/store_test.go b/store/v2alpha1/multi/store_test.go index 27c637e4c668..a1a64aad6b88 100644 --- a/store/v2alpha1/multi/store_test.go +++ b/store/v2alpha1/multi/store_test.go @@ -59,6 +59,38 @@ func newSubStoreWithData(t *testing.T, db dbm.DBConnection, storeData map[string return root, store } +func TestStoreParams(t *testing.T) { + opts := DefaultStoreParams() + // Fail with invalid types + require.Error(t, opts.RegisterSubstore(skey_1, types.StoreTypeDB)) + require.Error(t, opts.RegisterSubstore(skey_1, types.StoreTypeSMT)) + // Ensure that no prefix conflicts are allowed + require.NoError(t, opts.RegisterSubstore(skey_1, types.StoreTypePersistent)) + require.NoError(t, opts.RegisterSubstore(skey_2, types.StoreTypeMemory)) + require.NoError(t, opts.RegisterSubstore(skey_3b, types.StoreTypeTransient)) + require.Error(t, opts.RegisterSubstore(skey_1b, types.StoreTypePersistent)) + require.Error(t, opts.RegisterSubstore(skey_2b, types.StoreTypePersistent)) + require.Error(t, opts.RegisterSubstore(skey_3, types.StoreTypePersistent)) +} + +func TestMultiStoreBasic(t *testing.T) { + opts := DefaultStoreParams() + err := opts.RegisterSubstore(skey_1, types.StoreTypePersistent) + require.NoError(t, err) + db := memdb.NewDB() + store, err := NewStore(db, opts) + require.NoError(t, err) + + store_1 := store.GetKVStore(skey_1) + require.NotNil(t, store_1) + store_1.Set([]byte{0}, []byte{0}) + val := store_1.Get([]byte{0}) + require.Equal(t, []byte{0}, val) + store_1.Delete([]byte{0}) + val = store_1.Get([]byte{0}) + require.Equal(t, []byte(nil), val) +} + func TestGetSetHasDelete(t *testing.T) { _, store := newSubStoreWithData(t, memdb.NewDB(), alohaData) key := "hello" @@ -87,6 +119,67 @@ func TestGetSetHasDelete(t *testing.T) { require.Panics(t, func() { store.Set([]byte("key"), nil) }, "Set(nil value) should panic") } +func TestIterators(t *testing.T) { + _, store := newSubStoreWithData(t, memdb.NewDB(), map[string]string{ + string([]byte{0x00}): "0", + string([]byte{0x00, 0x00}): "0 0", + string([]byte{0x00, 0x01}): "0 1", + string([]byte{0x00, 0x02}): "0 2", + string([]byte{0x01}): "1", + }) + + var testCase = func(t *testing.T, iter types.Iterator, expected []string) { + var i int + for i = 0; iter.Valid(); iter.Next() { + expectedValue := expected[i] + value := iter.Value() + require.EqualValues(t, string(value), expectedValue) + i++ + } + require.Equal(t, len(expected), i) + } + + testCase(t, store.Iterator(nil, nil), + []string{"0", "0 0", "0 1", "0 2", "1"}) + testCase(t, store.Iterator([]byte{0}, nil), + []string{"0", "0 0", "0 1", "0 2", "1"}) + testCase(t, store.Iterator([]byte{0}, []byte{0, 1}), + []string{"0", "0 0"}) + testCase(t, store.Iterator([]byte{0}, []byte{1}), + []string{"0", "0 0", "0 1", "0 2"}) + testCase(t, store.Iterator([]byte{0, 1}, []byte{1}), + []string{"0 1", "0 2"}) + testCase(t, store.Iterator(nil, []byte{1}), + []string{"0", "0 0", "0 1", "0 2"}) + testCase(t, store.Iterator([]byte{0}, []byte{0}), []string{}) // start = end + testCase(t, store.Iterator([]byte{1}, []byte{0}), []string{}) // start > end + + testCase(t, store.ReverseIterator(nil, nil), + []string{"1", "0 2", "0 1", "0 0", "0"}) + testCase(t, store.ReverseIterator([]byte{0}, nil), + []string{"1", "0 2", "0 1", "0 0", "0"}) + testCase(t, store.ReverseIterator([]byte{0}, []byte{0, 1}), + []string{"0 0", "0"}) + testCase(t, store.ReverseIterator([]byte{0}, []byte{1}), + []string{"0 2", "0 1", "0 0", "0"}) + testCase(t, store.ReverseIterator([]byte{0, 1}, []byte{1}), + []string{"0 2", "0 1"}) + testCase(t, store.ReverseIterator(nil, []byte{1}), + []string{"0 2", "0 1", "0 0", "0"}) + testCase(t, store.ReverseIterator([]byte{0}, []byte{0}), []string{}) // start = end + testCase(t, store.ReverseIterator([]byte{1}, []byte{0}), []string{}) // start > end + + testCase(t, types.KVStorePrefixIterator(store, []byte{0}), + []string{"0", "0 0", "0 1", "0 2"}) + testCase(t, types.KVStoreReversePrefixIterator(store, []byte{0}), + []string{"0 2", "0 1", "0 0", "0"}) + + require.Panics(t, func() { store.Iterator([]byte{}, nil) }, "Iterator(empty key) should panic") + require.Panics(t, func() { store.Iterator(nil, []byte{}) }, "Iterator(empty key) should panic") + require.Panics(t, func() { store.ReverseIterator([]byte{}, nil) }, "Iterator(empty key) should panic") + require.Panics(t, func() { store.ReverseIterator(nil, []byte{}) }, "Iterator(empty key) should panic") +} + func TestConstructors(t *testing.T) { db := memdb.NewDB() @@ -150,67 +243,6 @@ func TestConstructors(t *testing.T) { }) } -func TestIterators(t *testing.T) { - _, store := newSubStoreWithData(t, memdb.NewDB(), map[string]string{ - string([]byte{0x00}): "0", - string([]byte{0x00, 0x00}): "0 0", - string([]byte{0x00, 0x01}): "0 1", - string([]byte{0x00, 0x02}): "0 2", - string([]byte{0x01}): "1", - }) - - var testCase = func(t *testing.T, iter types.Iterator, expected []string) { - var i int - for i = 0; iter.Valid(); iter.Next() { - expectedValue := expected[i] - value := iter.Value() - require.EqualValues(t, string(value), expectedValue) - i++ - } - require.Equal(t, len(expected), i) - } - - testCase(t, store.Iterator(nil, nil), - []string{"0", "0 0", "0 1", "0 2", "1"}) - testCase(t, store.Iterator([]byte{0}, nil), - []string{"0", "0 0", "0 1", "0 2", "1"}) - testCase(t, store.Iterator([]byte{0}, []byte{0, 1}), - []string{"0", "0 0"}) - testCase(t, store.Iterator([]byte{0}, []byte{1}), - []string{"0", "0 0", "0 1", "0 2"}) - testCase(t, store.Iterator([]byte{0, 1}, []byte{1}), - []string{"0 1", "0 2"}) - testCase(t, store.Iterator(nil, []byte{1}), - []string{"0", "0 0", "0 1", "0 2"}) - testCase(t, store.Iterator([]byte{0}, []byte{0}), []string{}) // start = end - testCase(t, store.Iterator([]byte{1}, []byte{0}), []string{}) // start > end - - testCase(t, store.ReverseIterator(nil, nil), - []string{"1", "0 2", "0 1", "0 0", "0"}) - testCase(t, store.ReverseIterator([]byte{0}, nil), - []string{"1", "0 2", "0 1", "0 0", "0"}) - testCase(t, store.ReverseIterator([]byte{0}, []byte{0, 1}), - []string{"0 0", "0"}) - testCase(t, store.ReverseIterator([]byte{0}, []byte{1}), - []string{"0 2", "0 1", "0 0", "0"}) - testCase(t, store.ReverseIterator([]byte{0, 1}, []byte{1}), - []string{"0 2", "0 1"}) - testCase(t, store.ReverseIterator(nil, []byte{1}), - []string{"0 2", "0 1", "0 0", "0"}) - testCase(t, store.ReverseIterator([]byte{0}, []byte{0}), []string{}) // start = end - testCase(t, store.ReverseIterator([]byte{1}, []byte{0}), []string{}) // start > end - - testCase(t, types.KVStorePrefixIterator(store, []byte{0}), - []string{"0", "0 0", "0 1", "0 2"}) - testCase(t, types.KVStoreReversePrefixIterator(store, []byte{0}), - []string{"0 2", "0 1", "0 0", "0"}) - - require.Panics(t, func() { store.Iterator([]byte{}, nil) }, "Iterator(empty key) should panic") - require.Panics(t, func() { store.Iterator(nil, []byte{}) }, "Iterator(empty key) should panic") - require.Panics(t, func() { store.ReverseIterator([]byte{}, nil) }, "Iterator(empty key) should panic") - require.Panics(t, func() { store.ReverseIterator(nil, []byte{}) }, "Iterator(empty key) should panic") -} - func TestCommit(t *testing.T) { testBasic := func(opts StoreParams) { db := memdb.NewDB() @@ -676,38 +708,6 @@ func TestQuery(t *testing.T) { }) } -func TestStoreParams(t *testing.T) { - opts := DefaultStoreParams() - // Fail with invalid types - require.Error(t, opts.RegisterSubstore(skey_1, types.StoreTypeDB)) - require.Error(t, opts.RegisterSubstore(skey_1, types.StoreTypeSMT)) - // Ensure that no prefix conflicts are allowed - require.NoError(t, opts.RegisterSubstore(skey_1, types.StoreTypePersistent)) - require.NoError(t, opts.RegisterSubstore(skey_2, types.StoreTypeMemory)) - require.NoError(t, opts.RegisterSubstore(skey_3b, types.StoreTypeTransient)) - require.Error(t, opts.RegisterSubstore(skey_1b, types.StoreTypePersistent)) - require.Error(t, opts.RegisterSubstore(skey_2b, types.StoreTypePersistent)) - require.Error(t, opts.RegisterSubstore(skey_3, types.StoreTypePersistent)) -} - -func TestMultiStoreBasic(t *testing.T) { - opts := DefaultStoreParams() - err := opts.RegisterSubstore(skey_1, types.StoreTypePersistent) - require.NoError(t, err) - db := memdb.NewDB() - store, err := NewStore(db, opts) - require.NoError(t, err) - - store_1 := store.GetKVStore(skey_1) - require.NotNil(t, store_1) - store_1.Set([]byte{0}, []byte{0}) - val := store_1.Get([]byte{0}) - require.Equal(t, []byte{0}, val) - store_1.Delete([]byte{0}) - val = store_1.Get([]byte{0}) - require.Equal(t, []byte(nil), val) -} - func TestGetVersion(t *testing.T) { db := memdb.NewDB() opts := storeParams123(t) @@ -743,7 +743,7 @@ func TestGetVersion(t *testing.T) { require.Equal(t, []byte{0}, subview.Get([]byte{0})) } -func TestMultiStoreMigration(t *testing.T) { +func TestStoreSchemaMigration(t *testing.T) { db := memdb.NewDB() opts := storeParams123(t) store, err := NewStore(db, opts) From 7f747abba267755c9309a9f65bceb46c253f5fa1 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Tue, 12 Apr 2022 14:59:04 +0800 Subject: [PATCH 54/78] check multistore key types --- store/v2alpha1/multi/store_test.go | 11 +++++++---- store/v2alpha1/types.go | 2 ++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/store/v2alpha1/multi/store_test.go b/store/v2alpha1/multi/store_test.go index a1a64aad6b88..c062b128821d 100644 --- a/store/v2alpha1/multi/store_test.go +++ b/store/v2alpha1/multi/store_test.go @@ -31,6 +31,9 @@ var ( skey_1b = types.NewKVStoreKey("store1b") skey_2b = types.NewKVStoreKey("store2b") skey_3b = types.NewKVStoreKey("store3b") + + skey_mem1 = types.NewMemoryStoreKey("mstore1") + skey_tran1 = types.NewTransientStoreKey("tstore1") ) func storeParams1(t *testing.T) StoreParams { @@ -905,10 +908,10 @@ func TestTrace(t *testing.T) { db := memdb.NewDB() opts := storeParams1(t) - require.NoError(t, opts.RegisterSubstore(skey_2, types.StoreTypeMemory)) - require.NoError(t, opts.RegisterSubstore(skey_3, types.StoreTypeTransient)) + require.NoError(t, opts.RegisterSubstore(skey_mem1, types.StoreTypeMemory)) + require.NoError(t, opts.RegisterSubstore(skey_tran1, types.StoreTypeTransient)) - store, err := NewStore(db, opts) + store, err := ctor(db, opts) require.NoError(t, err) store.SetTracingContext(tc) require.False(t, store.TracingEnabled()) @@ -917,7 +920,7 @@ func TestTrace(t *testing.T) { store.SetTracer(&buf) require.True(t, store.TracingEnabled()) - for _, skey := range []types.StoreKey{skey_1, skey_2, skey_3} { + for _, skey := range []types.StoreKey{skey_1, skey_mem1, skey_tran1} { buf.Reset() store.GetKVStore(skey).Get(key) require.Equal(t, expected_Get_missing, buf.String()) diff --git a/store/v2alpha1/types.go b/store/v2alpha1/types.go index 3fd04def2c78..a0aa13713f82 100644 --- a/store/v2alpha1/types.go +++ b/store/v2alpha1/types.go @@ -51,6 +51,8 @@ var ( PruneNothing = v1.PruneNothing NewKVStoreKey = v1.NewKVStoreKey + NewMemoryStoreKey = v1.NewMemoryStoreKey + NewTransientStoreKey = v1.NewTransientStoreKey PrefixEndBytes = v1.PrefixEndBytes KVStorePrefixIterator = v1.KVStorePrefixIterator KVStoreReversePrefixIterator = v1.KVStoreReversePrefixIterator From 7de3adf2d1c4c0fd46d1333fdd850326100078d6 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Tue, 12 Apr 2022 14:30:20 +0800 Subject: [PATCH 55/78] fix v1asv2 methods --- store/v2alpha1/multi/v1asv2.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/store/v2alpha1/multi/v1asv2.go b/store/v2alpha1/multi/v1asv2.go index 55d11b596fa9..7551819c7ab9 100644 --- a/store/v2alpha1/multi/v1asv2.go +++ b/store/v2alpha1/multi/v1asv2.go @@ -86,8 +86,8 @@ func (s *store1as2) SetInitialVersion(ver uint64) error { return s.Store.SetInitialVersion(int64(ver)) } -func (s *store1as2) SetTracer(w io.Writer) { s.SetTracer(w) } -func (s *store1as2) SetTracingContext(tc v2.TraceContext) { s.SetTracingContext(tc) } +func (s *store1as2) SetTracer(w io.Writer) { s.Store.SetTracer(w) } +func (s *store1as2) SetTracingContext(tc v2.TraceContext) { s.Store.SetTracingContext(tc) } func (s *store1as2) GetAllVersions() []int { panic("unsupported: GetAllVersions") } @@ -97,5 +97,7 @@ func (s cacheStore1as2) CacheWrap() v2.CacheMultiStore { return cacheStore1as2{s.CacheMultiStore.CacheMultiStore()} } -func (s cacheStore1as2) SetTracer(w io.Writer) { s.SetTracer(w) } -func (s cacheStore1as2) SetTracingContext(tc v2.TraceContext) { s.SetTracingContext(tc) } +func (s cacheStore1as2) SetTracer(w io.Writer) { s.CacheMultiStore.SetTracer(w) } +func (s cacheStore1as2) SetTracingContext(tc v2.TraceContext) { + s.CacheMultiStore.SetTracingContext(tc) +} From 47ab63fe0f8085beb5320eca0b85175c4570a5d2 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Tue, 12 Apr 2022 14:57:46 +0800 Subject: [PATCH 56/78] tests and fixes for v1asv2 adapter --- internal/db/tmdb_adapter.go | 10 ++-- store/v2alpha1/multi/store.go | 32 ++++++++----- store/v2alpha1/multi/store_test.go | 73 ++++++++++++++++++++++------- store/v2alpha1/multi/v1asv2.go | 32 ++++++++++++- store/v2alpha1/multi/v1asv2_test.go | 29 ++++++++++++ 5 files changed, 141 insertions(+), 35 deletions(-) create mode 100644 store/v2alpha1/multi/v1asv2_test.go diff --git a/internal/db/tmdb_adapter.go b/internal/db/tmdb_adapter.go index a36f5d023188..c7eb775efb79 100644 --- a/internal/db/tmdb_adapter.go +++ b/internal/db/tmdb_adapter.go @@ -15,7 +15,7 @@ import ( // compatibility with existing code that expects the old interface. type TmdbAdapter struct { dbm.DBReadWriter - db dbm.DBConnection + Connection dbm.DBConnection } type tmdbBatchAdapter struct { *TmdbAdapter @@ -29,7 +29,7 @@ var ( // ConnectionAsTmdb returns a tmdb.DB which wraps a DBConnection. func ConnectionAsTmdb(db dbm.DBConnection) *TmdbAdapter { return &TmdbAdapter{db.ReadWriter(), db} } -func (d *TmdbAdapter) Close() error { d.CloseTx(); return d.db.Close() } +func (d *TmdbAdapter) Close() error { d.CloseTx(); return d.Connection.Close() } func (d *TmdbAdapter) CloseTx() error { return d.DBReadWriter.Discard() } func (d *TmdbAdapter) sync() error { @@ -37,7 +37,7 @@ func (d *TmdbAdapter) sync() error { if err != nil { return err } - d.DBReadWriter = d.db.ReadWriter() + d.DBReadWriter = d.Connection.ReadWriter() return nil } func (d *TmdbAdapter) DeleteSync(k []byte) error { @@ -60,11 +60,11 @@ func (d *TmdbAdapter) Commit() (uint64, error) { if err != nil { return 0, err } - v, err := d.db.SaveNextVersion() + v, err := d.Connection.SaveNextVersion() if err != nil { return 0, err } - d.DBReadWriter = d.db.ReadWriter() + d.DBReadWriter = d.Connection.ReadWriter() return v, err } diff --git a/store/v2alpha1/multi/store.go b/store/v2alpha1/multi/store.go index fae729c4ae53..d8f50628f695 100644 --- a/store/v2alpha1/multi/store.go +++ b/store/v2alpha1/multi/store.go @@ -542,26 +542,32 @@ func (s *Store) Commit() types.CommitID { panic(err) } - // Prune if necessary - previous := cid.Version - 1 - if s.Pruning.Interval != 0 && cid.Version%int64(s.Pruning.Interval) == 0 { - // The range of newly prunable versions - lastPrunable := previous - int64(s.Pruning.KeepRecent) - firstPrunable := lastPrunable - int64(s.Pruning.Interval) - - for version := firstPrunable; version <= lastPrunable; version++ { - s.stateDB.DeleteVersion(uint64(version)) + pruneVersions(cid.Version, s.Pruning, func(ver int64) { + s.stateDB.DeleteVersion(uint64(ver)) - if s.StateCommitmentDB != nil { - s.StateCommitmentDB.DeleteVersion(uint64(version)) - } + if s.StateCommitmentDB != nil { + s.StateCommitmentDB.DeleteVersion(uint64(ver)) } - } + }) s.tran.Commit() return *cid } +// Performs necessary pruning via callback +func pruneVersions(current int64, opts types.PruningOptions, prune func(int64)) { + previous := current - 1 + if opts.Interval != 0 && current%int64(opts.Interval) == 0 { + // The range of newly prunable versions + lastPrunable := previous - int64(opts.KeepRecent) + firstPrunable := lastPrunable - int64(opts.Interval) + + for version := firstPrunable; version <= lastPrunable; version++ { + prune(version) + } + } +} + func (s *Store) getMerkleRoots() (ret map[string][]byte, err error) { ret = map[string][]byte{} for key := range s.schema { diff --git a/store/v2alpha1/multi/store_test.go b/store/v2alpha1/multi/store_test.go index c062b128821d..1bf881aea50f 100644 --- a/store/v2alpha1/multi/store_test.go +++ b/store/v2alpha1/multi/store_test.go @@ -36,6 +36,13 @@ var ( skey_tran1 = types.NewTransientStoreKey("tstore1") ) +// Factored out so the same tests can be run on Store and adaptor (v1asv2) +type storeConstructor = func(dbm.DBConnection, StoreParams) (types.CommitMultiStore, error) + +func multistoreConstructor(db dbm.DBConnection, params StoreParams) (types.CommitMultiStore, error) { + return NewStore(db, params) +} + func storeParams1(t *testing.T) StoreParams { opts := DefaultStoreParams() require.NoError(t, opts.RegisterSubstore(skey_1, types.StoreTypePersistent)) @@ -77,11 +84,14 @@ func TestStoreParams(t *testing.T) { } func TestMultiStoreBasic(t *testing.T) { + doTestMultiStoreBasic(t, multistoreConstructor) +} + +func doTestMultiStoreBasic(t *testing.T, ctor storeConstructor) { opts := DefaultStoreParams() err := opts.RegisterSubstore(skey_1, types.StoreTypePersistent) require.NoError(t, err) - db := memdb.NewDB() - store, err := NewStore(db, opts) + store, err := ctor(memdb.NewDB(), opts) require.NoError(t, err) store_1 := store.GetKVStore(skey_1) @@ -430,6 +440,10 @@ func sliceToSet(slice []uint64) map[uint64]struct{} { } func TestPruning(t *testing.T) { + doTestPruning(t, multistoreConstructor, true) +} + +func doTestPruning(t *testing.T, ctor storeConstructor, sepDBs bool) { // Save versions up to 10 and verify pruning at final commit testCases := []struct { types.PruningOptions @@ -442,11 +456,14 @@ func TestPruning(t *testing.T) { } for tci, tc := range testCases { - dbs := []dbm.DBConnection{memdb.NewDB(), memdb.NewDB()} opts := storeParams1(t) opts.Pruning = tc.PruningOptions - opts.StateCommitmentDB = dbs[1] - store, err := NewStore(dbs[0], opts) + dbs := []dbm.DBConnection{memdb.NewDB()} + if sepDBs { + dbs = append(dbs, memdb.NewDB()) + opts.StateCommitmentDB = dbs[1] + } + store, err := ctor(dbs[0], opts) require.NoError(t, err) s1 := store.GetKVStore(skey_1) @@ -481,7 +498,7 @@ func TestPruning(t *testing.T) { db := memdb.NewDB() opts := storeParams1(t) opts.Pruning = types.PruningOptions{0, 10} - store, err := NewStore(db, opts) + store, err := ctor(db, opts) require.NoError(t, err) for i := byte(1); i <= 20; i++ { @@ -507,9 +524,13 @@ func TestPruning(t *testing.T) { } } +func TestQuery(t *testing.T) { + doTestQuery(t, multistoreConstructor) +} + func queryPath(skey types.StoreKey, endp string) string { return "/" + skey.Name() + endp } -func TestQuery(t *testing.T) { +func doTestQuery(t *testing.T, ctor storeConstructor) { k1, v1 := []byte("k1"), []byte("v1") k2, v2 := []byte("k2"), []byte("v2") v3 := []byte("v3") @@ -712,9 +733,13 @@ func TestQuery(t *testing.T) { } func TestGetVersion(t *testing.T) { + doTestGetVersion(t, multistoreConstructor) +} + +func doTestGetVersion(t *testing.T, ctor storeConstructor) { db := memdb.NewDB() opts := storeParams123(t) - store, err := NewStore(db, opts) + store, err := ctor(db, opts) require.NoError(t, err) cid := store.Commit() @@ -747,9 +772,13 @@ func TestGetVersion(t *testing.T) { } func TestStoreSchemaMigration(t *testing.T) { + doTestStoreSchemaMigration(t, multistoreConstructor) +} + +func doTestStoreSchemaMigration(t *testing.T, ctor storeConstructor) { db := memdb.NewDB() opts := storeParams123(t) - store, err := NewStore(db, opts) + store, err := ctor(db, opts) require.NoError(t, err) // write some data in all stores @@ -776,7 +805,7 @@ func TestStoreSchemaMigration(t *testing.T) { var migratedID types.CommitID // Load without changes and make sure it is sensible - store, err = NewStore(db, opts) + store, err = ctor(db, opts) require.NoError(t, err) // let's query data to see it was saved properly @@ -799,7 +828,7 @@ func TestStoreSchemaMigration(t *testing.T) { // store must be loaded with post-migration schema, so this fails opts := storeParams123(t) opts.Upgrades = upgrades - store, err = NewStore(db, opts) + store, err = ctor(db, opts) require.Error(t, err) opts = DefaultStoreParams() @@ -807,7 +836,7 @@ func TestStoreSchemaMigration(t *testing.T) { require.NoError(t, opts.RegisterSubstore(skey_1, types.StoreTypePersistent)) require.NoError(t, opts.RegisterSubstore(skey_2b, types.StoreTypePersistent)) require.NoError(t, opts.RegisterSubstore(skey_4, types.StoreTypePersistent)) - store, err = NewStore(db, opts) + store, err = ctor(db, opts) require.NoError(t, err) // store1 was not changed @@ -846,7 +875,7 @@ func TestStoreSchemaMigration(t *testing.T) { t.Run("reload after migrations", func(t *testing.T) { // fail to load the migrated store with the old schema - store, err = NewStore(db, storeParams123(t)) + store, err = ctor(db, storeParams123(t)) require.Error(t, err) // pass in a schema reflecting the migrations @@ -854,7 +883,7 @@ func TestStoreSchemaMigration(t *testing.T) { require.NoError(t, migratedOpts.RegisterSubstore(skey_1, types.StoreTypePersistent)) require.NoError(t, migratedOpts.RegisterSubstore(skey_2b, types.StoreTypePersistent)) require.NoError(t, migratedOpts.RegisterSubstore(skey_4, types.StoreTypePersistent)) - store, err = NewStore(db, migratedOpts) + store, err = ctor(db, migratedOpts) require.NoError(t, err) require.Equal(t, migratedID, store.LastCommitID()) @@ -896,6 +925,10 @@ func TestStoreSchemaMigration(t *testing.T) { } func TestTrace(t *testing.T) { + doTestTrace(t, multistoreConstructor) +} + +func doTestTrace(t *testing.T, ctor storeConstructor) { key, value := []byte("test-key"), []byte("test-value") tc := types.TraceContext(map[string]interface{}{"blockHeight": 64}) @@ -953,9 +986,13 @@ func TestTrace(t *testing.T) { } func TestTraceConcurrency(t *testing.T) { + doTestTraceConcurrency(t, multistoreConstructor) +} + +func doTestTraceConcurrency(t *testing.T, ctor storeConstructor) { db := memdb.NewDB() opts := storeParams123(t) - store, err := NewStore(db, opts) + store, err := ctor(db, opts) require.NoError(t, err) b := &bytes.Buffer{} @@ -1000,6 +1037,10 @@ func TestTraceConcurrency(t *testing.T) { } func TestListeners(t *testing.T) { + doTestListeners(t, multistoreConstructor) +} + +func doTestListeners(t *testing.T, ctor storeConstructor) { kvPairs := []types.KVPair{ {Key: []byte{1}, Value: []byte("v1")}, {Key: []byte{2}, Value: []byte("v2")}, @@ -1036,7 +1077,7 @@ func TestListeners(t *testing.T) { require.NoError(t, opts.RegisterSubstore(skey_2, types.StoreTypeMemory)) require.NoError(t, opts.RegisterSubstore(skey_3, types.StoreTypeTransient)) - store, err := NewStore(db, opts) + store, err := ctor(db, opts) require.NoError(t, err) for i, tc := range testCases { diff --git a/store/v2alpha1/multi/v1asv2.go b/store/v2alpha1/multi/v1asv2.go index 7551819c7ab9..4b19ecf5ef38 100644 --- a/store/v2alpha1/multi/v1asv2.go +++ b/store/v2alpha1/multi/v1asv2.go @@ -25,6 +25,12 @@ type cacheStore1as2 struct { v1.CacheMultiStore } +type viewStore1as2 struct{ cacheStore1as2 } + +type readonlyKVStore struct { + v2.KVStore +} + // NewV1MultiStoreAsV2 constructs a v1 CommitMultiStore from v2.StoreParams func NewV1MultiStoreAsV2(database db.DBConnection, opts StoreParams) (v2.CommitMultiStore, error) { adapter := dbutil.ConnectionAsTmdb(database) @@ -62,9 +68,17 @@ func NewV1MultiStoreAsV2(database db.DBConnection, opts StoreParams) (v2.CommitM func (s *store1as2) CacheWrap() v2.CacheMultiStore { return cacheStore1as2{s.CacheMultiStore()} } + func (s *store1as2) GetVersion(ver int64) (v2.MultiStore, error) { ret, err := s.CacheMultiStoreWithVersion(ver) - return cacheStore1as2{ret}, err + versions, err := s.database.Connection.Versions() + if err != nil { + return nil, err + } + if !versions.Exists(uint64(ver)) { + return nil, db.ErrVersionDoesNotExist + } + return viewStore1as2{cacheStore1as2{ret}}, err } // CommitMultiStore @@ -79,6 +93,9 @@ func (s *store1as2) Commit() v2.CommitID { if err != nil { panic(err) } + pruneVersions(ret.Version, s.GetPruning(), func(ver int64) { + s.database.Connection.DeleteVersion(uint64(ver)) + }) return ret } @@ -101,3 +118,16 @@ func (s cacheStore1as2) SetTracer(w io.Writer) { s.CacheMultiStore.SetTracer(w) func (s cacheStore1as2) SetTracingContext(tc v2.TraceContext) { s.CacheMultiStore.SetTracingContext(tc) } + +func (s viewStore1as2) GetKVStore(skey v2.StoreKey) v2.KVStore { + sub := s.CacheMultiStore.GetKVStore(skey) + return readonlyKVStore{sub} +} + +func (kv readonlyKVStore) Set(key []byte, value []byte) { + panic(ErrReadOnly) +} + +func (kv readonlyKVStore) Delete(key []byte) { + panic(ErrReadOnly) +} diff --git a/store/v2alpha1/multi/v1asv2_test.go b/store/v2alpha1/multi/v1asv2_test.go new file mode 100644 index 000000000000..9e1c057712a0 --- /dev/null +++ b/store/v2alpha1/multi/v1asv2_test.go @@ -0,0 +1,29 @@ +package multi + +import ( + "testing" +) + +func TestV1asV2MultiStoreBasic(t *testing.T) { + doTestMultiStoreBasic(t, NewV1MultiStoreAsV2) +} + +func TestV1asV2GetVersion(t *testing.T) { + doTestGetVersion(t, NewV1MultiStoreAsV2) +} + +func TestV1asV2Pruning(t *testing.T) { + doTestPruning(t, NewV1MultiStoreAsV2, false) +} + +func TestV1asV2Trace(t *testing.T) { + doTestTrace(t, NewV1MultiStoreAsV2) +} + +func TestV1asV2TraceConcurrency(t *testing.T) { + doTestTraceConcurrency(t, NewV1MultiStoreAsV2) +} + +func TestV1asV2Listeners(t *testing.T) { + doTestListeners(t, NewV1MultiStoreAsV2) +} From 15681980ede45877a23750ed2d318d194af8c3a2 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Tue, 12 Apr 2022 17:46:56 +0800 Subject: [PATCH 57/78] force mem & transient stores to use bespoke key type matching v1 behavior --- store/v2alpha1/multi/store.go | 20 +++++++++++++++----- store/v2alpha1/multi/store_test.go | 19 ++++++++++++------- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/store/v2alpha1/multi/store.go b/store/v2alpha1/multi/store.go index d8f50628f695..e5b8e4bd72ee 100644 --- a/store/v2alpha1/multi/store.go +++ b/store/v2alpha1/multi/store.go @@ -148,8 +148,22 @@ func DefaultStoreParams() StoreParams { } } -// func (pr *SchemaBuilder) registerName(key string, typ types.StoreType) error { func (par *StoreParams) RegisterSubstore(skey types.StoreKey, typ types.StoreType) error { + if !validSubStoreType(typ) { + return fmt.Errorf("StoreType not supported: %v", typ) + } + var ok bool + switch typ { + case types.StoreTypePersistent: + _, ok = skey.(*types.KVStoreKey) + case types.StoreTypeMemory: + _, ok = skey.(*types.MemoryStoreKey) + case types.StoreTypeTransient: + _, ok = skey.(*types.TransientStoreKey) + } + if !ok { + return fmt.Errorf("invalid StoreKey for %v: %T", typ, skey) + } if err := par.registerName(skey.Name(), typ); err != nil { return err } @@ -893,10 +907,6 @@ func (reg *SchemaBuilder) storeInfo(key string) (sst types.StoreType, ix int, er // registerName registers a store key by name only func (reg *SchemaBuilder) registerName(key string, typ types.StoreType) error { - if !validSubStoreType(typ) { - return fmt.Errorf("StoreType not supported: %v", typ) - } - // Find the neighboring reserved prefix, and check for duplicates and conflicts i, has := binarySearch(reg.reserved, key) if has { diff --git a/store/v2alpha1/multi/store_test.go b/store/v2alpha1/multi/store_test.go index 1bf881aea50f..b987f4a3993f 100644 --- a/store/v2alpha1/multi/store_test.go +++ b/store/v2alpha1/multi/store_test.go @@ -71,13 +71,18 @@ func newSubStoreWithData(t *testing.T, db dbm.DBConnection, storeData map[string func TestStoreParams(t *testing.T) { opts := DefaultStoreParams() - // Fail with invalid types + // Fail with invalid type enum require.Error(t, opts.RegisterSubstore(skey_1, types.StoreTypeDB)) require.Error(t, opts.RegisterSubstore(skey_1, types.StoreTypeSMT)) + // Mem & tranient stores require a bespoke concrete type + require.Error(t, opts.RegisterSubstore(skey_1, types.StoreTypeMemory)) + require.Error(t, opts.RegisterSubstore(skey_1, types.StoreTypeTransient)) + require.NoError(t, opts.RegisterSubstore(skey_mem1, types.StoreTypeMemory)) + require.NoError(t, opts.RegisterSubstore(skey_tran1, types.StoreTypeTransient)) // Ensure that no prefix conflicts are allowed require.NoError(t, opts.RegisterSubstore(skey_1, types.StoreTypePersistent)) - require.NoError(t, opts.RegisterSubstore(skey_2, types.StoreTypeMemory)) - require.NoError(t, opts.RegisterSubstore(skey_3b, types.StoreTypeTransient)) + require.NoError(t, opts.RegisterSubstore(skey_2, types.StoreTypePersistent)) + require.NoError(t, opts.RegisterSubstore(skey_3b, types.StoreTypePersistent)) require.Error(t, opts.RegisterSubstore(skey_1b, types.StoreTypePersistent)) require.Error(t, opts.RegisterSubstore(skey_2b, types.StoreTypePersistent)) require.Error(t, opts.RegisterSubstore(skey_3, types.StoreTypePersistent)) @@ -1060,12 +1065,12 @@ func doTestListeners(t *testing.T, ctor storeConstructor) { { key: kvPairs[1].Key, value: kvPairs[1].Value, - skey: skey_2, + skey: skey_mem1, }, { key: kvPairs[2].Key, value: kvPairs[2].Value, - skey: skey_3, + skey: skey_tran1, }, } @@ -1074,8 +1079,8 @@ func doTestListeners(t *testing.T, ctor storeConstructor) { db := memdb.NewDB() opts := storeParams1(t) - require.NoError(t, opts.RegisterSubstore(skey_2, types.StoreTypeMemory)) - require.NoError(t, opts.RegisterSubstore(skey_3, types.StoreTypeTransient)) + require.NoError(t, opts.RegisterSubstore(skey_mem1, types.StoreTypeMemory)) + require.NoError(t, opts.RegisterSubstore(skey_tran1, types.StoreTypeTransient)) store, err := ctor(db, opts) require.NoError(t, err) From bb15cd3df09f43a03ee64f7d671e992fce4cd41e Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Tue, 19 Apr 2022 19:43:51 +0800 Subject: [PATCH 58/78] godoc copyedit --- store/dbadapter/store.go | 8 ++++---- store/v2alpha1/dbadapter/store.go | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/store/dbadapter/store.go b/store/dbadapter/store.go index 2f0ceb5df54a..815c1f12ec1c 100644 --- a/store/dbadapter/store.go +++ b/store/dbadapter/store.go @@ -36,7 +36,7 @@ func (dsa Store) Has(key []byte) bool { return ok } -// Set wraps the underlying DB's Set method panicing on error. +// Set wraps the underlying DB's Set method panicking on error. func (dsa Store) Set(key, value []byte) { types.AssertValidKey(key) if err := dsa.DB.Set(key, value); err != nil { @@ -44,14 +44,14 @@ func (dsa Store) Set(key, value []byte) { } } -// Delete wraps the underlying DB's Delete method panicing on error. +// Delete wraps the underlying DB's Delete method panicking on error. func (dsa Store) Delete(key []byte) { if err := dsa.DB.Delete(key); err != nil { panic(err) } } -// Iterator wraps the underlying DB's Iterator method panicing on error. +// Iterator wraps the underlying DB's Iterator method panicking on error. func (dsa Store) Iterator(start, end []byte) types.Iterator { iter, err := dsa.DB.Iterator(start, end) if err != nil { @@ -61,7 +61,7 @@ func (dsa Store) Iterator(start, end []byte) types.Iterator { return iter } -// ReverseIterator wraps the underlying DB's ReverseIterator method panicing on error. +// ReverseIterator wraps the underlying DB's ReverseIterator method panicking on error. func (dsa Store) ReverseIterator(start, end []byte) types.Iterator { iter, err := dsa.DB.ReverseIterator(start, end) if err != nil { diff --git a/store/v2alpha1/dbadapter/store.go b/store/v2alpha1/dbadapter/store.go index 1cbd6c83585b..9745c7597615 100644 --- a/store/v2alpha1/dbadapter/store.go +++ b/store/v2alpha1/dbadapter/store.go @@ -18,7 +18,7 @@ type Store struct { DB dbm.DBReadWriter } -// Get wraps the underlying DB's Get method panicing on error. +// Get wraps the underlying DB's Get method, panicking on error. func (dsa Store) Get(key []byte) []byte { v, err := dsa.DB.Get(key) if err != nil { @@ -28,7 +28,7 @@ func (dsa Store) Get(key []byte) []byte { return v } -// Has wraps the underlying DB's Has method panicing on error. +// Has wraps the underlying DB's Has method, panicking on error. func (dsa Store) Has(key []byte) bool { ok, err := dsa.DB.Has(key) if err != nil { @@ -38,7 +38,7 @@ func (dsa Store) Has(key []byte) bool { return ok } -// Set wraps the underlying DB's Set method panicing on error. +// Set wraps the underlying DB's Set method, panicking on error. func (dsa Store) Set(key, value []byte) { types.AssertValidKey(key) if err := dsa.DB.Set(key, value); err != nil { @@ -46,14 +46,14 @@ func (dsa Store) Set(key, value []byte) { } } -// Delete wraps the underlying DB's Delete method panicing on error. +// Delete wraps the underlying DB's Delete method, panicking on error. func (dsa Store) Delete(key []byte) { if err := dsa.DB.Delete(key); err != nil { panic(err) } } -// Iterator wraps the underlying DB's Iterator method panicing on error. +// Iterator wraps the underlying DB's Iterator method, panicking on error. func (dsa Store) Iterator(start, end []byte) types.Iterator { iter, err := dsa.DB.Iterator(start, end) if err != nil { @@ -62,7 +62,7 @@ func (dsa Store) Iterator(start, end []byte) types.Iterator { return dbutil.DBToStoreIterator(iter) } -// ReverseIterator wraps the underlying DB's ReverseIterator method panicing on error. +// ReverseIterator wraps the underlying DB's ReverseIterator method, panicking on error. func (dsa Store) ReverseIterator(start, end []byte) types.Iterator { iter, err := dsa.DB.ReverseIterator(start, end) if err != nil { From e103a634831813d770c041f3dd2c0918eaf0c93b Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Tue, 19 Apr 2022 19:44:30 +0800 Subject: [PATCH 59/78] smt store test nits --- store/v2alpha1/smt/ics23_test.go | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/store/v2alpha1/smt/ics23_test.go b/store/v2alpha1/smt/ics23_test.go index a94cc6f0a477..5667dbe16e78 100644 --- a/store/v2alpha1/smt/ics23_test.go +++ b/store/v2alpha1/smt/ics23_test.go @@ -6,6 +6,7 @@ import ( ics23 "github.com/confio/ics23/go" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/cosmos/cosmos-sdk/db/memdb" store "github.com/cosmos/cosmos-sdk/store/v2alpha1/smt" @@ -33,7 +34,7 @@ func TestProofICS23(t *testing.T) { nonexist := proof.GetNonexist() assert.Nil(t, nonexist) exist := proof.GetExist() - assert.NotNil(t, exist) + require.NotNil(t, exist) assert.Equal(t, 0, len(exist.Path)) assert.NoError(t, exist.Verify(ics23.SmtSpec, s.Root(), path01[:], val1)) @@ -41,18 +42,18 @@ func TestProofICS23(t *testing.T) { proof, err = s.GetProofICS23(key00) // When leaf is leftmost node assert.NoError(t, err) nonexist = proof.GetNonexist() - assert.NotNil(t, nonexist) + require.NotNil(t, nonexist) assert.Nil(t, nonexist.Left) assert.Equal(t, path00[:], nonexist.Key) - assert.NotNil(t, nonexist.Right) + require.NotNil(t, nonexist.Right) assert.Equal(t, 0, len(nonexist.Right.Path)) assert.NoError(t, nonexist.Verify(ics23.SmtSpec, s.Root(), path00[:])) proof, err = s.GetProofICS23(key10) // When rightmost assert.NoError(t, err) nonexist = proof.GetNonexist() - assert.NotNil(t, nonexist) - assert.NotNil(t, nonexist.Left) + require.NotNil(t, nonexist) + require.NotNil(t, nonexist.Left) assert.Equal(t, 0, len(nonexist.Left.Path)) assert.Nil(t, nonexist.Right) assert.NoError(t, nonexist.Verify(ics23.SmtSpec, s.Root(), path10[:])) @@ -63,11 +64,11 @@ func TestProofICS23(t *testing.T) { proof, err = s.GetProofICS23(key10) // In between two keys assert.NoError(t, err) nonexist = proof.GetNonexist() - assert.NotNil(t, nonexist) + require.NotNil(t, nonexist) assert.Equal(t, path10[:], nonexist.Key) - assert.NotNil(t, nonexist.Left) + require.NotNil(t, nonexist.Left) assert.Equal(t, 1, len(nonexist.Left.Path)) - assert.NotNil(t, nonexist.Right) + require.NotNil(t, nonexist.Right) assert.Equal(t, 1, len(nonexist.Right.Path)) assert.NoError(t, nonexist.Verify(ics23.SmtSpec, s.Root(), path10[:])) @@ -78,9 +79,9 @@ func TestProofICS23(t *testing.T) { assert.NoError(t, err) nonexist = proof.GetNonexist() assert.Equal(t, path10[:], nonexist.Key) - assert.NotNil(t, nonexist.Left) + require.NotNil(t, nonexist.Left) assert.Equal(t, 1, len(nonexist.Left.Path)) - assert.NotNil(t, nonexist.Right) + require.NotNil(t, nonexist.Right) assert.Equal(t, 1, len(nonexist.Right.Path)) assert.NoError(t, nonexist.Verify(ics23.SmtSpec, s.Root(), path10[:])) From 1a37cc229c2bd27f83061c415dda4f21b4aca795 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Mon, 18 Apr 2022 19:37:44 +0800 Subject: [PATCH 60/78] [dev] update SMT library --- go.mod | 3 + store/v2alpha1/multi/proof_test.go | 2 +- store/v2alpha1/multi/store.go | 47 +++++++-------- store/v2alpha1/multi/view_store.go | 10 ++-- store/v2alpha1/smt/ics23.go | 8 +-- store/v2alpha1/smt/ics23_test.go | 5 +- store/v2alpha1/smt/proof.go | 93 ------------------------------ store/v2alpha1/smt/proof_test.go | 69 ---------------------- store/v2alpha1/smt/store.go | 82 +++++++++++++++----------- store/v2alpha1/smt/store_test.go | 7 ++- 10 files changed, 93 insertions(+), 233 deletions(-) delete mode 100644 store/v2alpha1/smt/proof.go delete mode 100644 store/v2alpha1/smt/proof_test.go diff --git a/go.mod b/go.mod index 5ae6d9659e7f..021708e3aeb0 100644 --- a/go.mod +++ b/go.mod @@ -158,3 +158,6 @@ replace github.com/gin-gonic/gin => github.com/gin-gonic/gin v1.7.0 replace github.com/cosmos/cosmos-sdk/db => ./db retract v0.43.0 + +// DEV +replace github.com/lazyledger/smt => ../smt diff --git a/store/v2alpha1/multi/proof_test.go b/store/v2alpha1/multi/proof_test.go index 13c69a5d616c..dc9e38fa15ec 100644 --- a/store/v2alpha1/multi/proof_test.go +++ b/store/v2alpha1/multi/proof_test.go @@ -20,7 +20,7 @@ func keyPath(prefix, key string) string { func TestVerifySMTStoreProof(t *testing.T) { // Create main tree for testing. txn := memdb.NewDB().ReadWriter() - store := smt.NewStore(txn) + store := smt.NewStore(smt.StoreParams{TreeData: txn}) store.Set([]byte("MYKEY"), []byte("MYVALUE")) root := store.Root() diff --git a/store/v2alpha1/multi/store.go b/store/v2alpha1/multi/store.go index e5b8e4bd72ee..f7b998d9ce90 100644 --- a/store/v2alpha1/multi/store.go +++ b/store/v2alpha1/multi/store.go @@ -503,23 +503,27 @@ func (rs *Store) getSubstore(key string) (*substore, error) { pfx := substorePrefix(key) stateRW := prefixdb.NewPrefixReadWriter(rs.stateTxn, pfx) stateCommitmentRW := prefixdb.NewPrefixReadWriter(rs.stateCommitmentTxn, pfx) - var stateCommitmentStore *smt.Store + var stateCommitmentStore *smt.Store + data := prefixdb.NewPrefixReadWriter(stateRW, dataPrefix) rootHash, err := stateRW.Get(substoreMerkleRootKey) if err != nil { return nil, err } if rootHash != nil { - stateCommitmentStore = loadSMT(stateCommitmentRW, rootHash) + stateCommitmentStore = loadSMT(stateCommitmentRW, data, rootHash) } else { - smtdb := prefixdb.NewPrefixReadWriter(stateCommitmentRW, smtPrefix) - stateCommitmentStore = smt.NewStore(smtdb) + params := smt.StoreParams{ + TreeData: prefixdb.NewPrefixReadWriter(stateCommitmentRW, smtPrefix), + ValueData: data, + } + stateCommitmentStore = smt.NewStore(params) } return &substore{ root: rs, name: key, - dataBucket: prefixdb.NewPrefixReadWriter(stateRW, dataPrefix), + dataBucket: data, stateCommitmentStore: stateCommitmentStore, }, nil } @@ -530,7 +534,7 @@ func (s *substore) refresh(rootHash []byte) { stateRW := prefixdb.NewPrefixReadWriter(s.root.stateTxn, pfx) stateCommitmentRW := prefixdb.NewPrefixReadWriter(s.root.stateCommitmentTxn, pfx) s.dataBucket = prefixdb.NewPrefixReadWriter(stateRW, dataPrefix) - s.stateCommitmentStore = loadSMT(stateCommitmentRW, rootHash) + s.stateCommitmentStore = loadSMT(stateCommitmentRW, s.dataBucket, rootHash) } // Commit implements Committer. @@ -582,26 +586,20 @@ func pruneVersions(current int64, opts types.PruningOptions, prune func(int64)) } } -func (s *Store) getMerkleRoots() (ret map[string][]byte, err error) { - ret = map[string][]byte{} +// Calculates root hashes and commits to DB. Does not verify target version or perform pruning. +func (s *Store) commit(target uint64) (id *types.CommitID, err error) { + storeHashes := make(map[string][]byte, len(s.schema)) for key := range s.schema { sub, has := s.substoreCache[key.Name()] if !has { - sub, err = s.getSubstore(key.Name()) - if err != nil { + if sub, err = s.getSubstore(key.Name()); err != nil { return } } - ret[key.Name()] = sub.stateCommitmentStore.Root() - } - return -} - -// Calculates root hashes and commits to DB. Does not verify target version or perform pruning. -func (s *Store) commit(target uint64) (id *types.CommitID, err error) { - storeHashes, err := s.getMerkleRoots() - if err != nil { - return + if err = sub.stateCommitmentStore.Commit(); err != nil { + return + } + storeHashes[key.Name()] = sub.stateCommitmentStore.Root() } // Update substore Merkle roots for key, storeHash := range storeHashes { @@ -831,9 +829,12 @@ func (rs *Store) Query(req abci.RequestQuery) (res abci.ResponseQuery) { return res } -func loadSMT(stateCommitmentTxn dbm.DBReadWriter, root []byte) *smt.Store { - smtdb := prefixdb.NewPrefixReadWriter(stateCommitmentTxn, smtPrefix) - return smt.LoadStore(smtdb, root) +func loadSMT(sc, data dbm.DBReadWriter, root []byte) *smt.Store { + params := smt.StoreParams{ + TreeData: prefixdb.NewPrefixReadWriter(sc, smtPrefix), + ValueData: data, + } + return smt.LoadStore(params, root) } // Returns closest index and whether it's a match diff --git a/store/v2alpha1/multi/view_store.go b/store/v2alpha1/multi/view_store.go index 5b794ed046cc..48356eacb445 100644 --- a/store/v2alpha1/multi/view_store.go +++ b/store/v2alpha1/multi/view_store.go @@ -57,11 +57,13 @@ func (vs *viewStore) getSubstore(key string) (*viewSubstore, error) { if err != nil { return nil, err } + data := prefixdb.NewPrefixReader(stateR, dataPrefix) return &viewSubstore{ - root: vs, - name: key, - dataBucket: prefixdb.NewPrefixReader(stateR, dataPrefix), - stateCommitmentStore: loadSMT(dbm.ReaderAsReadWriter(stateCommitmentR), rootHash), + root: vs, + name: key, + dataBucket: data, + stateCommitmentStore: loadSMT( + dbm.ReaderAsReadWriter(stateCommitmentR), dbm.ReaderAsReadWriter(data), rootHash), }, nil } diff --git a/store/v2alpha1/smt/ics23.go b/store/v2alpha1/smt/ics23.go index 31d78f993dc0..22d98faa8f43 100644 --- a/store/v2alpha1/smt/ics23.go +++ b/store/v2alpha1/smt/ics23.go @@ -15,12 +15,12 @@ import ( func createIcs23Proof(store *Store, key []byte) (*ics23.CommitmentProof, error) { ret := &ics23.CommitmentProof{} path := sha256.Sum256(key) - has, err := store.tree.Has(key) + valueHash, err := store.tree.Get(key) if err != nil { return nil, err } - if has { // Membership proof - value, err := store.values.Get(path[:]) + if valueHash != nil { // Membership proof + value, err := store.values.Get(key) if err != nil { return nil, err } @@ -52,7 +52,7 @@ func toNonExistenceProof(store *Store, path [32]byte) (*ics23.NonExistenceProof, getNext := func(it dbm.Iterator) (*ics23.ExistenceProof, error) { defer it.Close() if it.Next() { - value, err := store.values.Get(it.Key()) + value, err := store.values.Get(it.Value()) if err != nil { return nil, err } diff --git a/store/v2alpha1/smt/ics23_test.go b/store/v2alpha1/smt/ics23_test.go index 5667dbe16e78..14fa8889675b 100644 --- a/store/v2alpha1/smt/ics23_test.go +++ b/store/v2alpha1/smt/ics23_test.go @@ -14,7 +14,7 @@ import ( func TestProofICS23(t *testing.T) { txn := memdb.NewDB().ReadWriter() - s := store.NewStore(txn) + s := store.NewStore(store.StoreParams{TreeData: txn}) // pick keys whose hashes begin with different bits key00 := []byte("foo") // 00101100 = sha256(foo)[0] key01 := []byte("bill") // 01100010 @@ -74,7 +74,8 @@ func TestProofICS23(t *testing.T) { // Make sure proofs work with a loaded store root := s.Root() - s = store.LoadStore(txn, root) + assert.NoError(t, s.Commit()) + s = store.LoadStore(store.StoreParams{TreeData: txn}, root) proof, err = s.GetProofICS23(key10) assert.NoError(t, err) nonexist = proof.GetNonexist() diff --git a/store/v2alpha1/smt/proof.go b/store/v2alpha1/smt/proof.go deleted file mode 100644 index f013d1b2b995..000000000000 --- a/store/v2alpha1/smt/proof.go +++ /dev/null @@ -1,93 +0,0 @@ -package smt - -import ( - "bytes" - "crypto/sha256" - "encoding/gob" - "hash" - - "github.com/cosmos/cosmos-sdk/store/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/lazyledger/smt" - "github.com/tendermint/tendermint/crypto/merkle" - tmmerkle "github.com/tendermint/tendermint/proto/tendermint/crypto" -) - -type HasherType byte - -const ( - SHA256 HasherType = iota -) - -const ( - ProofType = "smt" -) - -type ProofOp struct { - Root []byte - Key []byte - Hasher HasherType - Proof smt.SparseMerkleProof -} - -var _ merkle.ProofOperator = (*ProofOp)(nil) - -// NewProofOp returns a ProofOp for a SparseMerkleProof. -func NewProofOp(root, key []byte, hasher HasherType, proof smt.SparseMerkleProof) *ProofOp { - return &ProofOp{ - Root: root, - Key: key, - Hasher: hasher, - Proof: proof, - } -} - -func (p *ProofOp) Run(args [][]byte) ([][]byte, error) { - switch len(args) { - case 0: // non-membership proof - if !smt.VerifyProof(p.Proof, p.Root, p.Key, []byte{}, getHasher(p.Hasher)) { - return nil, sdkerrors.Wrapf(types.ErrInvalidProof, "proof did not verify absence of key: %s", p.Key) - } - case 1: // membership proof - if !smt.VerifyProof(p.Proof, p.Root, p.Key, args[0], getHasher(p.Hasher)) { - return nil, sdkerrors.Wrapf(types.ErrInvalidProof, "proof did not verify existence of key %s with given value %x", p.Key, args[0]) - } - default: - return nil, sdkerrors.Wrapf(types.ErrInvalidProof, "args must be length 0 or 1, got: %d", len(args)) - } - return [][]byte{p.Root}, nil -} - -func (p *ProofOp) GetKey() []byte { - return p.Key -} - -func (p *ProofOp) ProofOp() tmmerkle.ProofOp { - var data bytes.Buffer - enc := gob.NewEncoder(&data) - enc.Encode(p) - return tmmerkle.ProofOp{ - Type: "smt", - Key: p.Key, - Data: data.Bytes(), - } -} - -func ProofDecoder(pop tmmerkle.ProofOp) (merkle.ProofOperator, error) { - dec := gob.NewDecoder(bytes.NewBuffer(pop.Data)) - var proof ProofOp - err := dec.Decode(&proof) - if err != nil { - return nil, err - } - return &proof, nil -} - -func getHasher(hasher HasherType) hash.Hash { - switch hasher { - case SHA256: - return sha256.New() - default: - return nil - } -} diff --git a/store/v2alpha1/smt/proof_test.go b/store/v2alpha1/smt/proof_test.go deleted file mode 100644 index 3ef6573dab32..000000000000 --- a/store/v2alpha1/smt/proof_test.go +++ /dev/null @@ -1,69 +0,0 @@ -package smt_test - -import ( - "crypto/sha256" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/cosmos/cosmos-sdk/db/memdb" - smtstore "github.com/cosmos/cosmos-sdk/store/v2alpha1/smt" - "github.com/lazyledger/smt" -) - -func TestProofOpInterface(t *testing.T) { - hasher := sha256.New() - nodes, values := memdb.NewDB(), memdb.NewDB() - tree := smt.NewSparseMerkleTree(nodes.ReadWriter(), values.ReadWriter(), hasher) - key := []byte("foo") - value := []byte("bar") - root, err := tree.Update(key, value) - require.NoError(t, err) - require.NotEmpty(t, root) - - proof, err := tree.Prove(key) - require.True(t, smt.VerifyProof(proof, root, key, value, hasher)) - - storeProofOp := smtstore.NewProofOp(root, key, smtstore.SHA256, proof) - require.NotNil(t, storeProofOp) - // inclusion proof - r, err := storeProofOp.Run([][]byte{value}) - assert.NoError(t, err) - assert.NotEmpty(t, r) - assert.Equal(t, root, r[0]) - - // inclusion proof - wrong value - should fail - r, err = storeProofOp.Run([][]byte{key}) - assert.Error(t, err) - assert.Empty(t, r) - - // exclusion proof - should fail - r, err = storeProofOp.Run([][]byte{}) - assert.Error(t, err) - assert.Empty(t, r) - - // invalid request - should fail - r, err = storeProofOp.Run([][]byte{key, key}) - assert.Error(t, err) - assert.Empty(t, r) - - // encode - tmProofOp := storeProofOp.ProofOp() - assert.NotNil(t, tmProofOp) - assert.Equal(t, smtstore.ProofType, tmProofOp.Type) - assert.Equal(t, key, tmProofOp.Key, key) - assert.NotEmpty(t, tmProofOp.Data) - - //decode - decoded, err := smtstore.ProofDecoder(tmProofOp) - assert.NoError(t, err) - assert.NotNil(t, decoded) - assert.Equal(t, key, decoded.GetKey()) - - // run proof after decoding - r, err = decoded.Run([][]byte{value}) - assert.NoError(t, err) - assert.NotEmpty(t, r) - assert.Equal(t, root, r[0]) -} diff --git a/store/v2alpha1/smt/store.go b/store/v2alpha1/smt/store.go index eb126fa9a27a..2b83129f6c52 100644 --- a/store/v2alpha1/smt/store.go +++ b/store/v2alpha1/smt/store.go @@ -10,7 +10,6 @@ import ( ics23 "github.com/confio/ics23/go" "github.com/lazyledger/smt" - tmcrypto "github.com/tendermint/tendermint/proto/tendermint/crypto" ) var ( @@ -20,18 +19,25 @@ var ( var ( nodesPrefix = []byte{0} - valuesPrefix = []byte{1} - preimagesPrefix = []byte{2} + preimagesPrefix = []byte{1} + valuesPrefix = []byte{2} errKeyEmpty = errors.New("key is empty or nil") errValueNil = errors.New("value is nil") ) +// StoreParams defines how the SMT structural and value data are accessed internally. +type StoreParams struct { + TreeData dbm.DBReadWriter + ValueData dbm.DBReadWriter +} + // Store Implements types.BasicKVStore. type Store struct { - tree *smt.SparseMerkleTree - values dbm.DBReader - // Map hashed keys back to preimage + tree smt.SparseMerkleTree + // Maps value hash back to preimage + values dbm.DBReadWriter + // Maps hashed key (tree path) back to preimage preimages dbm.DBReadWriter } @@ -39,40 +45,34 @@ type Store struct { // smt.SparseMerkleTree expects this error to be returned when a key is not found type dbMapStore struct{ dbm.DBReadWriter } -func NewStore(db dbm.DBReadWriter) *Store { - nodes := prefix.NewPrefixReadWriter(db, nodesPrefix) - values := prefix.NewPrefixReadWriter(db, valuesPrefix) - preimages := prefix.NewPrefixReadWriter(db, preimagesPrefix) +func NewStore(par StoreParams) *Store { + nodes := prefix.NewPrefixReadWriter(par.TreeData, nodesPrefix) + preimages := prefix.NewPrefixReadWriter(par.TreeData, preimagesPrefix) + values := par.ValueData + if values == nil { + values = prefix.NewPrefixReadWriter(par.TreeData, valuesPrefix) + } return &Store{ - tree: smt.NewSparseMerkleTree(dbMapStore{nodes}, dbMapStore{values}, sha256.New()), + tree: smt.NewSMT(dbMapStore{nodes}, sha256.New()), values: values, preimages: preimages, } } -func LoadStore(db dbm.DBReadWriter, root []byte) *Store { - nodes := prefix.NewPrefixReadWriter(db, nodesPrefix) - values := prefix.NewPrefixReadWriter(db, valuesPrefix) - preimages := prefix.NewPrefixReadWriter(db, preimagesPrefix) +func LoadStore(par StoreParams, root []byte) *Store { + nodes := prefix.NewPrefixReadWriter(par.TreeData, nodesPrefix) + preimages := prefix.NewPrefixReadWriter(par.TreeData, preimagesPrefix) + values := par.ValueData + if values == nil { + values = prefix.NewPrefixReadWriter(par.TreeData, valuesPrefix) + } return &Store{ - tree: smt.ImportSparseMerkleTree(dbMapStore{nodes}, dbMapStore{values}, sha256.New(), root), + tree: smt.ImportSMT(dbMapStore{nodes}, sha256.New(), root), values: values, preimages: preimages, } } -func (s *Store) GetProof(key []byte) (*tmcrypto.ProofOps, error) { - if len(key) == 0 { - return nil, errKeyEmpty - } - proof, err := s.tree.Prove(key) - if err != nil { - return nil, err - } - op := NewProofOp(s.tree.Root(), key, SHA256, proof) - return &tmcrypto.ProofOps{Ops: []tmcrypto.ProofOp{op.ProofOp()}}, nil -} - func (s *Store) GetProofICS23(key []byte) (*ics23.CommitmentProof, error) { return createIcs23Proof(s, key) } @@ -86,7 +86,7 @@ func (s *Store) Get(key []byte) []byte { if len(key) == 0 { panic(errKeyEmpty) } - val, err := s.tree.Get(key) + val, err := s.values.Get(key) if err != nil { panic(err) } @@ -98,7 +98,7 @@ func (s *Store) Has(key []byte) bool { if len(key) == 0 { panic(errKeyEmpty) } - has, err := s.tree.Has(key) + has, err := s.values.Has(key) if err != nil { panic(err) } @@ -113,12 +113,17 @@ func (s *Store) Set(key []byte, value []byte) { if value == nil { panic(errValueNil) } - _, err := s.tree.Update(key, value) - if err != nil { + if err := s.tree.Update(key, value); err != nil { + panic(err) + } + if err := s.values.Set(key, value); err != nil { panic(err) } + // TODO: plug into the SMT's hashers path := sha256.Sum256(key) - s.preimages.Set(path[:], key) + if err := s.preimages.Set(path[:], key); err != nil { + panic(err) + } } // Delete deletes the key. Panics on nil key. @@ -126,11 +131,20 @@ func (s *Store) Delete(key []byte) { if len(key) == 0 { panic(errKeyEmpty) } - _, _ = s.tree.Delete(key) + if err := s.tree.Delete(key); err != nil && err != smt.ErrKeyNotPresent { + panic(err) + } + if err := s.values.Delete(key); err != nil { + panic(err) + } path := sha256.Sum256(key) s.preimages.Delete(path[:]) } +func (s *Store) Commit() error { + return s.tree.Save() +} + func (ms dbMapStore) Get(key []byte) ([]byte, error) { val, err := ms.DBReadWriter.Get(key) if err != nil { diff --git a/store/v2alpha1/smt/store_test.go b/store/v2alpha1/smt/store_test.go index 3024343fbf95..91511f5d9c95 100644 --- a/store/v2alpha1/smt/store_test.go +++ b/store/v2alpha1/smt/store_test.go @@ -11,7 +11,7 @@ import ( func TestGetSetHasDelete(t *testing.T) { db := memdb.NewDB() - s := store.NewStore(db.ReadWriter()) + s := store.NewStore(store.StoreParams{TreeData: db.ReadWriter()}) s.Set([]byte("foo"), []byte("bar")) assert.Equal(t, []byte("bar"), s.Get([]byte("foo"))) @@ -33,14 +33,15 @@ func TestGetSetHasDelete(t *testing.T) { func TestLoadStore(t *testing.T) { db := memdb.NewDB() txn := db.ReadWriter() - s := store.NewStore(txn) + s := store.NewStore(store.StoreParams{TreeData: txn}) s.Set([]byte{0}, []byte{0}) s.Set([]byte{1}, []byte{1}) s.Delete([]byte{1}) root := s.Root() + assert.NoError(t, s.Commit()) - s = store.LoadStore(txn, root) + s = store.LoadStore(store.StoreParams{TreeData: txn}, root) assert.Equal(t, []byte{0}, s.Get([]byte{0})) assert.False(t, s.Has([]byte{1})) s.Set([]byte{2}, []byte{2}) From db9146901eedbb5ce14dd84e302b9a55d97a8a09 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Thu, 2 Jun 2022 16:04:49 +0800 Subject: [PATCH 61/78] Patch merge --- baseapp/baseapp_test.go | 2 -- server/mock/app.go | 7 ------- simapp/simd/cmd/root.go | 2 +- store/streaming/constructor_test.go | 5 +++-- x/upgrade/types/storeloader_test.go | 4 ++-- 5 files changed, 6 insertions(+), 14 deletions(-) diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index f10ce7001d4e..acb60a92a0df 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -1797,8 +1797,6 @@ func TestLoadSnapshotChunk(t *testing.T) { } func TestOfferSnapshot_Errors(t *testing.T) { - t.Skip("disabled pending MultiStore snapshot implementation") - // Set up app before test cases, since it's fairly expensive. setupConfig := &setupConfig{ blocks: 0, diff --git a/server/mock/app.go b/server/mock/app.go index 6b8211442b58..8d777612c198 100644 --- a/server/mock/app.go +++ b/server/mock/app.go @@ -6,20 +6,13 @@ import ( "fmt" "path/filepath" - "github.com/tendermint/tendermint/types" - abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/types" bam "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/codec" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/db/badgerdb" - "github.com/cosmos/cosmos-sdk/simapp" - - bam "github.com/cosmos/cosmos-sdk/baseapp" - "github.com/cosmos/cosmos-sdk/codec" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" ) diff --git a/simapp/simd/cmd/root.go b/simapp/simd/cmd/root.go index 8c8ccf5ba658..b9e0bd77c78d 100644 --- a/simapp/simd/cmd/root.go +++ b/simapp/simd/cmd/root.go @@ -311,7 +311,7 @@ func (a appCreator) appExport( return servertypes.ExportedApp{}, errors.New("application home not set") } - simApp := simapp.NewSimApp(logger, db, traceStore, false, map[int64]bool{}, homePath, uint(1), a.encCfg, appOpts) + simApp = simapp.NewSimApp(logger, db, traceStore, false, map[int64]bool{}, homePath, uint(1), a.encCfg, appOpts) if height != -1 { return simApp.ExportAppStateAndValidatorsAt(forZeroHeight, jailAllowedAddrs, height) } diff --git a/store/streaming/constructor_test.go b/store/streaming/constructor_test.go index 79051d16500a..e4955a9ceb06 100644 --- a/store/streaming/constructor_test.go +++ b/store/streaming/constructor_test.go @@ -6,14 +6,15 @@ import ( "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/codec" codecTypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/db/memdb" serverTypes "github.com/cosmos/cosmos-sdk/server/types" "github.com/cosmos/cosmos-sdk/simapp" "github.com/cosmos/cosmos-sdk/store/streaming" "github.com/cosmos/cosmos-sdk/store/streaming/file" "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/tendermint/tendermint/libs/log" - dbm "github.com/tendermint/tm-db" "github.com/stretchr/testify/require" ) @@ -49,7 +50,7 @@ func TestStreamingServiceConstructor(t *testing.T) { } func TestLoadStreamingServices(t *testing.T) { - db := dbm.NewMemDB() + db := memdb.NewDB() encCdc := simapp.MakeTestEncodingConfig() keys := sdk.NewKVStoreKeys("mockKey1", "mockKey2") bApp := baseapp.NewBaseApp("appName", log.NewNopLogger(), db, nil) diff --git a/x/upgrade/types/storeloader_test.go b/x/upgrade/types/storeloader_test.go index f88f850331e3..a8c34e4c4282 100644 --- a/x/upgrade/types/storeloader_test.go +++ b/x/upgrade/types/storeloader_test.go @@ -127,7 +127,7 @@ func TestSetLoader(t *testing.T) { baseapp.SetPruning(pruningtypes.NewPruningOptions(pruningtypes.PruningNothing)), baseapp.SetSubstores(tc.origStoreKey), } - origapp := baseapp.NewBaseApp(t.Name(), defaultLogger(), db, opts...) + origapp := baseapp.NewBaseApp(t.Name(), defaultLogger(), db, nil, opts...) require.NoError(t, origapp.Init()) for i := int64(2); i <= upgradeHeight-1; i++ { @@ -139,7 +139,7 @@ func TestSetLoader(t *testing.T) { // load the new app with the original app db opts = []baseapp.AppOption{ - baseapp.SetPruning(storetypes.PruneNothing), + baseapp.SetPruning(pruningtypes.NewPruningOptions(pruningtypes.PruningNothing)), baseapp.SetSubstores(tc.loadStoreKey), } if tc.setLoader != nil { From 5c1b566a71a359fabf4b72cf8d11f729daaabb6c Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Thu, 2 Jun 2022 15:30:54 +0800 Subject: [PATCH 62/78] Patch simapp upgrade handler --- simapp/app.go | 17 ++++++++------- simapp/upgrades.go | 43 +++++++++++++++++++++----------------- x/upgrade/keeper/keeper.go | 15 ++++++------- 3 files changed, 41 insertions(+), 34 deletions(-) diff --git a/simapp/app.go b/simapp/app.go index fed8c317211a..2af2dae79b39 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -219,11 +219,6 @@ func NewSimApp( legacyAmino := encodingConfig.Amino interfaceRegistry := encodingConfig.InterfaceRegistry - bApp := baseapp.NewBaseApp(appName, logger, db, encodingConfig.TxConfig.TxDecoder(), baseAppOptions...) - bApp.SetCommitMultiStoreTracer(traceStore) - bApp.SetVersion(version.Version) - bApp.SetInterfaceRegistry(interfaceRegistry) - keys := sdk.NewKVStoreKeys( authtypes.StoreKey, banktypes.StoreKey, stakingtypes.StoreKey, minttypes.StoreKey, distrtypes.StoreKey, slashingtypes.StoreKey, @@ -268,7 +263,13 @@ func NewSimApp( } baseAppOptions = append(baseAppOptions, baseapp.StoreOption(setNamespaces)) - bApp := baseapp.NewBaseApp(appName, logger, db, baseAppOptions...) + // set the governance module account as the authority for conducting upgrades + upgradeKeeper := upgradekeeper.NewKeeper(skipUpgradeHeights, keys[upgradetypes.StoreKey], appCodec, homePath, nil, authtypes.NewModuleAddress(govtypes.ModuleName).String()) + if upgradeOpt := GetUpgradeStoreOption(upgradeKeeper); upgradeOpt != nil { + baseAppOptions = append(baseAppOptions, upgradeOpt) + } + + bApp := baseapp.NewBaseApp(appName, logger, db, encodingConfig.TxConfig.TxDecoder(), baseAppOptions...) bApp.SetCommitMultiStoreTracer(traceStore) bApp.SetVersion(version.Version) bApp.SetInterfaceRegistry(interfaceRegistry) @@ -363,9 +364,9 @@ func NewSimApp( // register the governance hooks ), ) - // set the governance module account as the authority for conducting upgrades - app.UpgradeKeeper = upgradekeeper.NewKeeper(skipUpgradeHeights, keys[upgradetypes.StoreKey], appCodec, homePath, app.BaseApp, authtypes.NewModuleAddress(govtypes.ModuleName).String()) + upgradeKeeper.SetVersionSetter(app.BaseApp) + app.UpgradeKeeper = upgradeKeeper // RegisterUpgradeHandlers is used for registering any on-chain upgrades app.RegisterUpgradeHandlers() diff --git a/simapp/upgrades.go b/simapp/upgrades.go index 2a62ae5c881c..dd44a8d5633e 100644 --- a/simapp/upgrades.go +++ b/simapp/upgrades.go @@ -1,11 +1,13 @@ package simapp import ( + "github.com/cosmos/cosmos-sdk/baseapp" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/x/group" "github.com/cosmos/cosmos-sdk/x/nft" + upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" ) @@ -15,16 +17,36 @@ import ( // when an application is migrating from Cosmos SDK version v0.45.x to v0.46.x. const UpgradeName = "v045-to-v046" +func GetUpgradeStoreOption(keeper upgradekeeper.Keeper) baseapp.StoreOption { + upgradeInfo, err := keeper.ReadUpgradeInfoFromDisk() + if err != nil { + panic(err) + } + + if upgradeInfo.Name == UpgradeName && !keeper.IsSkipHeight(upgradeInfo.Height) { + storeUpgrades := storetypes.StoreUpgrades{ + Added: []string{ + group.ModuleName, + nft.ModuleName, + }, + } + + // configure store loader that checks if version == upgradeHeight and applies store upgrades + return upgradetypes.UpgradeStoreOption(uint64(upgradeInfo.Height), &storeUpgrades) + } + return nil +} + func (app SimApp) RegisterUpgradeHandlers() { app.UpgradeKeeper.SetUpgradeHandler(UpgradeName, func(ctx sdk.Context, plan upgradetypes.Plan, _ module.VersionMap) (module.VersionMap, error) { // We set fromVersion to 1 to avoid running InitGenesis for modules for // in-store migrations. - // + // // If you wish to skip any module migrations, i.e. they were already migrated // in an older version, you can use `modulename.AppModule{}.ConsensusVersion()` // instead of `1` below. - // + // // For example: // "auth": auth.AppModule{}.ConsensusVersion() fromVM := map[string]uint64{ @@ -48,21 +70,4 @@ func (app SimApp) RegisterUpgradeHandlers() { return app.mm.RunMigrations(ctx, app.configurator, fromVM) }) - - upgradeInfo, err := app.UpgradeKeeper.ReadUpgradeInfoFromDisk() - if err != nil { - panic(err) - } - - if upgradeInfo.Name == UpgradeName && !app.UpgradeKeeper.IsSkipHeight(upgradeInfo.Height) { - storeUpgrades := storetypes.StoreUpgrades{ - Added: []string{ - group.ModuleName, - nft.ModuleName, - }, - } - - // configure store loader that checks if version == upgradeHeight and applies store upgrades - app.SetStoreLoader(upgradetypes.UpgradeStoreLoader(upgradeInfo.Height, &storeUpgrades)) - } } diff --git a/x/upgrade/keeper/keeper.go b/x/upgrade/keeper/keeper.go index 44196403fde8..17f0d6f87acb 100644 --- a/x/upgrade/keeper/keeper.go +++ b/x/upgrade/keeper/keeper.go @@ -363,7 +363,7 @@ func (k Keeper) IsSkipHeight(height int64) bool { // DumpUpgradeInfoToDisk writes upgrade information to UpgradeInfoFileName. func (k Keeper) DumpUpgradeInfoToDisk(height int64, p types.Plan) error { - upgradeInfoFilePath, err := GetUpgradeInfoPath(k.getHomeDir()) + upgradeInfoFilePath, err := k.GetUpgradeInfoPath() if err != nil { return err } @@ -382,8 +382,8 @@ func (k Keeper) DumpUpgradeInfoToDisk(height int64, p types.Plan) error { } // GetUpgradeInfoPath returns the upgrade info file path -func GetUpgradeInfoPath(homePath string) (string, error) { - upgradeInfoFileDir := path.Join(homePath, "data") +func (k Keeper) GetUpgradeInfoPath() (string, error) { + upgradeInfoFileDir := path.Join(k.getHomeDir(), "data") err := tmos.EnsureDir(upgradeInfoFileDir, os.ModePerm) if err != nil { return "", err @@ -401,10 +401,10 @@ func (k Keeper) getHomeDir() string { // written to disk by the old binary when panicking. An error is returned if // the upgrade path directory cannot be created or if the file exists and // cannot be read or if the upgrade info fails to unmarshal. -func ReadUpgradeInfoFromDisk(homePath string) (types.Plan, error) { +func (k Keeper) ReadUpgradeInfoFromDisk() (types.Plan, error) { var upgradeInfo types.Plan - upgradeInfoPath, err := GetUpgradeInfoPath(homePath) + upgradeInfoPath, err := k.GetUpgradeInfoPath() if err != nil { return upgradeInfo, err } @@ -426,8 +426,9 @@ func ReadUpgradeInfoFromDisk(homePath string) (types.Plan, error) { return upgradeInfo, nil } -func (k Keeper) ReadUpgradeInfoFromDisk() (types.Plan, error) { - return ReadUpgradeInfoFromDisk(k.getHomeDir()) +// SetVersionSetter upgrades versionSetter. +func (k *Keeper) SetVersionSetter(vs xp.ProtocolVersionSetter) { + k.versionSetter = vs } // SetDowngradeVerified updates downgradeVerified. From 5f8397656985187ff71b763854d5c6838b775d01 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Tue, 31 May 2022 21:30:24 +0800 Subject: [PATCH 63/78] Revert "[dev] update SMT library" This reverts commit 1a37cc229c2bd27f83061c415dda4f21b4aca795. --- go.mod | 3 - store/v2alpha1/multi/proof_test.go | 2 +- store/v2alpha1/multi/store.go | 47 ++++++++------- store/v2alpha1/multi/view_store.go | 10 ++-- store/v2alpha1/smt/ics23.go | 8 +-- store/v2alpha1/smt/ics23_test.go | 5 +- store/v2alpha1/smt/proof.go | 93 ++++++++++++++++++++++++++++++ store/v2alpha1/smt/store.go | 82 +++++++++++--------------- store/v2alpha1/smt/store_test.go | 7 +-- 9 files changed, 164 insertions(+), 93 deletions(-) create mode 100644 store/v2alpha1/smt/proof.go diff --git a/go.mod b/go.mod index 8ee20273f325..b1a3d22c9673 100644 --- a/go.mod +++ b/go.mod @@ -163,6 +163,3 @@ replace ( ) retract v0.43.0 - -// DEV -replace github.com/lazyledger/smt => ../smt diff --git a/store/v2alpha1/multi/proof_test.go b/store/v2alpha1/multi/proof_test.go index dc9e38fa15ec..13c69a5d616c 100644 --- a/store/v2alpha1/multi/proof_test.go +++ b/store/v2alpha1/multi/proof_test.go @@ -20,7 +20,7 @@ func keyPath(prefix, key string) string { func TestVerifySMTStoreProof(t *testing.T) { // Create main tree for testing. txn := memdb.NewDB().ReadWriter() - store := smt.NewStore(smt.StoreParams{TreeData: txn}) + store := smt.NewStore(txn) store.Set([]byte("MYKEY"), []byte("MYVALUE")) root := store.Root() diff --git a/store/v2alpha1/multi/store.go b/store/v2alpha1/multi/store.go index e86d264f5978..300a0d4e8ad7 100644 --- a/store/v2alpha1/multi/store.go +++ b/store/v2alpha1/multi/store.go @@ -504,27 +504,23 @@ func (rs *Store) getSubstore(key string) (*substore, error) { pfx := substorePrefix(key) stateRW := prefixdb.NewPrefixReadWriter(rs.stateTxn, pfx) stateCommitmentRW := prefixdb.NewPrefixReadWriter(rs.stateCommitmentTxn, pfx) - var stateCommitmentStore *smt.Store - data := prefixdb.NewPrefixReadWriter(stateRW, dataPrefix) + rootHash, err := stateRW.Get(substoreMerkleRootKey) if err != nil { return nil, err } if rootHash != nil { - stateCommitmentStore = loadSMT(stateCommitmentRW, data, rootHash) + stateCommitmentStore = loadSMT(stateCommitmentRW, rootHash) } else { - params := smt.StoreParams{ - TreeData: prefixdb.NewPrefixReadWriter(stateCommitmentRW, smtPrefix), - ValueData: data, - } - stateCommitmentStore = smt.NewStore(params) + smtdb := prefixdb.NewPrefixReadWriter(stateCommitmentRW, smtPrefix) + stateCommitmentStore = smt.NewStore(smtdb) } return &substore{ root: rs, name: key, - dataBucket: data, + dataBucket: prefixdb.NewPrefixReadWriter(stateRW, dataPrefix), stateCommitmentStore: stateCommitmentStore, }, nil } @@ -535,7 +531,7 @@ func (s *substore) refresh(rootHash []byte) { stateRW := prefixdb.NewPrefixReadWriter(s.root.stateTxn, pfx) stateCommitmentRW := prefixdb.NewPrefixReadWriter(s.root.stateCommitmentTxn, pfx) s.dataBucket = prefixdb.NewPrefixReadWriter(stateRW, dataPrefix) - s.stateCommitmentStore = loadSMT(stateCommitmentRW, s.dataBucket, rootHash) + s.stateCommitmentStore = loadSMT(stateCommitmentRW, rootHash) } // Commit implements Committer. @@ -587,20 +583,26 @@ func pruneVersions(current int64, opts pruningtypes.PruningOptions, prune func(i } } -// Calculates root hashes and commits to DB. Does not verify target version or perform pruning. -func (s *Store) commit(target uint64) (id *types.CommitID, err error) { - storeHashes := make(map[string][]byte, len(s.schema)) +func (s *Store) getMerkleRoots() (ret map[string][]byte, err error) { + ret = map[string][]byte{} for key := range s.schema { sub, has := s.substoreCache[key.Name()] if !has { - if sub, err = s.getSubstore(key.Name()); err != nil { + sub, err = s.getSubstore(key.Name()) + if err != nil { return } } - if err = sub.stateCommitmentStore.Commit(); err != nil { - return - } - storeHashes[key.Name()] = sub.stateCommitmentStore.Root() + ret[key.Name()] = sub.stateCommitmentStore.Root() + } + return +} + +// Calculates root hashes and commits to DB. Does not verify target version or perform pruning. +func (s *Store) commit(target uint64) (id *types.CommitID, err error) { + storeHashes, err := s.getMerkleRoots() + if err != nil { + return } // Update substore Merkle roots for key, storeHash := range storeHashes { @@ -844,12 +846,9 @@ func (rs *Store) Query(req abci.RequestQuery) (res abci.ResponseQuery) { return res } -func loadSMT(sc, data dbm.DBReadWriter, root []byte) *smt.Store { - params := smt.StoreParams{ - TreeData: prefixdb.NewPrefixReadWriter(sc, smtPrefix), - ValueData: data, - } - return smt.LoadStore(params, root) +func loadSMT(stateCommitmentTxn dbm.DBReadWriter, root []byte) *smt.Store { + smtdb := prefixdb.NewPrefixReadWriter(stateCommitmentTxn, smtPrefix) + return smt.LoadStore(smtdb, root) } // Returns closest index and whether it's a match diff --git a/store/v2alpha1/multi/view_store.go b/store/v2alpha1/multi/view_store.go index 55c7eb598216..e9dda6259c66 100644 --- a/store/v2alpha1/multi/view_store.go +++ b/store/v2alpha1/multi/view_store.go @@ -57,13 +57,11 @@ func (vs *viewStore) getSubstore(key string) (*viewSubstore, error) { if err != nil { return nil, err } - data := prefixdb.NewPrefixReader(stateR, dataPrefix) return &viewSubstore{ - root: vs, - name: key, - dataBucket: data, - stateCommitmentStore: loadSMT( - dbm.ReaderAsReadWriter(stateCommitmentR), dbm.ReaderAsReadWriter(data), rootHash), + root: vs, + name: key, + dataBucket: prefixdb.NewPrefixReader(stateR, dataPrefix), + stateCommitmentStore: loadSMT(dbm.ReaderAsReadWriter(stateCommitmentR), rootHash), }, nil } diff --git a/store/v2alpha1/smt/ics23.go b/store/v2alpha1/smt/ics23.go index 22d98faa8f43..31d78f993dc0 100644 --- a/store/v2alpha1/smt/ics23.go +++ b/store/v2alpha1/smt/ics23.go @@ -15,12 +15,12 @@ import ( func createIcs23Proof(store *Store, key []byte) (*ics23.CommitmentProof, error) { ret := &ics23.CommitmentProof{} path := sha256.Sum256(key) - valueHash, err := store.tree.Get(key) + has, err := store.tree.Has(key) if err != nil { return nil, err } - if valueHash != nil { // Membership proof - value, err := store.values.Get(key) + if has { // Membership proof + value, err := store.values.Get(path[:]) if err != nil { return nil, err } @@ -52,7 +52,7 @@ func toNonExistenceProof(store *Store, path [32]byte) (*ics23.NonExistenceProof, getNext := func(it dbm.Iterator) (*ics23.ExistenceProof, error) { defer it.Close() if it.Next() { - value, err := store.values.Get(it.Value()) + value, err := store.values.Get(it.Key()) if err != nil { return nil, err } diff --git a/store/v2alpha1/smt/ics23_test.go b/store/v2alpha1/smt/ics23_test.go index 14fa8889675b..5667dbe16e78 100644 --- a/store/v2alpha1/smt/ics23_test.go +++ b/store/v2alpha1/smt/ics23_test.go @@ -14,7 +14,7 @@ import ( func TestProofICS23(t *testing.T) { txn := memdb.NewDB().ReadWriter() - s := store.NewStore(store.StoreParams{TreeData: txn}) + s := store.NewStore(txn) // pick keys whose hashes begin with different bits key00 := []byte("foo") // 00101100 = sha256(foo)[0] key01 := []byte("bill") // 01100010 @@ -74,8 +74,7 @@ func TestProofICS23(t *testing.T) { // Make sure proofs work with a loaded store root := s.Root() - assert.NoError(t, s.Commit()) - s = store.LoadStore(store.StoreParams{TreeData: txn}, root) + s = store.LoadStore(txn, root) proof, err = s.GetProofICS23(key10) assert.NoError(t, err) nonexist = proof.GetNonexist() diff --git a/store/v2alpha1/smt/proof.go b/store/v2alpha1/smt/proof.go new file mode 100644 index 000000000000..f013d1b2b995 --- /dev/null +++ b/store/v2alpha1/smt/proof.go @@ -0,0 +1,93 @@ +package smt + +import ( + "bytes" + "crypto/sha256" + "encoding/gob" + "hash" + + "github.com/cosmos/cosmos-sdk/store/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/lazyledger/smt" + "github.com/tendermint/tendermint/crypto/merkle" + tmmerkle "github.com/tendermint/tendermint/proto/tendermint/crypto" +) + +type HasherType byte + +const ( + SHA256 HasherType = iota +) + +const ( + ProofType = "smt" +) + +type ProofOp struct { + Root []byte + Key []byte + Hasher HasherType + Proof smt.SparseMerkleProof +} + +var _ merkle.ProofOperator = (*ProofOp)(nil) + +// NewProofOp returns a ProofOp for a SparseMerkleProof. +func NewProofOp(root, key []byte, hasher HasherType, proof smt.SparseMerkleProof) *ProofOp { + return &ProofOp{ + Root: root, + Key: key, + Hasher: hasher, + Proof: proof, + } +} + +func (p *ProofOp) Run(args [][]byte) ([][]byte, error) { + switch len(args) { + case 0: // non-membership proof + if !smt.VerifyProof(p.Proof, p.Root, p.Key, []byte{}, getHasher(p.Hasher)) { + return nil, sdkerrors.Wrapf(types.ErrInvalidProof, "proof did not verify absence of key: %s", p.Key) + } + case 1: // membership proof + if !smt.VerifyProof(p.Proof, p.Root, p.Key, args[0], getHasher(p.Hasher)) { + return nil, sdkerrors.Wrapf(types.ErrInvalidProof, "proof did not verify existence of key %s with given value %x", p.Key, args[0]) + } + default: + return nil, sdkerrors.Wrapf(types.ErrInvalidProof, "args must be length 0 or 1, got: %d", len(args)) + } + return [][]byte{p.Root}, nil +} + +func (p *ProofOp) GetKey() []byte { + return p.Key +} + +func (p *ProofOp) ProofOp() tmmerkle.ProofOp { + var data bytes.Buffer + enc := gob.NewEncoder(&data) + enc.Encode(p) + return tmmerkle.ProofOp{ + Type: "smt", + Key: p.Key, + Data: data.Bytes(), + } +} + +func ProofDecoder(pop tmmerkle.ProofOp) (merkle.ProofOperator, error) { + dec := gob.NewDecoder(bytes.NewBuffer(pop.Data)) + var proof ProofOp + err := dec.Decode(&proof) + if err != nil { + return nil, err + } + return &proof, nil +} + +func getHasher(hasher HasherType) hash.Hash { + switch hasher { + case SHA256: + return sha256.New() + default: + return nil + } +} diff --git a/store/v2alpha1/smt/store.go b/store/v2alpha1/smt/store.go index 7b43be4d22b6..eb126fa9a27a 100644 --- a/store/v2alpha1/smt/store.go +++ b/store/v2alpha1/smt/store.go @@ -10,6 +10,7 @@ import ( ics23 "github.com/confio/ics23/go" "github.com/lazyledger/smt" + tmcrypto "github.com/tendermint/tendermint/proto/tendermint/crypto" ) var ( @@ -19,25 +20,18 @@ var ( var ( nodesPrefix = []byte{0} - preimagesPrefix = []byte{1} - valuesPrefix = []byte{2} + valuesPrefix = []byte{1} + preimagesPrefix = []byte{2} errKeyEmpty = errors.New("key is empty or nil") errValueNil = errors.New("value is nil") ) -// StoreParams defines how the SMT structural and value data are accessed internally. -type StoreParams struct { - TreeData dbm.DBReadWriter - ValueData dbm.DBReadWriter -} - // Store Implements types.BasicKVStore. type Store struct { - tree smt.SparseMerkleTree - // Maps value hash back to preimage - values dbm.DBReadWriter - // Maps hashed key (tree path) back to preimage + tree *smt.SparseMerkleTree + values dbm.DBReader + // Map hashed keys back to preimage preimages dbm.DBReadWriter } @@ -45,34 +39,40 @@ type Store struct { // smt.SparseMerkleTree expects this error to be returned when a key is not found type dbMapStore struct{ dbm.DBReadWriter } -func NewStore(par StoreParams) *Store { - nodes := prefix.NewPrefixReadWriter(par.TreeData, nodesPrefix) - preimages := prefix.NewPrefixReadWriter(par.TreeData, preimagesPrefix) - values := par.ValueData - if values == nil { - values = prefix.NewPrefixReadWriter(par.TreeData, valuesPrefix) - } +func NewStore(db dbm.DBReadWriter) *Store { + nodes := prefix.NewPrefixReadWriter(db, nodesPrefix) + values := prefix.NewPrefixReadWriter(db, valuesPrefix) + preimages := prefix.NewPrefixReadWriter(db, preimagesPrefix) return &Store{ - tree: smt.NewSMT(dbMapStore{nodes}, sha256.New()), + tree: smt.NewSparseMerkleTree(dbMapStore{nodes}, dbMapStore{values}, sha256.New()), values: values, preimages: preimages, } } -func LoadStore(par StoreParams, root []byte) *Store { - nodes := prefix.NewPrefixReadWriter(par.TreeData, nodesPrefix) - preimages := prefix.NewPrefixReadWriter(par.TreeData, preimagesPrefix) - values := par.ValueData - if values == nil { - values = prefix.NewPrefixReadWriter(par.TreeData, valuesPrefix) - } +func LoadStore(db dbm.DBReadWriter, root []byte) *Store { + nodes := prefix.NewPrefixReadWriter(db, nodesPrefix) + values := prefix.NewPrefixReadWriter(db, valuesPrefix) + preimages := prefix.NewPrefixReadWriter(db, preimagesPrefix) return &Store{ - tree: smt.ImportSMT(dbMapStore{nodes}, sha256.New(), root), + tree: smt.ImportSparseMerkleTree(dbMapStore{nodes}, dbMapStore{values}, sha256.New(), root), values: values, preimages: preimages, } } +func (s *Store) GetProof(key []byte) (*tmcrypto.ProofOps, error) { + if len(key) == 0 { + return nil, errKeyEmpty + } + proof, err := s.tree.Prove(key) + if err != nil { + return nil, err + } + op := NewProofOp(s.tree.Root(), key, SHA256, proof) + return &tmcrypto.ProofOps{Ops: []tmcrypto.ProofOp{op.ProofOp()}}, nil +} + func (s *Store) GetProofICS23(key []byte) (*ics23.CommitmentProof, error) { return createIcs23Proof(s, key) } @@ -86,7 +86,7 @@ func (s *Store) Get(key []byte) []byte { if len(key) == 0 { panic(errKeyEmpty) } - val, err := s.values.Get(key) + val, err := s.tree.Get(key) if err != nil { panic(err) } @@ -98,7 +98,7 @@ func (s *Store) Has(key []byte) bool { if len(key) == 0 { panic(errKeyEmpty) } - has, err := s.values.Has(key) + has, err := s.tree.Has(key) if err != nil { panic(err) } @@ -113,17 +113,12 @@ func (s *Store) Set(key []byte, value []byte) { if value == nil { panic(errValueNil) } - if err := s.tree.Update(key, value); err != nil { - panic(err) - } - if err := s.values.Set(key, value); err != nil { + _, err := s.tree.Update(key, value) + if err != nil { panic(err) } - // TODO: plug into the SMT's hashers path := sha256.Sum256(key) - if err := s.preimages.Set(path[:], key); err != nil { - panic(err) - } + s.preimages.Set(path[:], key) } // Delete deletes the key. Panics on nil key. @@ -131,20 +126,11 @@ func (s *Store) Delete(key []byte) { if len(key) == 0 { panic(errKeyEmpty) } - if err := s.tree.Delete(key); err != nil && err != smt.ErrKeyNotPresent { - panic(err) - } - if err := s.values.Delete(key); err != nil { - panic(err) - } + _, _ = s.tree.Delete(key) path := sha256.Sum256(key) s.preimages.Delete(path[:]) } -func (s *Store) Commit() error { - return s.tree.Commit() -} - func (ms dbMapStore) Get(key []byte) ([]byte, error) { val, err := ms.DBReadWriter.Get(key) if err != nil { diff --git a/store/v2alpha1/smt/store_test.go b/store/v2alpha1/smt/store_test.go index 91511f5d9c95..3024343fbf95 100644 --- a/store/v2alpha1/smt/store_test.go +++ b/store/v2alpha1/smt/store_test.go @@ -11,7 +11,7 @@ import ( func TestGetSetHasDelete(t *testing.T) { db := memdb.NewDB() - s := store.NewStore(store.StoreParams{TreeData: db.ReadWriter()}) + s := store.NewStore(db.ReadWriter()) s.Set([]byte("foo"), []byte("bar")) assert.Equal(t, []byte("bar"), s.Get([]byte("foo"))) @@ -33,15 +33,14 @@ func TestGetSetHasDelete(t *testing.T) { func TestLoadStore(t *testing.T) { db := memdb.NewDB() txn := db.ReadWriter() - s := store.NewStore(store.StoreParams{TreeData: txn}) + s := store.NewStore(txn) s.Set([]byte{0}, []byte{0}) s.Set([]byte{1}, []byte{1}) s.Delete([]byte{1}) root := s.Root() - assert.NoError(t, s.Commit()) - s = store.LoadStore(store.StoreParams{TreeData: txn}, root) + s = store.LoadStore(txn, root) assert.Equal(t, []byte{0}, s.Get([]byte{0})) assert.False(t, s.Has([]byte{1})) s.Set([]byte{2}, []byte{2}) From 0b9aadcdaa610e1584f3151fed90b5b6c2e0210d Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Fri, 24 Jun 2022 02:34:43 +0800 Subject: [PATCH 64/78] put pruning manager into v2/multi store --- internal/db/tmdb_adapter.go | 103 +++++++++++++++----- store/rootmulti/store.go | 10 +- store/rootmulti/store_test.go | 8 +- store/v2alpha1/multi/store.go | 145 +++++++++++++++++++++-------- store/v2alpha1/multi/store_test.go | 6 +- store/v2alpha1/multi/v1asv2.go | 36 +++++-- 6 files changed, 225 insertions(+), 83 deletions(-) diff --git a/internal/db/tmdb_adapter.go b/internal/db/tmdb_adapter.go index a6bbe4cb6f07..f5e1805f968e 100644 --- a/internal/db/tmdb_adapter.go +++ b/internal/db/tmdb_adapter.go @@ -1,3 +1,8 @@ +// Adapters used to wrap objects supporting cosmos-sdk/db interface so that they support +// the tm-db interface. +// +// This serves as a transitional step in introducing the new DB interface while maintaining +// compatibility with existing code that expects the old interface. package db import ( @@ -8,31 +13,81 @@ import ( tmdb "github.com/tendermint/tm-db" ) -// An adapter used to wrap objects supporting cosmos-sdk/db interface so that they support -// the tm-db interface. -// -// This serves as a transitional step in introducing the new DB interface while maintaining -// compatibility with existing code that expects the old interface. -type TmdbAdapter struct { +// TmdbTxnAdapter adapter wraps a single ReadWriter. +// Calling *Sync methods performs a commit and closes the transaction, invalidating it. +type TmdbTxnAdapter struct { + dbm.ReadWriter +} + +// TmdbConnAdapter wraps a DBConnection and a current transaction. +// When calling a *Sync method, a commit is performed and the transaction refreshed. +type TmdbConnAdapter struct { dbm.ReadWriter Connection dbm.Connection } type tmdbBatchAdapter struct { - *TmdbAdapter + *TmdbConnAdapter closed bool } var ( - _ tmdb.DB = (*TmdbAdapter)(nil) + _ tmdb.DB = (*TmdbTxnAdapter)(nil) + _ tmdb.DB = (*TmdbConnAdapter)(nil) ) -// ConnectionAsTmdb returns a tmdb.DB which wraps a Connection. -func ConnectionAsTmdb(db dbm.Connection) *TmdbAdapter { return &TmdbAdapter{db.ReadWriter(), db} } +// ReadWriterAsTmdb returns a tmdb.DB which wraps a ReadWriter. +// Calling *Sync methods performs a commit and closes the transaction. +func ReadWriterAsTmdb(rw dbm.ReadWriter) TmdbTxnAdapter { return TmdbTxnAdapter{rw} } + +// ConnectionAsTmdb returns a tmdb.DB which wraps a DBConnection. +func ConnectionAsTmdb(db dbm.Connection) *TmdbConnAdapter { + return &TmdbConnAdapter{db.ReadWriter(), db} +} + +func (d TmdbTxnAdapter) DeleteSync(k []byte) error { + err := d.ReadWriter.Delete(k) + if err != nil { + return err + } + return d.Commit() +} +func (d TmdbTxnAdapter) SetSync(k, v []byte) error { + err := d.ReadWriter.Set(k, v) + if err != nil { + return err + } + return d.Commit() +} + +func (d TmdbTxnAdapter) Iterator(s, e []byte) (tmdb.Iterator, error) { + it, err := d.ReadWriter.Iterator(s, e) + if err != nil { + return nil, err + } + return ToStoreIterator(it), nil +} +func (d TmdbTxnAdapter) ReverseIterator(s, e []byte) (tmdb.Iterator, error) { + it, err := d.ReadWriter.ReverseIterator(s, e) + if err != nil { + return nil, err + } + return ToStoreIterator(it), nil +} + +func (d TmdbTxnAdapter) Close() error { return d.ReadWriter.Discard() } +func (d TmdbTxnAdapter) NewBatch() tmdb.Batch { return d } +func (d TmdbTxnAdapter) Print() error { return nil } +func (d TmdbTxnAdapter) Stats() map[string]string { return nil } + +func (d TmdbTxnAdapter) Write() error { return d.Commit() } +func (d TmdbTxnAdapter) WriteSync() error { return d.Commit() } + +// TmdbConnAdapter: -func (d *TmdbAdapter) Close() error { d.CloseTx(); return d.Connection.Close() } -func (d *TmdbAdapter) CloseTx() error { return d.ReadWriter.Discard() } +func (d *TmdbConnAdapter) Close() error { d.CloseTx(); return d.Connection.Close() } +func (d *TmdbConnAdapter) CloseTx() error { return d.ReadWriter.Discard() } -func (d *TmdbAdapter) sync() error { +func (d *TmdbConnAdapter) sync() error { err := d.ReadWriter.Commit() if err != nil { return err @@ -40,14 +95,14 @@ func (d *TmdbAdapter) sync() error { d.ReadWriter = d.Connection.ReadWriter() return nil } -func (d *TmdbAdapter) DeleteSync(k []byte) error { +func (d *TmdbConnAdapter) DeleteSync(k []byte) error { err := d.ReadWriter.Delete(k) if err != nil { return err } return d.sync() } -func (d *TmdbAdapter) SetSync(k, v []byte) error { +func (d *TmdbConnAdapter) SetSync(k, v []byte) error { err := d.ReadWriter.Set(k, v) if err != nil { return err @@ -55,7 +110,7 @@ func (d *TmdbAdapter) SetSync(k, v []byte) error { return d.sync() } -func (d *TmdbAdapter) Commit() (uint64, error) { +func (d *TmdbConnAdapter) Commit() (uint64, error) { err := d.ReadWriter.Commit() if err != nil { return 0, err @@ -68,14 +123,14 @@ func (d *TmdbAdapter) Commit() (uint64, error) { return v, err } -func (d *TmdbAdapter) Iterator(s, e []byte) (tmdb.Iterator, error) { +func (d *TmdbConnAdapter) Iterator(s, e []byte) (tmdb.Iterator, error) { it, err := d.ReadWriter.Iterator(s, e) if err != nil { return nil, err } return ToStoreIterator(it), nil } -func (d *TmdbAdapter) ReverseIterator(s, e []byte) (tmdb.Iterator, error) { +func (d *TmdbConnAdapter) ReverseIterator(s, e []byte) (tmdb.Iterator, error) { it, err := d.ReadWriter.ReverseIterator(s, e) if err != nil { return nil, err @@ -83,12 +138,12 @@ func (d *TmdbAdapter) ReverseIterator(s, e []byte) (tmdb.Iterator, error) { return ToStoreIterator(it), nil } -// NewBatch returns a tmdb.Batch which wraps a Writer. -func (d *TmdbAdapter) NewBatch() tmdb.Batch { +// NewBatch returns a tmdb.Batch which wraps a DBWriter. +func (d *TmdbConnAdapter) NewBatch() tmdb.Batch { return &tmdbBatchAdapter{d, false} } -func (d *TmdbAdapter) Print() error { return nil } -func (d *TmdbAdapter) Stats() map[string]string { return nil } +func (d *TmdbConnAdapter) Print() error { return nil } +func (d *TmdbConnAdapter) Stats() map[string]string { return nil } var errClosed = errors.New("batch is closed") @@ -96,13 +151,13 @@ func (d *tmdbBatchAdapter) Set(k, v []byte) error { if d.closed { return errClosed } - return d.TmdbAdapter.Set(k, v) + return d.TmdbConnAdapter.Set(k, v) } func (d *tmdbBatchAdapter) Delete(k []byte) error { if d.closed { return errClosed } - return d.TmdbAdapter.Delete(k) + return d.TmdbConnAdapter.Delete(k) } func (d *tmdbBatchAdapter) WriteSync() error { if d.closed { diff --git a/store/rootmulti/store.go b/store/rootmulti/store.go index d31cb2e42b03..483c075962b9 100644 --- a/store/rootmulti/store.go +++ b/store/rootmulti/store.go @@ -79,7 +79,7 @@ func NewStore(db dbm.DB, logger log.Logger) *Store { keysByName: make(map[string]types.StoreKey), listeners: make(map[types.StoreKey][]types.WriteListener), removalMap: make(map[types.StoreKey]bool), - pruningManager: pruning.NewManager(db, logger), + pruningManager: pruning.NewManager(), } } @@ -320,7 +320,7 @@ func moveKVStoreData(oldDB types.KVStore, newDB types.KVStore) error { // If other strategy, this height is persisted until it is // less than - KeepRecent and % Interval == 0 func (rs *Store) PruneSnapshotHeight(height int64) { - rs.pruningManager.HandleHeightSnapshot(height) + rs.pruningManager.HandleHeightSnapshot(height, rs.db) } // SetInterBlockCache sets the Store's internal inter-block (persistent) cache. @@ -533,7 +533,7 @@ func (rs *Store) GetKVStore(key types.StoreKey) types.KVStore { } func (rs *Store) handlePruning(version int64) error { - rs.pruningManager.HandleHeight(version - 1) // we should never prune the current version. + rs.pruningManager.HandleHeight(version-1, rs.db) // we should never prune the current version. if !rs.pruningManager.ShouldPruneAtHeight(version) { return nil } @@ -543,7 +543,7 @@ func (rs *Store) handlePruning(version int64) error { } func (rs *Store) pruneStores() error { - pruningHeights, err := rs.pruningManager.GetFlushAndResetPruningHeights() + pruningHeights, err := rs.pruningManager.GetFlushAndResetPruningHeights(rs.db) if err != nil { return err } @@ -933,7 +933,7 @@ func (rs *Store) RollbackToVersion(target int64) int64 { return current } for ; current > target; current-- { - rs.pruningManager.HandleHeight(current) + rs.pruningManager.HandleHeight(current, rs.db) } if err := rs.pruneStores(); err != nil { panic(err) diff --git a/store/rootmulti/store_test.go b/store/rootmulti/store_test.go index ccd58b153feb..e24f5de43450 100644 --- a/store/rootmulti/store_test.go +++ b/store/rootmulti/store_test.go @@ -546,7 +546,7 @@ func TestMultiStore_Pruning_SameHeightsTwice(t *testing.T) { require.NoError(t, err) // Ensure already pruned heights were loaded - heights, err := ms.pruningManager.GetFlushAndResetPruningHeights() + heights, err := ms.pruningManager.GetFlushAndResetPruningHeights(ms.db) require.NoError(t, err) require.Equal(t, expectedHeights, heights) @@ -579,7 +579,7 @@ func TestMultiStore_PruningRestart(t *testing.T) { err := ms.pruningManager.LoadPruningHeights(ms.db) require.NoError(t, err) - actualHeightsToPrune, err := ms.pruningManager.GetFlushAndResetPruningHeights() + actualHeightsToPrune, err := ms.pruningManager.GetFlushAndResetPruningHeights(ms.db) require.NoError(t, err) require.Equal(t, len(pruneHeights), len(actualHeightsToPrune)) require.Equal(t, pruneHeights, actualHeightsToPrune) @@ -590,14 +590,14 @@ func TestMultiStore_PruningRestart(t *testing.T) { err = ms.LoadLatestVersion() require.NoError(t, err) - actualHeightsToPrune, err = ms.pruningManager.GetFlushAndResetPruningHeights() + actualHeightsToPrune, err = ms.pruningManager.GetFlushAndResetPruningHeights(ms.db) require.NoError(t, err) require.Equal(t, pruneHeights, actualHeightsToPrune) // commit one more block and ensure the heights have been pruned ms.Commit() - actualHeightsToPrune, err = ms.pruningManager.GetFlushAndResetPruningHeights() + actualHeightsToPrune, err = ms.pruningManager.GetFlushAndResetPruningHeights(ms.db) require.NoError(t, err) require.Empty(t, actualHeightsToPrune) diff --git a/store/v2alpha1/multi/store.go b/store/v2alpha1/multi/store.go index e2f6387a023e..e0902012a0cf 100644 --- a/store/v2alpha1/multi/store.go +++ b/store/v2alpha1/multi/store.go @@ -13,6 +13,8 @@ import ( dbm "github.com/cosmos/cosmos-sdk/db" prefixdb "github.com/cosmos/cosmos-sdk/db/prefix" util "github.com/cosmos/cosmos-sdk/internal" + dbutil "github.com/cosmos/cosmos-sdk/internal/db" + "github.com/cosmos/cosmos-sdk/pruning" pruningtypes "github.com/cosmos/cosmos-sdk/pruning/types" sdkmaps "github.com/cosmos/cosmos-sdk/store/internal/maps" "github.com/cosmos/cosmos-sdk/store/listenkv" @@ -101,10 +103,11 @@ type Store struct { mtx sync.RWMutex // Copied from StoreParams - Pruning pruningtypes.PruningOptions InitialVersion uint64 *traceListenMixin + pruningManager *pruning.Manager + PersistentCache types.MultiStorePersistentCache substoreCache map[string]*substore } @@ -250,7 +253,18 @@ func readSavedSchema(bucket dbm.Reader) (*SchemaBuilder, error) { // NewStore constructs a MultiStore directly from a database. // Creates a new store if no data exists; otherwise loads existing data. -func NewStore(db dbm.Connection, opts StoreParams) (ret *Store, err error) { +func NewStore(db dbm.DBConnection, opts StoreParams) (ret *Store, err error) { + pruningManager := pruning.NewManager() + pruningManager.SetOptions(opts.Pruning) + { // load any pruned heights we missed from disk to be pruned on the next run + r := db.Reader() + defer r.Discard() + tmdb := dbutil.ReadWriterAsTmdb(dbm.ReaderAsReadWriter(r)) + if err = pruningManager.LoadPruningHeights(tmdb); err != nil { + return + } + } + versions, err := db.Versions() if err != nil { return @@ -301,14 +315,11 @@ func NewStore(db dbm.Connection, opts StoreParams) (ret *Store, err error) { stateCommitmentTxn: stateCommitmentTxn, mem: mem.NewStore(), tran: transient.NewStore(), - - substoreCache: map[string]*substore{}, - - traceListenMixin: opts.traceListenMixin, - PersistentCache: opts.PersistentCache, - - Pruning: opts.Pruning, - InitialVersion: opts.InitialVersion, + substoreCache: map[string]*substore{}, + traceListenMixin: opts.traceListenMixin, + PersistentCache: opts.PersistentCache, + pruningManager: pruningManager, + InitialVersion: opts.InitialVersion, } // Now load the substore schema @@ -386,6 +397,7 @@ func NewStore(db dbm.Connection, opts StoreParams) (ret *Store, err error) { } ret.schema[skey] = typ } + return } @@ -557,30 +569,54 @@ func (s *Store) Commit() types.CommitID { panic(err) } - pruneVersions(cid.Version, s.Pruning, func(ver int64) { - s.stateDB.DeleteVersion(uint64(ver)) - - if s.StateCommitmentDB != nil { - s.StateCommitmentDB.DeleteVersion(uint64(ver)) - } - }) + if err = s.handlePruning(cid.Version); err != nil { + panic(err) + } s.tran.Commit() return *cid } -// Performs necessary pruning via callback -func pruneVersions(current int64, opts pruningtypes.PruningOptions, prune func(int64)) { - previous := current - 1 - if opts.Interval != 0 && current%int64(opts.Interval) == 0 { - // The range of newly prunable versions - lastPrunable := previous - int64(opts.KeepRecent) - firstPrunable := lastPrunable - int64(opts.Interval) +func (rs *Store) handlePruning(current int64) error { + // Pass DB txn to pruning manager via adapter; running txns must be refreshed after this. + // This is hacky but needed in order to restrict to a single txn (for memdb compatibility) + // since the manager calls SetSync internally. + rs.stateTxn.Discard() + defer rs.refreshTransactions(true) + db := rs.stateDB.ReadWriter() + rs.pruningManager.HandleHeight(current-1, dbutil.ReadWriterAsTmdb(db)) // we should never prune the current version. + db.Discard() + if !rs.pruningManager.ShouldPruneAtHeight(current) { + return nil + } + db = rs.stateDB.ReadWriter() + defer db.Discard() + pruningHeights, err := rs.pruningManager.GetFlushAndResetPruningHeights(dbutil.ReadWriterAsTmdb(db)) + if err != nil { + return err + } + return pruneVersions(pruningHeights, func(ver int64) error { + if err := rs.stateDB.DeleteVersion(uint64(ver)); err != nil { + return fmt.Errorf("error pruning StateDB: %w", err) + } + + if rs.StateCommitmentDB != nil { + if err := rs.StateCommitmentDB.DeleteVersion(uint64(ver)); err != nil { + return fmt.Errorf("error pruning StateCommitmentDB: %w", err) + } + } + return nil + }) +} - for version := firstPrunable; version <= lastPrunable; version++ { - prune(version) +// Performs necessary pruning via callback +func pruneVersions(heights []int64, prune func(int64) error) error { + for _, height := range heights { + if err := prune(height); err != nil { + return err } } + return nil } func (s *Store) getMerkleRoots() (ret map[string][]byte, err error) { @@ -629,14 +665,11 @@ func (s *Store) commit(target uint64) (id *types.CommitID, err error) { return } - stateTxn := s.stateDB.ReadWriter() defer func() { if err != nil { - err = util.CombineErrors(err, stateTxn.Discard(), "stateTxn.Discard also failed") + err = util.CombineErrors(err, s.stateTxn.Discard(), "stateTxn.Discard also failed") } }() - stateCommitmentTxn := stateTxn - // If DBs are not separate, StateCommitment state has been committed & snapshotted if s.StateCommitmentDB != nil { // if any error is encountered henceforth, we must revert the state and SC dbs @@ -662,17 +695,38 @@ func (s *Store) commit(target uint64) (id *types.CommitID, err error) { if err != nil { return } - stateCommitmentTxn = s.StateCommitmentDB.ReadWriter() } - s.stateTxn = stateTxn - s.stateCommitmentTxn = stateCommitmentTxn + // flush is complete, refresh our DB read/writers + if err = s.refreshTransactions(false); err != nil { + return + } + + return &types.CommitID{Version: int64(target), Hash: rootHash}, nil +} + +// Resets the txn objects in the store (does not discard current txns), then propagates +// them to cached substores. +// justState indicates we only need to refresh the stateDB txn. +func (s *Store) refreshTransactions(onlyState bool) error { + s.stateTxn = s.stateDB.ReadWriter() + if s.StateCommitmentDB != nil { + if !onlyState { + s.stateCommitmentTxn = s.StateCommitmentDB.ReadWriter() + } + } else { + s.stateCommitmentTxn = s.stateTxn + } + + storeHashes, err := s.getMerkleRoots() + if err != nil { + return err + } // the state of all live substores must be refreshed for key, sub := range s.substoreCache { sub.refresh(storeHashes[key]) } - - return &types.CommitID{Version: int64(target), Hash: rootHash}, nil + return nil } // LastCommitID implements Committer. @@ -727,14 +781,18 @@ func (s *Store) GetAllVersions() []uint64 { // If PruneNothing, this is a no-op. // If other strategy, this height is persisted until it is // less than - KeepRecent and % Interval == 0 -func (s *Store) PruneSnapshotHeight(height int64) { - panic("not implemented") +func (rs *Store) PruneSnapshotHeight(height int64) { + rs.stateTxn.Discard() + defer rs.refreshTransactions(true) + db := rs.stateDB.ReadWriter() + defer db.Discard() + rs.pruningManager.HandleHeightSnapshot(height, dbutil.ReadWriterAsTmdb(db)) } // SetSnapshotInterval sets the interval at which the snapshots are taken. // It is used by the store to determine which heights to retain until after the snapshot is complete. -func (s *Store) SetSnapshotInterval(snapshotInterval uint64) { - panic("not implemented") +func (rs *Store) SetSnapshotInterval(snapshotInterval uint64) { + rs.pruningManager.SetSnapshotInterval(snapshotInterval) } // parsePath expects a format like /[/] @@ -996,5 +1054,10 @@ func (tlm *traceListenMixin) wrapTraceListen(store types.KVStore, skey types.Sto return store } -func (s *Store) GetPruning() pruningtypes.PruningOptions { return s.Pruning } -func (s *Store) SetPruning(po pruningtypes.PruningOptions) { s.Pruning = po } +func (s *Store) GetPruning() pruningtypes.PruningOptions { + return s.pruningManager.GetOptions() +} + +func (s *Store) SetPruning(po pruningtypes.PruningOptions) { + s.pruningManager.SetOptions(po) +} diff --git a/store/v2alpha1/multi/store_test.go b/store/v2alpha1/multi/store_test.go index 1c077dcfdf3f..666befd970ee 100644 --- a/store/v2alpha1/multi/store_test.go +++ b/store/v2alpha1/multi/store_test.go @@ -454,10 +454,10 @@ func doTestPruning(t *testing.T, ctor storeConstructor, sepDBs bool) { pruningtypes.PruningOptions kept []uint64 }{ + {pruningtypes.NewPruningOptions(pruningtypes.PruningNothing), []uint64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}, + {pruningtypes.NewPruningOptions(pruningtypes.PruningEverything), []uint64{8, 9, 10}}, {pruningtypes.NewCustomPruningOptions(2, 10), []uint64{8, 9, 10}}, {pruningtypes.NewCustomPruningOptions(0, 10), []uint64{10}}, - {pruningtypes.NewPruningOptions(pruningtypes.PruningEverything), []uint64{8, 9, 10}}, - {pruningtypes.NewPruningOptions(pruningtypes.PruningNothing), []uint64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}, } for tci, tc := range testCases { @@ -486,7 +486,7 @@ func doTestPruning(t *testing.T, ctor storeConstructor, sepDBs bool) { kept := sliceToSet(tc.kept) for v := uint64(1); v <= 10; v++ { _, has := kept[v] - require.Equal(t, has, versions.Exists(v), "Version = %v; tc #%d", v, tci) + require.Equal(t, has, versions.Exists(v), "version = %v; tc #%d", v, tci) } } } diff --git a/store/v2alpha1/multi/v1asv2.go b/store/v2alpha1/multi/v1asv2.go index 0073ec02a1dc..2ce3ee834c2d 100644 --- a/store/v2alpha1/multi/v1asv2.go +++ b/store/v2alpha1/multi/v1asv2.go @@ -6,7 +6,9 @@ import ( "github.com/tendermint/tendermint/libs/log" "github.com/cosmos/cosmos-sdk/db" + "github.com/cosmos/cosmos-sdk/db/memdb" dbutil "github.com/cosmos/cosmos-sdk/internal/db" + "github.com/cosmos/cosmos-sdk/pruning" "github.com/cosmos/cosmos-sdk/store/rootmulti" v1 "github.com/cosmos/cosmos-sdk/store/types" v2 "github.com/cosmos/cosmos-sdk/store/v2alpha1" @@ -20,7 +22,10 @@ var ( type store1as2 struct { *rootmulti.Store - database *dbutil.TmdbAdapter + database *dbutil.TmdbConnAdapter + // Mirror the pruning state in rootmulti.Store + pruner *pruning.Manager + pruneDB *memdb.MemDB } type cacheStore1as2 struct { @@ -49,6 +54,10 @@ func NewV1MultiStoreAsV2(database db.Connection, opts StoreParams) (v2.CommitMul cms.MountStoreWithDB(skey, typ, nil) } cms.SetPruning(opts.Pruning) + pruner := pruning.NewManager() + pruner.SetOptions(opts.Pruning) + pruner.LoadPruningHeights(adapter) + err := cms.SetInitialVersion(int64(opts.InitialVersion)) if err != nil { return nil, err @@ -62,7 +71,12 @@ func NewV1MultiStoreAsV2(database db.Connection, opts StoreParams) (v2.CommitMul } cms.SetTracer(opts.TraceWriter) cms.SetTracingContext(opts.TraceContext) - return &store1as2{cms, adapter}, nil + return &store1as2{ + Store: cms, + database: adapter, + pruner: pruner, + pruneDB: memdb.NewDB(), + }, nil } // MultiStore @@ -90,15 +104,25 @@ func (s *store1as2) Close() error { } func (s *store1as2) Commit() v2.CommitID { - ret := s.Store.Commit() + cid := s.Store.Commit() _, err := s.database.Commit() if err != nil { panic(err) } - pruneVersions(ret.Version, s.GetPruning(), func(ver int64) { - s.database.Connection.DeleteVersion(uint64(ver)) + + db := dbutil.ConnectionAsTmdb(s.pruneDB) + s.pruner.HandleHeight(cid.Version-1, db) + if !s.pruner.ShouldPruneAtHeight(cid.Version) { + return cid + } + pruningHeights, err := s.pruner.GetFlushAndResetPruningHeights(db) + if err != nil { + panic(err) + } + pruneVersions(pruningHeights, func(ver int64) error { + return s.database.Connection.DeleteVersion(uint64(ver)) }) - return ret + return cid } func (s *store1as2) SetInitialVersion(ver uint64) error { From 538bacb8c2894e738746b062071b7ec474c9c763 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Sat, 16 Jul 2022 16:14:55 -0500 Subject: [PATCH 65/78] fixup renames --- internal/db/iterator_adapter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/db/iterator_adapter.go b/internal/db/iterator_adapter.go index 8bf717041d57..e98b5800336a 100644 --- a/internal/db/iterator_adapter.go +++ b/internal/db/iterator_adapter.go @@ -12,7 +12,7 @@ type AsStoreIter struct { valid bool } -// DBToStoreIterator returns an iterator wrapping the given iterator so that it satisfies the +// ToStoreIterator returns an iterator wrapping the given iterator so that it satisfies the // (store/types).Iterator interface. func ToStoreIterator(source dbm.Iterator) *AsStoreIter { ret := &AsStoreIter{Iterator: source} From d7ff9099ae14c2c1eea6a4b668ac2e409f464f4d Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Mon, 4 Jul 2022 23:25:57 +0800 Subject: [PATCH 66/78] fix db creators - refactor packages/build directives --- db/badgerdb/creator.go | 17 +++++ db/badgerdb/db.go | 65 +++++++--------- db/badgerdb/db_test.go | 8 +- db/dbtest/benchmark.go | 12 +-- db/dbtest/testcases.go | 59 ++++++++------- db/dbtest/util.go | 16 ++-- db/dbtest/version_set.go | 12 +-- db/internal/backends/imports.go | 8 ++ db/internal/util.go | 6 +- db/memdb/creator.go | 13 ++++ db/memdb/db.go | 93 +++++++++++------------ db/memdb/db_test.go | 4 +- db/memdb/iterator.go | 4 +- db/prefix/prefix.go | 44 +++++------ db/reexport.go | 30 ++++++++ db/rocksdb/batch.go | 14 ++-- db/rocksdb/creator.go | 17 +++++ db/rocksdb/db.go | 100 +++++++++++-------------- db/rocksdb/db_test.go | 6 +- db/rocksdb/iterator.go | 6 +- db/{ => types}/adapter.go | 2 +- db/{ => types}/creator.go | 4 +- db/{ => types}/types.go | 18 ++--- db/{ => types}/version_manager.go | 2 +- db/{ => types}/version_manager_test.go | 6 +- go.mod | 1 + go.sum | 2 + 27 files changed, 311 insertions(+), 258 deletions(-) create mode 100644 db/badgerdb/creator.go create mode 100644 db/internal/backends/imports.go create mode 100644 db/memdb/creator.go create mode 100644 db/reexport.go create mode 100644 db/rocksdb/creator.go rename db/{ => types}/adapter.go (97%) rename db/{ => types}/creator.go (95%) rename db/{ => types}/types.go (92%) rename db/{ => types}/version_manager.go (99%) rename db/{ => types}/version_manager_test.go (50%) diff --git a/db/badgerdb/creator.go b/db/badgerdb/creator.go new file mode 100644 index 000000000000..5da5704fc560 --- /dev/null +++ b/db/badgerdb/creator.go @@ -0,0 +1,17 @@ +//go:build badgerdb + +package badgerdb + +import ( + "path/filepath" + + "github.com/cosmos/cosmos-sdk/db/types" +) + +func init() { + creator := func(name string, dir string) (types.Connection, error) { + dir = filepath.Join(dir, name) + return NewDB(dir) + } + types.RegisterCreator(types.BadgerDBBackend, creator, false) +} diff --git a/db/badgerdb/db.go b/db/badgerdb/db.go index 512bb962ff67..41a55329f5f1 100644 --- a/db/badgerdb/db.go +++ b/db/badgerdb/db.go @@ -1,5 +1,3 @@ -//go:build badgerdb - package badgerdb import ( @@ -13,8 +11,8 @@ import ( "sync" "sync/atomic" - "github.com/cosmos/cosmos-sdk/db" dbutil "github.com/cosmos/cosmos-sdk/db/internal" + "github.com/cosmos/cosmos-sdk/db/types" "github.com/dgraph-io/badger/v3" bpb "github.com/dgraph-io/badger/v3/pb" @@ -24,11 +22,11 @@ import ( var versionsFilename = "versions.csv" var ( - _ db.Connection = (*BadgerDB)(nil) - _ db.Reader = (*badgerTxn)(nil) - _ db.Writer = (*badgerWriter)(nil) - _ db.ReadWriter = (*badgerWriter)(nil) - _ db.VersionSet = (*versionManager)(nil) + _ types.Connection = (*BadgerDB)(nil) + _ types.Reader = (*badgerTxn)(nil) + _ types.Writer = (*badgerWriter)(nil) + _ types.ReadWriter = (*badgerWriter)(nil) + _ types.VersionSet = (*versionManager)(nil) ) // BadgerDB is a connection to a BadgerDB key-value database. @@ -65,19 +63,11 @@ type badgerIterator struct { // commit to see current state. So we must use commit increments that are more // granular than our version interval, and map versions to the corresponding timestamp. type versionManager struct { - *db.VersionManager + *types.VersionManager vmap map[uint64]uint64 lastTs uint64 } -func init() { - creator := func(name string, dir string) (db.Connection, error) { - dir = filepath.Join(dir, name) - return NewDB(dir) - } - db.RegisterCreator(db.BadgerDBBackend, creator, false) -} - // NewDB creates or loads a BadgerDB key-value database inside the given directory. // If dir does not exist, it will be created. func NewDB(dir string) (*BadgerDB, error) { @@ -92,8 +82,7 @@ func NewDB(dir string) (*BadgerDB, error) { return NewDBWithOptions(opts) } -// NewDBWithOptions creates a BadgerDB key-value database with the specified Options -// (https://pkg.go.dev/github.com/dgraph-io/badger/v3#Options) +// NewDBWithOptions creates a BadgerDB key-value database with the specified Options. func NewDBWithOptions(opts badger.Options) (*BadgerDB, error) { d, err := badger.OpenManaged(opts) if err != nil { @@ -144,7 +133,7 @@ func readVersionsFile(path string) (*versionManager, error) { versions = append(versions, version) vmap[version] = ts } - vmgr := db.NewVersionManager(versions) + vmgr := types.NewVersionManager(versions) return &versionManager{ VersionManager: vmgr, vmap: vmap, @@ -177,24 +166,24 @@ func writeVersionsFile(vm *versionManager, path string) error { return w.WriteAll(rows) } -func (b *BadgerDB) Reader() db.Reader { +func (b *BadgerDB) Reader() types.Reader { b.mtx.RLock() ts := b.vmgr.lastTs b.mtx.RUnlock() return &badgerTxn{txn: b.db.NewTransactionAt(ts, false), db: b} } -func (b *BadgerDB) ReaderAt(version uint64) (db.Reader, error) { +func (b *BadgerDB) ReaderAt(version uint64) (types.Reader, error) { b.mtx.RLock() defer b.mtx.RUnlock() ts, has := b.vmgr.versionTs(version) if !has { - return nil, db.ErrVersionDoesNotExist + return nil, types.ErrVersionDoesNotExist } return &badgerTxn{txn: b.db.NewTransactionAt(ts, false), db: b}, nil } -func (b *BadgerDB) ReadWriter() db.ReadWriter { +func (b *BadgerDB) ReadWriter() types.ReadWriter { atomic.AddInt32(&b.openWriters, 1) b.mtx.RLock() ts := b.vmgr.lastTs @@ -202,7 +191,7 @@ func (b *BadgerDB) ReadWriter() db.ReadWriter { return &badgerWriter{badgerTxn{txn: b.db.NewTransactionAt(ts, true), db: b}, false} } -func (b *BadgerDB) Writer() db.Writer { +func (b *BadgerDB) Writer() types.Writer { // Badger has a WriteBatch, but it doesn't support conflict detection return b.ReadWriter() } @@ -219,7 +208,7 @@ func (b *BadgerDB) Close() error { // Versions implements Connection. // Returns a VersionSet that is valid until the next call to SaveVersion or DeleteVersion. -func (b *BadgerDB) Versions() (db.VersionSet, error) { +func (b *BadgerDB) Versions() (types.VersionSet, error) { b.mtx.RLock() defer b.mtx.RUnlock() return b.vmgr, nil @@ -229,7 +218,7 @@ func (b *BadgerDB) save(target uint64) (uint64, error) { b.mtx.Lock() defer b.mtx.Unlock() if b.openWriters > 0 { - return 0, db.ErrOpenTransactions + return 0, types.ErrOpenTransactions } b.vmgr = b.vmgr.Copy() v, err := b.vmgr.Save(target) @@ -247,7 +236,7 @@ func (b *BadgerDB) SaveNextVersion() (uint64, error) { // SaveVersion implements Connection. func (b *BadgerDB) SaveVersion(target uint64) error { if target == 0 { - return db.ErrInvalidVersion + return types.ErrInvalidVersion } _, err := b.save(target) return err @@ -257,7 +246,7 @@ func (b *BadgerDB) DeleteVersion(target uint64) error { b.mtx.Lock() defer b.mtx.Unlock() if !b.vmgr.Exists(target) { - return db.ErrVersionDoesNotExist + return types.ErrVersionDoesNotExist } b.vmgr = b.vmgr.Copy() b.vmgr.Delete(target) @@ -268,7 +257,7 @@ func (b *BadgerDB) Revert() error { b.mtx.RLock() defer b.mtx.RUnlock() if b.openWriters > 0 { - return db.ErrOpenTransactions + return types.ErrOpenTransactions } // Revert from latest commit timestamp to last "saved" timestamp @@ -291,12 +280,12 @@ func (b *BadgerDB) RevertTo(ver uint64) error { b.mtx.RLock() defer b.mtx.RUnlock() if b.openWriters > 0 { - return db.ErrOpenTransactions + return types.ErrOpenTransactions } // Revert from latest commit timestamp to target timestamp if !b.vmgr.Exists(ver) { - return db.ErrVersionDoesNotExist + return types.ErrVersionDoesNotExist } targetTs, has := b.vmgr.versionTs(ver) if !has { @@ -373,7 +362,7 @@ func (b *BadgerDB) Stats() map[string]string { return nil } func (tx *badgerTxn) Get(key []byte) ([]byte, error) { if len(key) == 0 { - return nil, db.ErrKeyEmpty + return nil, types.ErrKeyEmpty } item, err := tx.txn.Get(key) @@ -391,7 +380,7 @@ func (tx *badgerTxn) Get(key []byte) ([]byte, error) { func (tx *badgerTxn) Has(key []byte) (bool, error) { if len(key) == 0 { - return false, db.ErrKeyEmpty + return false, types.ErrKeyEmpty } _, err := tx.txn.Get(key) @@ -420,7 +409,7 @@ func (tx *badgerWriter) Set(key, value []byte) error { func (tx *badgerWriter) Delete(key []byte) error { if len(key) == 0 { - return db.ErrKeyEmpty + return types.ErrKeyEmpty } err := tx.txn.Delete(key) if errors.Is(err, badger.ErrTxnTooBig) { @@ -464,7 +453,7 @@ func (tx *badgerWriter) Discard() error { func (tx *badgerTxn) iteratorOpts(start, end []byte, opts badger.IteratorOptions) (*badgerIterator, error) { if (start != nil && len(start) == 0) || (end != nil && len(end) == 0) { - return nil, db.ErrKeyEmpty + return nil, types.ErrKeyEmpty } iter := tx.txn.NewIterator(opts) iter.Rewind() @@ -482,12 +471,12 @@ func (tx *badgerTxn) iteratorOpts(start, end []byte, opts badger.IteratorOptions }, nil } -func (tx *badgerTxn) Iterator(start, end []byte) (db.Iterator, error) { +func (tx *badgerTxn) Iterator(start, end []byte) (types.Iterator, error) { opts := badger.DefaultIteratorOptions return tx.iteratorOpts(start, end, opts) } -func (tx *badgerTxn) ReverseIterator(start, end []byte) (db.Iterator, error) { +func (tx *badgerTxn) ReverseIterator(start, end []byte) (types.Iterator, error) { opts := badger.DefaultIteratorOptions opts.Reverse = true return tx.iteratorOpts(end, start, opts) diff --git a/db/badgerdb/db_test.go b/db/badgerdb/db_test.go index 006e3269efa1..d80dae5fd2be 100644 --- a/db/badgerdb/db_test.go +++ b/db/badgerdb/db_test.go @@ -5,11 +5,11 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/cosmos-sdk/db" "github.com/cosmos/cosmos-sdk/db/dbtest" + "github.com/cosmos/cosmos-sdk/db/types" ) -func load(t *testing.T, dir string) db.Connection { +func load(t *testing.T, dir string) types.Connection { d, err := NewDB(dir) require.NoError(t, err) return d @@ -41,14 +41,14 @@ func TestReloadDB(t *testing.T) { } func TestVersionManager(t *testing.T) { - new := func(vs []uint64) db.VersionSet { + new := func(vs []uint64) types.VersionSet { vmap := map[uint64]uint64{} var lastTs uint64 for _, v := range vs { vmap[v] = v lastTs = v } - return &versionManager{db.NewVersionManager(vs), vmap, lastTs} + return &versionManager{types.NewVersionManager(vs), vmap, lastTs} } dbtest.DoTestVersionSet(t, new) } diff --git a/db/dbtest/benchmark.go b/db/dbtest/benchmark.go index cb4a77ba5a81..fdfed250710a 100644 --- a/db/dbtest/benchmark.go +++ b/db/dbtest/benchmark.go @@ -8,7 +8,7 @@ import ( "github.com/stretchr/testify/require" - dbm "github.com/cosmos/cosmos-sdk/db" + "github.com/cosmos/cosmos-sdk/db/types" ) func Int64ToBytes(i int64) []byte { @@ -21,7 +21,7 @@ func BytesToInt64(buf []byte) int64 { return int64(binary.BigEndian.Uint64(buf)) } -func BenchmarkRangeScans(b *testing.B, db dbm.ReadWriter, dbSize int64) { +func BenchmarkRangeScans(b *testing.B, db types.ReadWriter, dbSize int64) { b.StopTimer() rangeSize := int64(10000) @@ -40,7 +40,7 @@ func BenchmarkRangeScans(b *testing.B, db dbm.ReadWriter, dbSize int64) { b.StartTimer() for i := 0; i < b.N; i++ { - start := rand.Int63n(dbSize - rangeSize) + start := rand.Int63n(dbSize - rangeSize) // nolint: gosec end := start + rangeSize iter, err := db.Iterator(Int64ToBytes(start), Int64ToBytes(end)) require.NoError(b, err) @@ -53,7 +53,7 @@ func BenchmarkRangeScans(b *testing.B, db dbm.ReadWriter, dbSize int64) { } } -func BenchmarkRandomReadsWrites(b *testing.B, db dbm.ReadWriter) { +func BenchmarkRandomReadsWrites(b *testing.B, db types.ReadWriter) { b.StopTimer() // create dummy data @@ -67,7 +67,7 @@ func BenchmarkRandomReadsWrites(b *testing.B, db dbm.ReadWriter) { for i := 0; i < b.N; i++ { { - idx := rand.Int63n(numItems) + idx := rand.Int63n(numItems) // nolint: gosec internal[idx]++ val := internal[idx] idxBytes := Int64ToBytes(idx) @@ -80,7 +80,7 @@ func BenchmarkRandomReadsWrites(b *testing.B, db dbm.ReadWriter) { } { - idx := rand.Int63n(numItems) + idx := rand.Int63n(numItems) // nolint: gosec valExp := internal[idx] idxBytes := Int64ToBytes(idx) valBytes, err := db.Get(idxBytes) diff --git a/db/dbtest/testcases.go b/db/dbtest/testcases.go index 78850159c91b..b80b77168bca 100644 --- a/db/dbtest/testcases.go +++ b/db/dbtest/testcases.go @@ -8,10 +8,10 @@ import ( "github.com/stretchr/testify/require" - dbm "github.com/cosmos/cosmos-sdk/db" + "github.com/cosmos/cosmos-sdk/db/types" ) -type Loader func(*testing.T, string) dbm.Connection +type Loader func(*testing.T, string) types.Connection func ikey(i int) []byte { return []byte(fmt.Sprintf("key-%03d", i)) } func ival(i int) []byte { return []byte(fmt.Sprintf("val-%03d", i)) } @@ -20,9 +20,10 @@ func DoTestGetSetHasDelete(t *testing.T, load Loader) { t.Helper() db := load(t, t.TempDir()) - var txn dbm.ReadWriter - view := db.Reader() + var txn types.ReadWriter + var view types.Reader + view = db.Reader() require.NotNil(t, view) // A nonexistent key should return nil. @@ -84,28 +85,28 @@ func DoTestGetSetHasDelete(t *testing.T, load Loader) { // Setting, getting, and deleting an empty key should error. _, err = txn.Get([]byte{}) - require.Equal(t, dbm.ErrKeyEmpty, err) + require.Equal(t, types.ErrKeyEmpty, err) _, err = txn.Get(nil) - require.Equal(t, dbm.ErrKeyEmpty, err) + require.Equal(t, types.ErrKeyEmpty, err) _, err = txn.Has([]byte{}) - require.Equal(t, dbm.ErrKeyEmpty, err) + require.Equal(t, types.ErrKeyEmpty, err) _, err = txn.Has(nil) - require.Equal(t, dbm.ErrKeyEmpty, err) + require.Equal(t, types.ErrKeyEmpty, err) err = txn.Set([]byte{}, []byte{0x01}) - require.Equal(t, dbm.ErrKeyEmpty, err) + require.Equal(t, types.ErrKeyEmpty, err) err = txn.Set(nil, []byte{0x01}) - require.Equal(t, dbm.ErrKeyEmpty, err) + require.Equal(t, types.ErrKeyEmpty, err) err = txn.Delete([]byte{}) - require.Equal(t, dbm.ErrKeyEmpty, err) + require.Equal(t, types.ErrKeyEmpty, err) err = txn.Delete(nil) - require.Equal(t, dbm.ErrKeyEmpty, err) + require.Equal(t, types.ErrKeyEmpty, err) // Setting a nil value should error, but an empty value is fine. err = txn.Set([]byte("x"), nil) - require.Equal(t, dbm.ErrValueNil, err) + require.Equal(t, types.ErrValueNil, err) err = txn.Set([]byte("x"), []byte{}) require.NoError(t, err) @@ -143,7 +144,7 @@ func DoTestIterators(t *testing.T, load Loader) { start, end []byte expected []string } - testRange := func(t *testing.T, iter dbm.Iterator, tc testCase) { + testRange := func(t *testing.T, iter types.Iterator, tc testCase) { i := 0 for ; iter.Next(); i++ { expectedValue := tc.expected[i] @@ -258,12 +259,12 @@ func DoTestVersioning(t *testing.T, load Loader) { require.False(t, has) require.NoError(t, view.Discard()) - view, err = db.ReaderAt(versions.Last() + 1) //nolint:staticcheck // we nolint here because we are checking for the absence of an error. - require.Equal(t, dbm.ErrVersionDoesNotExist, err, "should fail to read a nonexistent version") + view, err = db.ReaderAt(versions.Last() + 1) + require.Equal(t, types.ErrVersionDoesNotExist, err, "should fail to read a nonexistent version") require.NoError(t, db.DeleteVersion(v2), "should delete version v2") - view, err = db.ReaderAt(v2) //nolint:staticcheck // we nolint here because we are checking for the absence of an error. - require.Equal(t, dbm.ErrVersionDoesNotExist, err) + view, err = db.ReaderAt(v2) + require.Equal(t, types.ErrVersionDoesNotExist, err) // Ensure latest version is accurate prev := v3 @@ -294,10 +295,10 @@ func DoTestVersioning(t *testing.T, load Loader) { func DoTestTransactions(t *testing.T, load Loader, multipleWriters bool) { t.Helper() db := load(t, t.TempDir()) - // Both methods should work in a DBWriter context - writerFuncs := []func() dbm.Writer{ + // Both methods should work in a Writer context + writerFuncs := []func() types.Writer{ db.Writer, - func() dbm.Writer { return db.ReadWriter() }, + func() types.Writer { return db.ReadWriter() }, } for _, getWriter := range writerFuncs { @@ -320,7 +321,7 @@ func DoTestTransactions(t *testing.T, load Loader, multipleWriters bool) { tx := getWriter() require.NoError(t, tx.Set([]byte("0"), []byte("a"))) _, err := db.SaveNextVersion() - require.Equal(t, dbm.ErrOpenTransactions, err) + require.Equal(t, types.ErrOpenTransactions, err) require.NoError(t, tx.Discard()) }) @@ -394,7 +395,7 @@ func DoTestRevert(t *testing.T, load Loader, reload bool) { t.Helper() dirname := t.TempDir() db := load(t, dirname) - var txn dbm.Writer + var txn types.Writer initContents := func() { txn = db.Writer() @@ -452,7 +453,7 @@ func DoTestRevert(t *testing.T, load Loader, reload bool) { view.Discard() } - changeContents := func() { + modifyContents := func() { txn = db.Writer() require.NoError(t, txn.Set([]byte{3}, []byte{15})) require.NoError(t, txn.Set([]byte{7}, []byte{70})) @@ -468,11 +469,11 @@ func DoTestRevert(t *testing.T, load Loader, reload bool) { require.NoError(t, txn.Commit()) } - changeContents() + modifyContents() - cases := []func(dbm.Connection) error{ - func(db dbm.Connection) error { return db.Revert() }, - func(db dbm.Connection) error { return db.RevertTo(v1) }, + cases := []func(types.Connection) error{ + func(db types.Connection) error { return db.Revert() }, + func(db types.Connection) error { return db.RevertTo(v1) }, } for _, revertFunc := range cases { if reload { @@ -493,7 +494,7 @@ func DoTestRevert(t *testing.T, load Loader, reload bool) { checkContents() // With intermediate versions added & deleted, revert again to v1 - changeContents() + modifyContents() v2, _ := db.SaveNextVersion() txn = db.Writer() diff --git a/db/dbtest/util.go b/db/dbtest/util.go index 66bc208bf7ad..c567988f1559 100644 --- a/db/dbtest/util.go +++ b/db/dbtest/util.go @@ -6,47 +6,47 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - dbm "github.com/cosmos/cosmos-sdk/db" + "github.com/cosmos/cosmos-sdk/db/types" ) -func AssertNext(t *testing.T, itr dbm.Iterator, expected bool) { +func AssertNext(t *testing.T, itr types.Iterator, expected bool) { t.Helper() require.Equal(t, expected, itr.Next()) } -func AssertDomain(t *testing.T, itr dbm.Iterator, start, end []byte) { +func AssertDomain(t *testing.T, itr types.Iterator, start, end []byte) { t.Helper() ds, de := itr.Domain() assert.Equal(t, start, ds, "checkDomain domain start incorrect") assert.Equal(t, end, de, "checkDomain domain end incorrect") } -func AssertItem(t *testing.T, itr dbm.Iterator, key, value []byte) { +func AssertItem(t *testing.T, itr types.Iterator, key, value []byte) { t.Helper() assert.Exactly(t, key, itr.Key()) assert.Exactly(t, value, itr.Value()) } -func AssertInvalid(t *testing.T, itr dbm.Iterator) { +func AssertInvalid(t *testing.T, itr types.Iterator) { t.Helper() AssertNext(t, itr, false) AssertKeyPanics(t, itr) AssertValuePanics(t, itr) } -func AssertKeyPanics(t *testing.T, itr dbm.Iterator) { +func AssertKeyPanics(t *testing.T, itr types.Iterator) { t.Helper() assert.Panics(t, func() { itr.Key() }, "checkKeyPanics expected panic but didn't") } -func AssertValue(t *testing.T, db dbm.Reader, key, valueWanted []byte) { +func AssertValue(t *testing.T, db types.Reader, key, valueWanted []byte) { t.Helper() valueGot, err := db.Get(key) assert.NoError(t, err) assert.Equal(t, valueWanted, valueGot) } -func AssertValuePanics(t *testing.T, itr dbm.Iterator) { +func AssertValuePanics(t *testing.T, itr types.Iterator) { t.Helper() assert.Panics(t, func() { itr.Value() }) } diff --git a/db/dbtest/version_set.go b/db/dbtest/version_set.go index 017d90b76676..7bdcc2cd01ec 100644 --- a/db/dbtest/version_set.go +++ b/db/dbtest/version_set.go @@ -6,12 +6,12 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/cosmos-sdk/db" + "github.com/cosmos/cosmos-sdk/db/types" ) // Test that a type satisfies the behavior of VersionSet -func DoTestVersionSet(t *testing.T, new func([]uint64) db.VersionSet) { - vm := db.NewVersionManager(nil) +func DoTestVersionSet(t *testing.T, new func([]uint64) types.VersionSet) { + vm := types.NewVersionManager(nil) require.Equal(t, uint64(0), vm.Last()) require.Equal(t, 0, vm.Count()) require.True(t, vm.Equal(vm)) @@ -50,17 +50,17 @@ func DoTestVersionSet(t *testing.T, new func([]uint64) db.VersionSet) { require.NoError(t, err) require.False(t, vm.Exists(id5)) // true copy is made - vm2 := db.NewVersionManager([]uint64{id2, id3}) + vm2 := types.NewVersionManager([]uint64{id2, id3}) require.True(t, vm.Equal(vm2)) - vm = db.NewVersionManager([]uint64{1, 2, 3, 5, 10}) + vm = types.NewVersionManager([]uint64{1, 2, 3, 5, 10}) vm.DeleteAbove(10) require.Equal(t, []uint64{1, 2, 3, 5, 10}, allVersions(vm)) vm.DeleteAbove(4) require.Equal(t, []uint64{1, 2, 3}, allVersions(vm)) } -func allVersions(vm *db.VersionManager) (all []uint64) { +func allVersions(vm *types.VersionManager) (all []uint64) { for it := vm.Iterator(); it.Next(); { all = append(all, it.Value()) } diff --git a/db/internal/backends/imports.go b/db/internal/backends/imports.go new file mode 100644 index 000000000000..2069e37c3865 --- /dev/null +++ b/db/internal/backends/imports.go @@ -0,0 +1,8 @@ +// This is a dummy package used to trigger initialization of backend creators +package backends + +import ( + _ "github.com/cosmos/cosmos-sdk/db/badgerdb" + _ "github.com/cosmos/cosmos-sdk/db/memdb" + _ "github.com/cosmos/cosmos-sdk/db/rocksdb" +) diff --git a/db/internal/util.go b/db/internal/util.go index ea7425319ce3..cc35d33e6ace 100644 --- a/db/internal/util.go +++ b/db/internal/util.go @@ -3,15 +3,15 @@ package util import ( "fmt" - dbm "github.com/cosmos/cosmos-sdk/db" + "github.com/cosmos/cosmos-sdk/db/types" ) func ValidateKv(key, value []byte) error { if len(key) == 0 { - return dbm.ErrKeyEmpty + return types.ErrKeyEmpty } if value == nil { - return dbm.ErrValueNil + return types.ErrValueNil } return nil } diff --git a/db/memdb/creator.go b/db/memdb/creator.go new file mode 100644 index 000000000000..a1ad082ca997 --- /dev/null +++ b/db/memdb/creator.go @@ -0,0 +1,13 @@ +// No build directive, memdb is always built +package memdb + +import ( + "github.com/cosmos/cosmos-sdk/db/types" +) + +func init() { + creator := func(name string, dir string) (types.Connection, error) { + return NewDB(), nil + } + types.RegisterCreator(types.MemDBBackend, creator, false) +} diff --git a/db/memdb/db.go b/db/memdb/db.go index 75ccfb1725b9..0343fefb06f7 100644 --- a/db/memdb/db.go +++ b/db/memdb/db.go @@ -6,8 +6,8 @@ import ( "sync" "sync/atomic" - "github.com/cosmos/cosmos-sdk/db" dbutil "github.com/cosmos/cosmos-sdk/db/internal" + "github.com/cosmos/cosmos-sdk/db/types" "github.com/google/btree" ) @@ -16,13 +16,6 @@ const ( bTreeDegree = 32 ) -func init() { - creator := func(name string, dir string) (db.Connection, error) { - return NewDB(), nil - } - db.RegisterCreator(db.MemDBBackend, creator, false) -} - // MemDB is an in-memory database backend using a B-tree for storage. // // For performance reasons, all given and returned keys and values are pointers to the in-memory @@ -39,7 +32,7 @@ type MemDB struct { btree *btree.BTree // Main contents mtx sync.RWMutex // Guards version history saved map[uint64]*btree.BTree // Past versions - vmgr *db.VersionManager // Mirrors version keys + vmgr *types.VersionManager // Mirrors version keys openWriters int32 // Open writers } @@ -50,10 +43,10 @@ type dbTxn struct { type dbWriter struct{ dbTxn } var ( - _ db.Connection = (*MemDB)(nil) - _ db.Reader = (*dbTxn)(nil) - _ db.Writer = (*dbWriter)(nil) - _ db.ReadWriter = (*dbWriter)(nil) + _ types.Connection = (*MemDB)(nil) + _ types.Reader = (*dbTxn)(nil) + _ types.Writer = (*dbWriter)(nil) + _ types.ReadWriter = (*dbWriter)(nil) ) // item is a btree.Item with byte slices as keys and values @@ -67,7 +60,7 @@ func NewDB() *MemDB { return &MemDB{ btree: btree.New(bTreeDegree), saved: make(map[uint64]*btree.BTree), - vmgr: db.NewVersionManager(nil), + vmgr: types.NewVersionManager(nil), } } @@ -75,7 +68,7 @@ func (dbm *MemDB) newTxn(tree *btree.BTree) dbTxn { return dbTxn{tree, dbm} } -// Close implements DB. +// Close implements Connection. // Close is a noop since for an in-memory database, we don't have a destination to flush // contents to nor do we want any data loss on invoking Close(). // See the discussion in https://github.com/tendermint/tendermint/libs/pull/56 @@ -84,14 +77,14 @@ func (dbm *MemDB) Close() error { } // Versions implements Connection. -func (dbm *MemDB) Versions() (db.VersionSet, error) { +func (dbm *MemDB) Versions() (types.VersionSet, error) { dbm.mtx.RLock() defer dbm.mtx.RUnlock() return dbm.vmgr, nil } // Reader implements Connection. -func (dbm *MemDB) Reader() db.Reader { +func (dbm *MemDB) Reader() types.Reader { dbm.mtx.RLock() defer dbm.mtx.RUnlock() ret := dbm.newTxn(dbm.btree) @@ -99,24 +92,24 @@ func (dbm *MemDB) Reader() db.Reader { } // ReaderAt implements Connection. -func (dbm *MemDB) ReaderAt(version uint64) (db.Reader, error) { +func (dbm *MemDB) ReaderAt(version uint64) (types.Reader, error) { dbm.mtx.RLock() defer dbm.mtx.RUnlock() tree, ok := dbm.saved[version] if !ok { - return nil, db.ErrVersionDoesNotExist + return nil, types.ErrVersionDoesNotExist } ret := dbm.newTxn(tree) return &ret, nil } // Writer implements Connection. -func (dbm *MemDB) Writer() db.Writer { +func (dbm *MemDB) Writer() types.Writer { return dbm.ReadWriter() } // ReadWriter implements Connection. -func (dbm *MemDB) ReadWriter() db.ReadWriter { +func (dbm *MemDB) ReadWriter() types.ReadWriter { dbm.mtx.RLock() defer dbm.mtx.RUnlock() atomic.AddInt32(&dbm.openWriters, 1) @@ -128,7 +121,7 @@ func (dbm *MemDB) save(target uint64) (uint64, error) { dbm.mtx.Lock() defer dbm.mtx.Unlock() if dbm.openWriters > 0 { - return 0, db.ErrOpenTransactions + return 0, types.ErrOpenTransactions } newVmgr := dbm.vmgr.Copy() @@ -149,7 +142,7 @@ func (dbm *MemDB) SaveNextVersion() (uint64, error) { // SaveNextVersion implements Connection. func (dbm *MemDB) SaveVersion(target uint64) error { if target == 0 { - return db.ErrInvalidVersion + return types.ErrInvalidVersion } _, err := dbm.save(target) return err @@ -160,7 +153,7 @@ func (dbm *MemDB) DeleteVersion(target uint64) error { dbm.mtx.Lock() defer dbm.mtx.Unlock() if _, has := dbm.saved[target]; !has { - return db.ErrVersionDoesNotExist + return types.ErrVersionDoesNotExist } delete(dbm.saved, target) dbm.vmgr = dbm.vmgr.Copy() @@ -172,7 +165,7 @@ func (dbm *MemDB) Revert() error { dbm.mtx.RLock() defer dbm.mtx.RUnlock() if dbm.openWriters > 0 { - return db.ErrOpenTransactions + return types.ErrOpenTransactions } last := dbm.vmgr.Last() if last == 0 { @@ -186,10 +179,10 @@ func (dbm *MemDB) RevertTo(target uint64) error { dbm.mtx.RLock() defer dbm.mtx.RUnlock() if dbm.openWriters > 0 { - return db.ErrOpenTransactions + return types.ErrOpenTransactions } if !dbm.vmgr.Exists(target) { - return db.ErrVersionDoesNotExist + return types.ErrVersionDoesNotExist } err := dbm.revert(target) if err != nil { @@ -212,13 +205,13 @@ func (dbm *MemDB) revert(target uint64) error { return nil } -// Get implements DBReader. +// Get implements Reader. func (tx *dbTxn) Get(key []byte) ([]byte, error) { if tx.btree == nil { - return nil, db.ErrTransactionClosed + return nil, types.ErrTransactionClosed } if len(key) == 0 { - return nil, db.ErrKeyEmpty + return nil, types.ErrKeyEmpty } i := tx.btree.Get(newKey(key)) if i != nil { @@ -227,21 +220,21 @@ func (tx *dbTxn) Get(key []byte) ([]byte, error) { return nil, nil } -// Has implements DBReader. +// Has implements Reader. func (tx *dbTxn) Has(key []byte) (bool, error) { if tx.btree == nil { - return false, db.ErrTransactionClosed + return false, types.ErrTransactionClosed } if len(key) == 0 { - return false, db.ErrKeyEmpty + return false, types.ErrKeyEmpty } return tx.btree.Has(newKey(key)), nil } -// Set implements DBWriter. +// Set implements Writer. func (tx *dbWriter) Set(key []byte, value []byte) error { if tx.btree == nil { - return db.ErrTransactionClosed + return types.ErrTransactionClosed } if err := dbutil.ValidateKv(key, value); err != nil { return err @@ -250,46 +243,46 @@ func (tx *dbWriter) Set(key []byte, value []byte) error { return nil } -// Delete implements DBWriter. +// Delete implements Writer. func (tx *dbWriter) Delete(key []byte) error { if tx.btree == nil { - return db.ErrTransactionClosed + return types.ErrTransactionClosed } if len(key) == 0 { - return db.ErrKeyEmpty + return types.ErrKeyEmpty } tx.btree.Delete(newKey(key)) return nil } -// Iterator implements DBReader. +// Iterator implements Reader. // Takes out a read-lock on the database until the iterator is closed. -func (tx *dbTxn) Iterator(start, end []byte) (db.Iterator, error) { +func (tx *dbTxn) Iterator(start, end []byte) (types.Iterator, error) { if tx.btree == nil { - return nil, db.ErrTransactionClosed + return nil, types.ErrTransactionClosed } if (start != nil && len(start) == 0) || (end != nil && len(end) == 0) { - return nil, db.ErrKeyEmpty + return nil, types.ErrKeyEmpty } return newMemDBIterator(tx, start, end, false), nil } -// ReverseIterator implements DBReader. +// ReverseIterator implements Reader. // Takes out a read-lock on the database until the iterator is closed. -func (tx *dbTxn) ReverseIterator(start, end []byte) (db.Iterator, error) { +func (tx *dbTxn) ReverseIterator(start, end []byte) (types.Iterator, error) { if tx.btree == nil { - return nil, db.ErrTransactionClosed + return nil, types.ErrTransactionClosed } if (start != nil && len(start) == 0) || (end != nil && len(end) == 0) { - return nil, db.ErrKeyEmpty + return nil, types.ErrKeyEmpty } return newMemDBIterator(tx, start, end, true), nil } -// Commit implements DBWriter. +// Commit implements Writer. func (tx *dbWriter) Commit() error { if tx.btree == nil { - return db.ErrTransactionClosed + return types.ErrTransactionClosed } tx.db.mtx.Lock() defer tx.db.mtx.Unlock() @@ -297,7 +290,7 @@ func (tx *dbWriter) Commit() error { return tx.Discard() } -// Discard implements DBReader. +// Discard implements Reader. func (tx *dbTxn) Discard() error { if tx.btree != nil { tx.btree = nil @@ -305,7 +298,7 @@ func (tx *dbTxn) Discard() error { return nil } -// Discard implements DBWriter. +// Discard implements Writer. func (tx *dbWriter) Discard() error { if tx.btree != nil { defer atomic.AddInt32(&tx.db.openWriters, -1) diff --git a/db/memdb/db_test.go b/db/memdb/db_test.go index e330718ca6cb..c52fec72dad2 100644 --- a/db/memdb/db_test.go +++ b/db/memdb/db_test.go @@ -3,8 +3,8 @@ package memdb import ( "testing" - "github.com/cosmos/cosmos-sdk/db" "github.com/cosmos/cosmos-sdk/db/dbtest" + "github.com/cosmos/cosmos-sdk/db/types" ) func BenchmarkMemDBRangeScans1M(b *testing.B) { @@ -28,7 +28,7 @@ func BenchmarkMemDBRandomReadsWrites(b *testing.B) { dbtest.BenchmarkRandomReadsWrites(b, dbm.ReadWriter()) } -func load(t *testing.T, _ string) db.Connection { +func load(t *testing.T, _ string) types.Connection { return NewDB() } diff --git a/db/memdb/iterator.go b/db/memdb/iterator.go index 7d2f89721127..c0a215ec07d4 100644 --- a/db/memdb/iterator.go +++ b/db/memdb/iterator.go @@ -4,7 +4,7 @@ import ( "bytes" "context" - "github.com/cosmos/cosmos-sdk/db" + "github.com/cosmos/cosmos-sdk/db/types" "github.com/google/btree" ) @@ -24,7 +24,7 @@ type memDBIterator struct { end []byte } -var _ db.Iterator = (*memDBIterator)(nil) +var _ types.Iterator = (*memDBIterator)(nil) // newMemDBIterator creates a new memDBIterator. // A visitor is passed to the btree which streams items to the iterator over a channel. Advancing diff --git a/db/prefix/prefix.go b/db/prefix/prefix.go index 2d218cedcbe5..43522d4add1f 100644 --- a/db/prefix/prefix.go +++ b/db/prefix/prefix.go @@ -3,36 +3,36 @@ package prefix import ( - "github.com/cosmos/cosmos-sdk/db" + "github.com/cosmos/cosmos-sdk/db/types" ) // prefixed Reader type Reader struct { - db db.Reader + db types.Reader prefix []byte } // prefixed ReadWriter type ReadWriter struct { - db db.ReadWriter + db types.ReadWriter prefix []byte } // prefixed Writer type Writer struct { - db db.Writer + db types.Writer prefix []byte } var ( - _ db.Reader = (*Reader)(nil) - _ db.ReadWriter = (*ReadWriter)(nil) - _ db.Writer = (*Writer)(nil) + _ types.Reader = (*Reader)(nil) + _ types.ReadWriter = (*ReadWriter)(nil) + _ types.Writer = (*Writer)(nil) ) // NewReadereader returns a DBReader that only has access to the subset of DB keys // that contain the given prefix. -func NewReader(dbr db.Reader, prefix []byte) Reader { +func NewReader(dbr types.Reader, prefix []byte) Reader { return Reader{ prefix: prefix, db: dbr, @@ -41,7 +41,7 @@ func NewReader(dbr db.Reader, prefix []byte) Reader { // NewReadWriter returns a DBReader that only has access to the subset of DB keys // that contain the given prefix. -func NewReadWriter(dbrw db.ReadWriter, prefix []byte) ReadWriter { +func NewReadWriter(dbrw types.ReadWriter, prefix []byte) ReadWriter { return ReadWriter{ prefix: prefix, db: dbrw, @@ -50,7 +50,7 @@ func NewReadWriter(dbrw db.ReadWriter, prefix []byte) ReadWriter { // NewWriterriter returns a DBWriter that reads/writes only from the subset of DB keys // that contain the given prefix -func NewWriter(dbw db.Writer, prefix []byte) Writer { +func NewWriter(dbw types.Writer, prefix []byte) Writer { return Writer{ prefix: prefix, db: dbw, @@ -64,7 +64,7 @@ func prefixed(prefix, key []byte) []byte { // Get implements DBReader. func (pdb Reader) Get(key []byte) ([]byte, error) { if len(key) == 0 { - return nil, db.ErrKeyEmpty + return nil, types.ErrKeyEmpty } return pdb.db.Get(prefixed(pdb.prefix, key)) } @@ -72,15 +72,15 @@ func (pdb Reader) Get(key []byte) ([]byte, error) { // Has implements DBReader. func (pdb Reader) Has(key []byte) (bool, error) { if len(key) == 0 { - return false, db.ErrKeyEmpty + return false, types.ErrKeyEmpty } return pdb.db.Has(prefixed(pdb.prefix, key)) } // Iterator implements DBReader. -func (pdb Reader) Iterator(start, end []byte) (db.Iterator, error) { +func (pdb Reader) Iterator(start, end []byte) (types.Iterator, error) { if (start != nil && len(start) == 0) || (end != nil && len(end) == 0) { - return nil, db.ErrKeyEmpty + return nil, types.ErrKeyEmpty } var pend []byte @@ -97,9 +97,9 @@ func (pdb Reader) Iterator(start, end []byte) (db.Iterator, error) { } // ReverseIterator implements DBReader. -func (pdb Reader) ReverseIterator(start, end []byte) (db.Iterator, error) { +func (pdb Reader) ReverseIterator(start, end []byte) (types.Iterator, error) { if (start != nil && len(start) == 0) || (end != nil && len(end) == 0) { - return nil, db.ErrKeyEmpty + return nil, types.ErrKeyEmpty } var pend []byte @@ -121,7 +121,7 @@ func (pdb Reader) Discard() error { return pdb.db.Discard() } // Set implements DBReadWriter. func (pdb ReadWriter) Set(key []byte, value []byte) error { if len(key) == 0 { - return db.ErrKeyEmpty + return types.ErrKeyEmpty } return pdb.db.Set(prefixed(pdb.prefix, key), value) } @@ -129,7 +129,7 @@ func (pdb ReadWriter) Set(key []byte, value []byte) error { // Delete implements DBReadWriter. func (pdb ReadWriter) Delete(key []byte) error { if len(key) == 0 { - return db.ErrKeyEmpty + return types.ErrKeyEmpty } return pdb.db.Delete(prefixed(pdb.prefix, key)) } @@ -145,12 +145,12 @@ func (pdb ReadWriter) Has(key []byte) (bool, error) { } // Iterator implements DBReadWriter. -func (pdb ReadWriter) Iterator(start, end []byte) (db.Iterator, error) { +func (pdb ReadWriter) Iterator(start, end []byte) (types.Iterator, error) { return NewReader(pdb.db, pdb.prefix).Iterator(start, end) } // ReverseIterator implements DBReadWriter. -func (pdb ReadWriter) ReverseIterator(start, end []byte) (db.Iterator, error) { +func (pdb ReadWriter) ReverseIterator(start, end []byte) (types.Iterator, error) { return NewReader(pdb.db, pdb.prefix).ReverseIterator(start, end) } @@ -163,7 +163,7 @@ func (pdb ReadWriter) Discard() error { return pdb.db.Discard() } // Set implements DBReadWriter. func (pdb Writer) Set(key []byte, value []byte) error { if len(key) == 0 { - return db.ErrKeyEmpty + return types.ErrKeyEmpty } return pdb.db.Set(prefixed(pdb.prefix, key), value) } @@ -171,7 +171,7 @@ func (pdb Writer) Set(key []byte, value []byte) error { // Delete implements DBWriter. func (pdb Writer) Delete(key []byte) error { if len(key) == 0 { - return db.ErrKeyEmpty + return types.ErrKeyEmpty } return pdb.db.Delete(prefixed(pdb.prefix, key)) } diff --git a/db/reexport.go b/db/reexport.go new file mode 100644 index 000000000000..50da6b2de29b --- /dev/null +++ b/db/reexport.go @@ -0,0 +1,30 @@ +package db + +import ( + "github.com/cosmos/cosmos-sdk/db/types" + + _ "github.com/cosmos/cosmos-sdk/db/internal/backends" +) + +type ( + Connection = types.Connection + Reader = types.Reader + Writer = types.Writer + ReadWriter = types.ReadWriter + Iterator = types.Iterator + VersionSet = types.VersionSet + VersionIterator = types.VersionIterator + BackendType = types.BackendType +) + +var ( + ErrVersionDoesNotExist = types.ErrVersionDoesNotExist + + MemDBBackend = types.MemDBBackend + RocksDBBackend = types.RocksDBBackend + BadgerDBBackend = types.BadgerDBBackend + + NewDB = types.NewDB + ReaderAsReadWriter = types.ReaderAsReadWriter + NewVersionManager = types.NewVersionManager +) diff --git a/db/rocksdb/batch.go b/db/rocksdb/batch.go index d9b257744fc4..6f497ede3e29 100644 --- a/db/rocksdb/batch.go +++ b/db/rocksdb/batch.go @@ -1,12 +1,10 @@ -//go:build rocksdb - package rocksdb import ( "sync/atomic" - "github.com/cosmos/cosmos-sdk/db" dbutil "github.com/cosmos/cosmos-sdk/db/internal" + "github.com/cosmos/cosmos-sdk/db/types" "github.com/cosmos/gorocksdb" ) @@ -15,7 +13,7 @@ type rocksDBBatch struct { mgr *dbManager } -var _ db.Writer = (*rocksDBBatch)(nil) +var _ types.Writer = (*rocksDBBatch)(nil) func (mgr *dbManager) newRocksDBBatch() *rocksDBBatch { return &rocksDBBatch{ @@ -30,7 +28,7 @@ func (b *rocksDBBatch) Set(key, value []byte) error { return err } if b.batch == nil { - return db.ErrTransactionClosed + return types.ErrTransactionClosed } b.batch.Put(key, value) return nil @@ -39,10 +37,10 @@ func (b *rocksDBBatch) Set(key, value []byte) error { // Delete implements Writer. func (b *rocksDBBatch) Delete(key []byte) error { if len(key) == 0 { - return db.ErrKeyEmpty + return types.ErrKeyEmpty } if b.batch == nil { - return db.ErrTransactionClosed + return types.ErrTransactionClosed } b.batch.Delete(key) return nil @@ -51,7 +49,7 @@ func (b *rocksDBBatch) Delete(key []byte) error { // Write implements Writer. func (b *rocksDBBatch) Commit() (err error) { if b.batch == nil { - return db.ErrTransactionClosed + return types.ErrTransactionClosed } defer func() { err = dbutil.CombineErrors(err, b.Discard(), "Discard also failed") }() err = b.mgr.current.Write(b.mgr.opts.wo, b.batch) diff --git a/db/rocksdb/creator.go b/db/rocksdb/creator.go new file mode 100644 index 000000000000..afedb4beb8e1 --- /dev/null +++ b/db/rocksdb/creator.go @@ -0,0 +1,17 @@ +//go:build rocksdb + +package rocksdb + +import ( + "path/filepath" + + "github.com/cosmos/cosmos-sdk/db/types" +) + +func init() { + creator := func(name string, dir string) (types.Connection, error) { + dir = filepath.Join(dir, name) + return NewDB(dir) + } + types.RegisterCreator(types.RocksDBBackend, creator, false) +} diff --git a/db/rocksdb/db.go b/db/rocksdb/db.go index f3a6448c4b3d..d42bf377e7d3 100644 --- a/db/rocksdb/db.go +++ b/db/rocksdb/db.go @@ -1,5 +1,3 @@ -//go:build rocksdb - package rocksdb import ( @@ -11,8 +9,8 @@ import ( "sync" "sync/atomic" - "github.com/cosmos/cosmos-sdk/db" dbutil "github.com/cosmos/cosmos-sdk/db/internal" + "github.com/cosmos/cosmos-sdk/db/types" "github.com/cosmos/gorocksdb" ) @@ -22,27 +20,27 @@ var ( ) var ( - _ db.Connection = (*RocksDB)(nil) - _ db.Reader = (*dbTxn)(nil) - _ db.Writer = (*dbWriter)(nil) - _ db.ReadWriter = (*dbWriter)(nil) + _ types.Connection = (*RocksDB)(nil) + _ types.Reader = (*dbTxn)(nil) + _ types.Writer = (*dbWriter)(nil) + _ types.ReadWriter = (*dbWriter)(nil) ) // RocksDB is a connection to a RocksDB key-value database. type RocksDB = dbManager type dbManager struct { - current *Connection + current *dbConnection dir string opts dbOptions - vmgr *db.VersionManager + vmgr *types.VersionManager mtx sync.RWMutex - // Track open DBWriters + // Track open Writers openWriters int32 cpCache checkpointCache } -type Connection = gorocksdb.OptimisticTransactionDB +type dbConnection = gorocksdb.OptimisticTransactionDB type checkpointCache struct { cache map[uint64]*cpCacheEntry @@ -50,7 +48,7 @@ type checkpointCache struct { } type cpCacheEntry struct { - cxn *Connection + cxn *dbConnection openCount uint } @@ -68,14 +66,6 @@ type dbOptions struct { wo *gorocksdb.WriteOptions } -func init() { - creator := func(name string, dir string) (db.DBConnection, error) { - dir = filepath.Join(dir, name) - return NewDB(dir) - } - db.RegisterCreator(db.RocksDBBackend, creator, false) -} - // NewDB creates a new RocksDB key-value database with inside the given directory. // If dir does not exist, it will be created. func NewDB(dir string) (*dbManager, error) { @@ -137,7 +127,7 @@ func (mgr *dbManager) checkpointsDir() string { } // Reads directory for checkpoints files -func readVersions(dir string) (*db.VersionManager, error) { +func readVersions(dir string) (*types.VersionManager, error) { files, err := os.ReadDir(dir) if err != nil { return nil, err @@ -150,23 +140,23 @@ func readVersions(dir string) (*db.VersionManager, error) { } versions = append(versions, version) } - return db.NewVersionManager(versions), nil + return types.NewVersionManager(versions), nil } func (mgr *dbManager) checkpointPath(version uint64) (string, error) { dbPath := filepath.Join(mgr.checkpointsDir(), fmt.Sprintf(checkpointFileFormat, version)) if stat, err := os.Stat(dbPath); err != nil { if errors.Is(err, os.ErrNotExist) { - err = db.ErrVersionDoesNotExist + err = types.ErrVersionDoesNotExist } return "", err } else if !stat.IsDir() { - return "", db.ErrVersionDoesNotExist + return "", types.ErrVersionDoesNotExist } return dbPath, nil } -func (mgr *dbManager) openCheckpoint(version uint64) (*Connection, error) { +func (mgr *dbManager) openCheckpoint(version uint64) (*dbConnection, error) { mgr.cpCache.mtx.Lock() defer mgr.cpCache.mtx.Unlock() cp, has := mgr.cpCache.cache[version] @@ -186,7 +176,7 @@ func (mgr *dbManager) openCheckpoint(version uint64) (*Connection, error) { return db, nil } -func (mgr *dbManager) Reader() db.Reader { +func (mgr *dbManager) Reader() types.Reader { mgr.mtx.RLock() defer mgr.mtx.RUnlock() return &dbTxn{ @@ -197,7 +187,7 @@ func (mgr *dbManager) Reader() db.Reader { } } -func (mgr *dbManager) ReaderAt(version uint64) (db.Reader, error) { +func (mgr *dbManager) ReaderAt(version uint64) (types.Reader, error) { mgr.mtx.RLock() defer mgr.mtx.RUnlock() d, err := mgr.openCheckpoint(version) @@ -212,7 +202,7 @@ func (mgr *dbManager) ReaderAt(version uint64) (db.Reader, error) { }, nil } -func (mgr *dbManager) ReadWriter() db.ReadWriter { +func (mgr *dbManager) ReadWriter() types.ReadWriter { mgr.mtx.RLock() defer mgr.mtx.RUnlock() atomic.AddInt32(&mgr.openWriters, 1) @@ -222,14 +212,14 @@ func (mgr *dbManager) ReadWriter() db.ReadWriter { }} } -func (mgr *dbManager) Writer() db.Writer { +func (mgr *dbManager) Writer() types.Writer { mgr.mtx.RLock() defer mgr.mtx.RUnlock() atomic.AddInt32(&mgr.openWriters, 1) return mgr.newRocksDBBatch() } -func (mgr *dbManager) Versions() (db.VersionSet, error) { +func (mgr *dbManager) Versions() (types.VersionSet, error) { mgr.mtx.RLock() defer mgr.mtx.RUnlock() return mgr.vmgr, nil @@ -243,7 +233,7 @@ func (mgr *dbManager) SaveNextVersion() (uint64, error) { // SaveVersion implements Connection. func (mgr *dbManager) SaveVersion(target uint64) error { if target == 0 { - return db.ErrInvalidVersion + return types.ErrInvalidVersion } _, err := mgr.save(target) return err @@ -253,7 +243,7 @@ func (mgr *dbManager) save(target uint64) (uint64, error) { mgr.mtx.Lock() defer mgr.mtx.Unlock() if mgr.openWriters > 0 { - return 0, db.ErrOpenTransactions + return 0, types.ErrOpenTransactions } newVmgr := mgr.vmgr.Copy() target, err := newVmgr.Save(target) @@ -275,7 +265,7 @@ func (mgr *dbManager) save(target uint64) (uint64, error) { func (mgr *dbManager) DeleteVersion(ver uint64) error { if mgr.cpCache.has(ver) { - return db.ErrOpenTransactions + return types.ErrOpenTransactions } mgr.mtx.Lock() defer mgr.mtx.Unlock() @@ -292,7 +282,7 @@ func (mgr *dbManager) Revert() (err error) { mgr.mtx.RLock() defer mgr.mtx.RUnlock() if mgr.openWriters > 0 { - return db.ErrOpenTransactions + return types.ErrOpenTransactions } return mgr.revert(mgr.vmgr.Last()) } @@ -301,10 +291,10 @@ func (mgr *dbManager) RevertTo(target uint64) (err error) { mgr.mtx.RLock() defer mgr.mtx.RUnlock() if mgr.openWriters > 0 { - return db.ErrOpenTransactions + return types.ErrOpenTransactions } if !mgr.vmgr.Exists(target) { - return db.ErrVersionDoesNotExist + return types.ErrVersionDoesNotExist } err = mgr.revert(target) if err != nil { @@ -370,10 +360,10 @@ func (mgr *dbManager) Stats() map[string]string { // Get implements Reader. func (tx *dbTxn) Get(key []byte) ([]byte, error) { if tx.txn == nil { - return nil, db.ErrTransactionClosed + return nil, types.ErrTransactionClosed } if len(key) == 0 { - return nil, db.ErrKeyEmpty + return nil, types.ErrKeyEmpty } res, err := tx.txn.Get(tx.mgr.opts.ro, key) if err != nil { @@ -385,10 +375,10 @@ func (tx *dbTxn) Get(key []byte) ([]byte, error) { // Get implements Reader. func (tx *dbWriter) Get(key []byte) ([]byte, error) { if tx.txn == nil { - return nil, db.ErrTransactionClosed + return nil, types.ErrTransactionClosed } if len(key) == 0 { - return nil, db.ErrKeyEmpty + return nil, types.ErrKeyEmpty } res, err := tx.txn.GetForUpdate(tx.mgr.opts.ro, key) if err != nil { @@ -397,7 +387,7 @@ func (tx *dbWriter) Get(key []byte) ([]byte, error) { return moveSliceToBytes(res), nil } -// Has implements DBReader. +// Has implements Reader. func (tx *dbTxn) Has(key []byte) (bool, error) { bytes, err := tx.Get(key) if err != nil { @@ -406,10 +396,10 @@ func (tx *dbTxn) Has(key []byte) (bool, error) { return bytes != nil, nil } -// Set implements DBWriter. +// Set implements Writer. func (tx *dbWriter) Set(key []byte, value []byte) error { if tx.txn == nil { - return db.ErrTransactionClosed + return types.ErrTransactionClosed } if err := dbutil.ValidateKv(key, value); err != nil { return err @@ -417,20 +407,20 @@ func (tx *dbWriter) Set(key []byte, value []byte) error { return tx.txn.Put(key, value) } -// Delete implements DBWriter. +// Delete implements Writer. func (tx *dbWriter) Delete(key []byte) error { if tx.txn == nil { - return db.ErrTransactionClosed + return types.ErrTransactionClosed } if len(key) == 0 { - return db.ErrKeyEmpty + return types.ErrKeyEmpty } return tx.txn.Delete(key) } func (tx *dbWriter) Commit() (err error) { if tx.txn == nil { - return db.ErrTransactionClosed + return types.ErrTransactionClosed } defer func() { err = dbutil.CombineErrors(err, tx.Discard(), "Discard also failed") }() err = tx.txn.Commit() @@ -458,25 +448,25 @@ func (tx *dbWriter) Discard() error { return tx.dbTxn.Discard() } -// Iterator implements DBReader. -func (tx *dbTxn) Iterator(start, end []byte) (db.Iterator, error) { +// Iterator implements Reader. +func (tx *dbTxn) Iterator(start, end []byte) (types.Iterator, error) { if tx.txn == nil { - return nil, db.ErrTransactionClosed + return nil, types.ErrTransactionClosed } if (start != nil && len(start) == 0) || (end != nil && len(end) == 0) { - return nil, db.ErrKeyEmpty + return nil, types.ErrKeyEmpty } itr := tx.txn.NewIterator(tx.mgr.opts.ro) return newRocksDBIterator(itr, start, end, false), nil } -// ReverseIterator implements DBReader. -func (tx *dbTxn) ReverseIterator(start, end []byte) (db.Iterator, error) { +// ReverseIterator implements Reader. +func (tx *dbTxn) ReverseIterator(start, end []byte) (types.Iterator, error) { if tx.txn == nil { - return nil, db.ErrTransactionClosed + return nil, types.ErrTransactionClosed } if (start != nil && len(start) == 0) || (end != nil && len(end) == 0) { - return nil, db.ErrKeyEmpty + return nil, types.ErrKeyEmpty } itr := tx.txn.NewIterator(tx.mgr.opts.ro) return newRocksDBIterator(itr, start, end, true), nil diff --git a/db/rocksdb/db_test.go b/db/rocksdb/db_test.go index c3ab2aa98f5e..0d67b55e6afc 100644 --- a/db/rocksdb/db_test.go +++ b/db/rocksdb/db_test.go @@ -1,5 +1,3 @@ -//go:build rocksdb - package rocksdb import ( @@ -9,11 +7,11 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/cosmos-sdk/db" "github.com/cosmos/cosmos-sdk/db/dbtest" + "github.com/cosmos/cosmos-sdk/db/types" ) -func load(t *testing.T, dir string) db.Connection { +func load(t *testing.T, dir string) types.Connection { d, err := NewDB(dir) require.NoError(t, err) return d diff --git a/db/rocksdb/iterator.go b/db/rocksdb/iterator.go index cf174019e1c1..231d7dfbedb5 100644 --- a/db/rocksdb/iterator.go +++ b/db/rocksdb/iterator.go @@ -1,11 +1,9 @@ -//go:build rocksdb - package rocksdb import ( "bytes" - "github.com/cosmos/cosmos-sdk/db" + "github.com/cosmos/cosmos-sdk/db/types" "github.com/cosmos/gorocksdb" ) @@ -18,7 +16,7 @@ type rocksDBIterator struct { primed bool } -var _ db.Iterator = (*rocksDBIterator)(nil) +var _ types.Iterator = (*rocksDBIterator)(nil) func newRocksDBIterator(source *gorocksdb.Iterator, start, end []byte, isReverse bool) *rocksDBIterator { if isReverse { diff --git a/db/adapter.go b/db/types/adapter.go similarity index 97% rename from db/adapter.go rename to db/types/adapter.go index ecfb400278de..6c72bdb69eb0 100644 --- a/db/adapter.go +++ b/db/types/adapter.go @@ -1,4 +1,4 @@ -package db +package types type readerRWAdapter struct{ Reader } diff --git a/db/creator.go b/db/types/creator.go similarity index 95% rename from db/creator.go rename to db/types/creator.go index 4d407a104673..7492465bebae 100644 --- a/db/creator.go +++ b/db/types/creator.go @@ -1,10 +1,8 @@ -package db +package types import ( "fmt" "strings" - - _ "github.com/cosmos/cosmos-sdk/db/internal/backends" ) type BackendType string diff --git a/db/types.go b/db/types/types.go similarity index 92% rename from db/types.go rename to db/types/types.go index 0e7f99f80f73..8d024c175ea2 100644 --- a/db/types.go +++ b/db/types/types.go @@ -1,4 +1,4 @@ -package db +package types import "errors" @@ -49,31 +49,31 @@ type Connection interface { // SaveNextVersion saves the current contents of the database and returns the next version ID, // which will be `Versions().Last()+1`. - // Returns an error if any open DBWriter transactions exist. + // Returns an error if any open Writer transactions exist. // TODO: rename to something more descriptive? SaveNextVersion() (uint64, error) // SaveVersion attempts to save database at a specific version ID, which must be greater than or // equal to what would be returned by `SaveNextVersion`. - // Returns an error if any open DBWriter transactions exist. + // Returns an error if any open Writer transactions exist. SaveVersion(uint64) error // DeleteVersion deletes a saved version. Returns ErrVersionDoesNotExist for invalid versions. DeleteVersion(uint64) error // Revert reverts the DB state to the last saved version; if none exist, this clears the DB. - // Returns an error if any open DBWriter transactions exist. + // Returns an error if any open Writer transactions exist. Revert() error // RevertTo reverts the DB state to the given version. Returns ErrVersionDoesNotExist for invalid versions. - // Returns an error if any open DBWriter transactions exist. + // Returns an error if any open Writer transactions exist. RevertTo(uint64) error // Close closes the database connection. Close() error } -// DBReader is a read-only transaction interface. It is safe for concurrent access. +// Reader is a read-only transaction interface. It is safe for concurrent access. // Callers must call Discard when done with the transaction. // // Keys cannot be nil or empty, while values cannot be nil. Keys and values should be considered @@ -108,7 +108,7 @@ type Reader interface { Discard() error } -// DBWriter is a write-only transaction interface. +// Writer is a write-only transaction interface. // It is safe for concurrent writes, following an optimistic (OCC) strategy, detecting any write // conflicts and returning an error on commit, rather than locking the DB. // Callers must call Commit or Discard when done with the transaction. @@ -130,7 +130,7 @@ type Writer interface { Discard() error } -// DBReadWriter is a transaction interface that allows both reading and writing. +// ReadWriter is a transaction interface that allows both reading and writing. type ReadWriter interface { Reader Writer @@ -145,7 +145,7 @@ type ReadWriter interface { // Note that the iterator is invalid on construction: Next() must be called to initialize it to its // starting position. // -// As with DBReader, keys and values should be considered read-only, and must be copied before they are +// As with Reader, keys and values should be considered read-only, and must be copied before they are // modified. // // Typical usage: diff --git a/db/version_manager.go b/db/types/version_manager.go similarity index 99% rename from db/version_manager.go rename to db/types/version_manager.go index 4088010298a5..01612ab8b998 100644 --- a/db/version_manager.go +++ b/db/types/version_manager.go @@ -1,4 +1,4 @@ -package db +package types import ( "fmt" diff --git a/db/version_manager_test.go b/db/types/version_manager_test.go similarity index 50% rename from db/version_manager_test.go rename to db/types/version_manager_test.go index 4bd7b64a2cd8..75799a665e56 100644 --- a/db/version_manager_test.go +++ b/db/types/version_manager_test.go @@ -1,13 +1,13 @@ -package db_test +package types_test import ( "testing" - "github.com/cosmos/cosmos-sdk/db" "github.com/cosmos/cosmos-sdk/db/dbtest" + "github.com/cosmos/cosmos-sdk/db/types" ) func TestVersionManager(t *testing.T) { - new := func(vs []uint64) db.VersionSet { return db.NewVersionManager(vs) } + new := func(vs []uint64) types.VersionSet { return types.NewVersionManager(vs) } dbtest.DoTestVersionSet(t, new) } diff --git a/go.mod b/go.mod index 449ab9b863a1..7eb4bcad8633 100644 --- a/go.mod +++ b/go.mod @@ -81,6 +81,7 @@ require ( github.com/cenkalti/backoff/v4 v4.1.1 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/cosmos/gorocksdb v1.2.0 // indirect github.com/cosmos/ledger-go v0.9.2 // indirect github.com/creachadair/taskgroup v0.3.2 // indirect github.com/danieljoos/wincred v1.0.2 // indirect diff --git a/go.sum b/go.sum index 44e081d0870c..bd5c03cd7913 100644 --- a/go.sum +++ b/go.sum @@ -257,6 +257,8 @@ github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= github.com/cosmos/iavl v0.18.0 h1:02ur4vnalMR2GuWCFNkuseUcl/BCVmg9tOeHOGiZOkE= github.com/cosmos/iavl v0.18.0/go.mod h1:L0VZHfq0tqMNJvXlslGExaaiZM7eSm+90Vh9QUbp6j4= +github.com/cosmos/gorocksdb v1.2.0 h1:d0l3jJG8M4hBouIZq0mDUHZ+zjOx044J3nGRskwTb4Y= +github.com/cosmos/gorocksdb v1.2.0/go.mod h1:aaKvKItm514hKfNJpUJXnnOWeBnk2GL4+Qw9NHizILw= github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76 h1:DdzS1m6o/pCqeZ8VOAit/gyATedRgjvkVI+UCrLpyuU= github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76/go.mod h1:0mkLWIoZuQ7uBoospo5Q9zIpqq6rYCPJDSUdeCJvPM8= github.com/cosmos/ledger-cosmos-go v0.11.1 h1:9JIYsGnXP613pb2vPjFeMMjBI5lEDsEaF6oYorTy6J4= From 04c832f4d2342253d665a405ffa90d9960330151 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Mon, 27 Jun 2022 18:14:34 +0800 Subject: [PATCH 67/78] clean up v2 store re-exports --- store/reexport.go | 45 +++++++++++++++-------------- store/v2alpha1/types.go | 64 ++++++++++++++++++----------------------- 2 files changed, 51 insertions(+), 58 deletions(-) diff --git a/store/reexport.go b/store/reexport.go index 5b101b4ac30f..6ad1e84a6a34 100644 --- a/store/reexport.go +++ b/store/reexport.go @@ -1,30 +1,31 @@ package store import ( - "github.com/cosmos/cosmos-sdk/store/types" + v1 "github.com/cosmos/cosmos-sdk/store/types" + v2 "github.com/cosmos/cosmos-sdk/store/v2alpha1" ) // Import cosmos-sdk/types/store.go for convenience. type ( - Store = types.Store - Committer = types.Committer - CommitStore = types.CommitStore - MultiStore = types.MultiStore - CacheMultiStore = types.CacheMultiStore - CommitMultiStore = types.CommitMultiStore - KVStore = types.KVStore - KVPair = types.KVPair - Iterator = types.Iterator - CacheKVStore = types.CacheKVStore - CommitKVStore = types.CommitKVStore - CacheWrapper = types.CacheWrapper - CacheWrap = types.CacheWrap - CommitID = types.CommitID - Key = types.StoreKey - Type = types.StoreType - Queryable = types.Queryable - TraceContext = types.TraceContext - Gas = types.Gas - GasMeter = types.GasMeter - GasConfig = types.GasConfig + Store = v1.Store + Committer = v1.Committer + CommitStore = v1.CommitStore + MultiStore = v2.MultiStore + CacheMultiStore = v2.CacheMultiStore + CommitMultiStore = v2.CommitMultiStore + KVStore = v1.KVStore + KVPair = v1.KVPair + Iterator = v1.Iterator + CacheKVStore = v1.CacheKVStore + CommitKVStore = v1.CommitKVStore + CacheWrapper = v1.CacheWrapper + CacheWrap = v1.CacheWrap + CommitID = v1.CommitID + Key = v1.StoreKey + Type = v1.StoreType + Queryable = v1.Queryable + TraceContext = v1.TraceContext + Gas = v1.Gas + GasMeter = v1.GasMeter + GasConfig = v1.GasConfig ) diff --git a/store/v2alpha1/types.go b/store/v2alpha1/types.go index e133e2bf7c71..932fb0c508e0 100644 --- a/store/v2alpha1/types.go +++ b/store/v2alpha1/types.go @@ -9,30 +9,26 @@ import ( // Re-export relevant original store types type ( - StoreKey = v1.StoreKey - StoreType = v1.StoreType - CommitID = v1.CommitID - StoreUpgrades = v1.StoreUpgrades - StoreRename = v1.StoreRename - Iterator = v1.Iterator - - TraceContext = v1.TraceContext - WriteListener = v1.WriteListener - - BasicKVStore = v1.BasicKVStore - KVStore = v1.KVStore - Committer = v1.Committer - CommitKVStore = v1.CommitKVStore - CacheKVStore = v1.CacheKVStore - Queryable = v1.Queryable - CacheWrap = v1.CacheWrap - + StoreKey = v1.StoreKey + StoreType = v1.StoreType + CommitID = v1.CommitID + StoreUpgrades = v1.StoreUpgrades + StoreRename = v1.StoreRename + Iterator = v1.Iterator + TraceContext = v1.TraceContext + WriteListener = v1.WriteListener + BasicKVStore = v1.BasicKVStore + KVStore = v1.KVStore + Committer = v1.Committer + CommitKVStore = v1.CommitKVStore + CacheKVStore = v1.CacheKVStore + Queryable = v1.Queryable + CacheWrap = v1.CacheWrap KVStoreKey = v1.KVStoreKey MemoryStoreKey = v1.MemoryStoreKey TransientStoreKey = v1.TransientStoreKey - - KVPair = v1.KVPair - StoreKVPair = v1.StoreKVPair + KVPair = v1.KVPair + StoreKVPair = v1.StoreKVPair ) // Re-export relevant constants, values and utility functions @@ -45,21 +41,17 @@ const ( ) var ( - NewKVStoreKey = v1.NewKVStoreKey - NewMemoryStoreKey = v1.NewMemoryStoreKey - NewTransientStoreKey = v1.NewTransientStoreKey - PrefixEndBytes = v1.PrefixEndBytes - KVStorePrefixIterator = v1.KVStorePrefixIterator - KVStoreReversePrefixIterator = v1.KVStoreReversePrefixIterator - - NewStoreKVPairWriteListener = v1.NewStoreKVPairWriteListener - - AssertValidKey = v1.AssertValidKey - AssertValidValue = v1.AssertValidValue - - CommitmentOpDecoder = v1.CommitmentOpDecoder - ProofOpFromMap = v1.ProofOpFromMap - + NewKVStoreKey = v1.NewKVStoreKey + NewMemoryStoreKey = v1.NewMemoryStoreKey + NewTransientStoreKey = v1.NewTransientStoreKey + PrefixEndBytes = v1.PrefixEndBytes + KVStorePrefixIterator = v1.KVStorePrefixIterator + KVStoreReversePrefixIterator = v1.KVStoreReversePrefixIterator + NewStoreKVPairWriteListener = v1.NewStoreKVPairWriteListener + AssertValidKey = v1.AssertValidKey + AssertValidValue = v1.AssertValidValue + CommitmentOpDecoder = v1.CommitmentOpDecoder + ProofOpFromMap = v1.ProofOpFromMap ProofOpSMTCommitment = v1.ProofOpSMTCommitment ProofOpSimpleMerkleCommitment = v1.ProofOpSimpleMerkleCommitment NewSmtCommitmentOp = v1.NewSmtCommitmentOp From 1232d5a1360a23ab36e83be8fedaa65501447969 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Tue, 28 Jun 2022 18:51:55 +0800 Subject: [PATCH 68/78] v2 store additions * remove iterator read lock; doesn't play with orm tests, so just rely on the stated contract: no store writes while iterator open * use v2 store in orm tests * clean up app boilerplate * update NewCommitMultiStore() * fix NewUncachedContext --- baseapp/options.go | 19 ++++++++ baseapp/test_helpers.go | 3 +- simapp/app.go | 38 +-------------- store/store.go | 15 +++--- store/v2alpha1/multi/params.go | 63 +++++++++++++++++++++++++ store/v2alpha1/multi/store.go | 71 ++++------------------------- store/v2alpha1/multi/sub_store.go | 22 +-------- types/query/pagination_test.go | 8 ---- x/group/internal/orm/testsupport.go | 54 ++++++++++++++-------- 9 files changed, 140 insertions(+), 153 deletions(-) create mode 100644 store/v2alpha1/multi/params.go diff --git a/baseapp/options.go b/baseapp/options.go index 7023794aefba..f6c3498fd978 100644 --- a/baseapp/options.go +++ b/baseapp/options.go @@ -84,6 +84,25 @@ func SetSubstores(keys ...storetypes.StoreKey) StoreOption { } } +func SetSubstoresFromMaps( + keys map[string]*storetypes.KVStoreKey, + tkeys map[string]*storetypes.TransientStoreKey, + memKeys map[string]*storetypes.MemoryStoreKey, +) StoreOption { + return func(params *multi.StoreParams, _ uint64) error { + if err := multi.RegisterSubstoresFromMap(params, keys); err != nil { + return err + } + if err := multi.RegisterSubstoresFromMap(params, tkeys); err != nil { + return err + } + if err := multi.RegisterSubstoresFromMap(params, memKeys); err != nil { + return err + } + return nil + } +} + // SetSnapshot sets the snapshot store. func SetSnapshot(snapshotStore *snapshots.Store, opts snapshottypes.SnapshotOptions) AppOption { return AppOptionOrdered{ diff --git a/baseapp/test_helpers.go b/baseapp/test_helpers.go index ebb2af388421..08180075ce6b 100644 --- a/baseapp/test_helpers.go +++ b/baseapp/test_helpers.go @@ -3,7 +3,6 @@ package baseapp import ( tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - "github.com/cosmos/cosmos-sdk/store/v2alpha1/multi" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) @@ -48,7 +47,7 @@ func (app *BaseApp) NewContext(isCheckTx bool, header tmproto.Header) sdk.Contex } func (app *BaseApp) NewUncachedContext(isCheckTx bool, header tmproto.Header) sdk.Context { - return sdk.NewContext(multi.CommitAsCacheStore(app.store), header, isCheckTx, app.logger) + return sdk.NewContext(app.store, header, isCheckTx, app.logger) } // NewContextAt creates a context using a (read-only) store at a given block height. diff --git a/simapp/app.go b/simapp/app.go index a8babfa915b5..583a9b74a5be 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -33,7 +33,6 @@ import ( simappparams "github.com/cosmos/cosmos-sdk/simapp/params" "github.com/cosmos/cosmos-sdk/store/streaming" storetypes "github.com/cosmos/cosmos-sdk/store/v2alpha1" - "github.com/cosmos/cosmos-sdk/store/v2alpha1/multi" "github.com/cosmos/cosmos-sdk/testutil/testdata_pulsar" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" @@ -252,40 +251,8 @@ func NewSimApp( ) // NOTE: The testingkey is just mounted for testing purposes. Actual applications should // not include this key. - app.memKeys = sdk.NewMemoryStoreKeys("testingkey") - - // Store loader which initialized substores - setNamespaces := func(config *multi.StoreParams, ver uint64) error { - for _, key := range keys { - typ, err := storetypes.StoreKeyToType(key) - if err != nil { - return err - } - if err = config.RegisterSubstore(key, typ); err != nil { - return err - } - } - for _, key := range memKeys { - typ, err := storetypes.StoreKeyToType(key) - if err != nil { - return err - } - if err = config.RegisterSubstore(key, typ); err != nil { - return err - } - } - for _, key := range tkeys { - typ, err := storetypes.StoreKeyToType(key) - if err != nil { - return err - } - if err = config.RegisterSubstore(key, typ); err != nil { - return err - } - } - return nil - } - baseAppOptions = append(baseAppOptions, baseapp.StoreOption(setNamespaces)) + app.memKeys = sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey, "testingkey") + baseAppOptions = append(baseAppOptions, baseapp.SetSubstoresFromMaps(keys, tkeys, memKeys)) // set the governance module account as the authority for conducting upgrades upgradeKeeper := upgradekeeper.NewKeeper(skipUpgradeHeights, app.keys[upgradetypes.StoreKey], app.appCodec, homePath, nil, authtypes.NewModuleAddress(govtypes.ModuleName).String()) @@ -427,7 +394,6 @@ func (app *SimApp) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci. // LoadHeight loads a particular height func (app *SimApp) LoadHeight(height int64) error { - // return app.LoadVersion(height) return errors.New("cannot load arbitrary height") } diff --git a/store/store.go b/store/store.go index 492bd4fee1dc..96c33d8784ba 100644 --- a/store/store.go +++ b/store/store.go @@ -1,16 +1,19 @@ package store import ( - "github.com/tendermint/tendermint/libs/log" - dbm "github.com/tendermint/tm-db" + dbm "github.com/cosmos/cosmos-sdk/db" "github.com/cosmos/cosmos-sdk/store/cache" - "github.com/cosmos/cosmos-sdk/store/rootmulti" - "github.com/cosmos/cosmos-sdk/store/types" + types "github.com/cosmos/cosmos-sdk/store/v2alpha1" + "github.com/cosmos/cosmos-sdk/store/v2alpha1/multi" ) -func NewCommitMultiStore(db dbm.DB) types.CommitMultiStore { - return rootmulti.NewStore(db, log.NewNopLogger()) +func NewCommitMultiStore(db dbm.DBConnection) types.CommitMultiStore { + store, err := multi.NewV1MultiStoreAsV2(db, multi.DefaultStoreParams()) + if err != nil { + panic(err) + } + return store } func NewCommitKVStoreCacheManager() types.MultiStorePersistentCache { diff --git a/store/v2alpha1/multi/params.go b/store/v2alpha1/multi/params.go new file mode 100644 index 000000000000..9d5fe0063237 --- /dev/null +++ b/store/v2alpha1/multi/params.go @@ -0,0 +1,63 @@ +package multi + +import ( + "fmt" + + pruningtypes "github.com/cosmos/cosmos-sdk/pruning/types" + types "github.com/cosmos/cosmos-sdk/store/v2alpha1" +) + +// DefaultStoreParams returns a MultiStore config with an empty schema, a single backing DB, +// pruning with PruneDefault, no listeners and no tracer. +func DefaultStoreParams() StoreParams { + return StoreParams{ + Pruning: pruningtypes.NewPruningOptions(pruningtypes.PruningDefault), + SchemaBuilder: newSchemaBuilder(), + storeKeys: storeKeys{}, + traceListenMixin: newTraceListenMixin(), + } +} + +func (par *StoreParams) RegisterSubstore(skey types.StoreKey, typ types.StoreType) error { + if !validSubStoreType(typ) { + return fmt.Errorf("StoreType not supported: %v", typ) + } + var ok bool + switch typ { + case types.StoreTypePersistent: + _, ok = skey.(*types.KVStoreKey) + case types.StoreTypeMemory: + _, ok = skey.(*types.MemoryStoreKey) + case types.StoreTypeTransient: + _, ok = skey.(*types.TransientStoreKey) + } + if !ok { + return fmt.Errorf("invalid StoreKey for %v: %T", typ, skey) + } + if err := par.registerName(skey.Name(), typ); err != nil { + return err + } + par.storeKeys[skey.Name()] = skey + return nil +} + +func (par *StoreParams) storeKey(key string) (types.StoreKey, error) { + skey, ok := par.storeKeys[key] + if !ok { + return nil, fmt.Errorf("StoreKey instance not mapped: %s", key) + } + return skey, nil +} + +func RegisterSubstoresFromMap[T types.StoreKey](par *StoreParams, keys map[string]T) error { + for _, key := range keys { + typ, err := types.StoreKeyToType(key) + if err != nil { + return err + } + if err = par.RegisterSubstore(key, typ); err != nil { + return err + } + } + return nil +} diff --git a/store/v2alpha1/multi/store.go b/store/v2alpha1/multi/store.go index e0902012a0cf..166218b04a9b 100644 --- a/store/v2alpha1/multi/store.go +++ b/store/v2alpha1/multi/store.go @@ -141,48 +141,6 @@ func newSchemaBuilder() SchemaBuilder { return SchemaBuilder{StoreSchema: StoreSchema{}} } -// DefaultStoreParams returns a MultiStore config with an empty schema, a single backing DB, -// pruning with PruneDefault, no listeners and no tracer. -func DefaultStoreParams() StoreParams { - return StoreParams{ - Pruning: pruningtypes.NewPruningOptions(pruningtypes.PruningDefault), - SchemaBuilder: newSchemaBuilder(), - storeKeys: storeKeys{}, - traceListenMixin: newTraceListenMixin(), - } -} - -func (par *StoreParams) RegisterSubstore(skey types.StoreKey, typ types.StoreType) error { - if !validSubStoreType(typ) { - return fmt.Errorf("StoreType not supported: %v", typ) - } - var ok bool - switch typ { - case types.StoreTypePersistent: - _, ok = skey.(*types.KVStoreKey) - case types.StoreTypeMemory: - _, ok = skey.(*types.MemoryStoreKey) - case types.StoreTypeTransient: - _, ok = skey.(*types.TransientStoreKey) - } - if !ok { - return fmt.Errorf("invalid StoreKey for %v: %T", typ, skey) - } - if err := par.registerName(skey.Name(), typ); err != nil { - return err - } - par.storeKeys[skey.Name()] = skey - return nil -} - -func (par *StoreParams) storeKey(key string) (types.StoreKey, error) { - skey, ok := par.storeKeys[key] - if !ok { - return nil, fmt.Errorf("StoreKey instance not mapped: %s", key) - } - return skey, nil -} - // Returns true for valid store types for a MultiStore schema func validSubStoreType(sst types.StoreType) bool { switch sst { @@ -279,8 +237,7 @@ func NewStore(db dbm.DBConnection, opts StoreParams) (ret *Store, err error) { // To abide by atomicity constraints, revert the DB to the last saved version, in case it contains // committed data in the "working" version. // This should only happen if Store.Commit previously failed. - err = db.Revert() - if err != nil { + if err = db.Revert(); err != nil { return } stateTxn := db.ReadWriter() @@ -292,8 +249,7 @@ func NewStore(db dbm.DBConnection, opts StoreParams) (ret *Store, err error) { stateCommitmentTxn := stateTxn if opts.StateCommitmentDB != nil { var scVersions dbm.VersionSet - scVersions, err = opts.StateCommitmentDB.Versions() - if err != nil { + if scVersions, err = opts.StateCommitmentDB.Versions(); err != nil { return } // Version sets of each DB must match @@ -301,8 +257,7 @@ func NewStore(db dbm.DBConnection, opts StoreParams) (ret *Store, err error) { err = fmt.Errorf("different version history between Storage and StateCommitment DB ") return } - err = opts.StateCommitmentDB.Revert() - if err != nil { + if err = opts.StateCommitmentDB.Revert(); err != nil { return } stateCommitmentTxn = opts.StateCommitmentDB.ReadWriter() @@ -333,8 +288,7 @@ func NewStore(db dbm.DBConnection, opts StoreParams) (ret *Store, err error) { writeSchema := func(sch StoreSchema) { schemaWriter := prefixdb.NewWriter(ret.stateTxn, schemaPrefix) var it dbm.Iterator - it, err = schemaView.Iterator(nil, nil) - if err != nil { + if it, err = schemaView.Iterator(nil, nil); err != nil { return } for it.Next() { @@ -343,12 +297,10 @@ func NewStore(db dbm.DBConnection, opts StoreParams) (ret *Store, err error) { return } } - err = it.Close() - if err != nil { + if err = it.Close(); err != nil { return } - err = schemaView.Discard() - if err != nil { + if err = schemaView.Discard(); err != nil { return } // NB. the migrated contents and schema are not committed until the next store.Commit @@ -642,9 +594,8 @@ func (s *Store) commit(target uint64) (id *types.CommitID, err error) { } // Update substore Merkle roots for key, storeHash := range storeHashes { - pfx := substorePrefix(key) - stateW := prefixdb.NewReadWriter(s.stateTxn, pfx) - if err = stateW.Set(substoreMerkleRootKey, storeHash); err != nil { + w := prefixdb.NewPrefixReadWriter(s.stateTxn, substorePrefix(key)) + if err = w.Set(substoreMerkleRootKey, storeHash); err != nil { return } } @@ -660,8 +611,7 @@ func (s *Store) commit(target uint64) (id *types.CommitID, err error) { err = util.CombineErrors(err, s.stateDB.Revert(), "stateDB.Revert also failed") } }() - err = s.stateDB.SaveVersion(target) - if err != nil { + if err = s.stateDB.SaveVersion(target); err != nil { return } @@ -691,8 +641,7 @@ func (s *Store) commit(target uint64) (id *types.CommitID, err error) { } }() - err = s.StateCommitmentDB.SaveVersion(target) - if err != nil { + if err = s.StateCommitmentDB.SaveVersion(target); err != nil { return } } diff --git a/store/v2alpha1/multi/sub_store.go b/store/v2alpha1/multi/sub_store.go index 6aaf030260c7..97b74f1186ba 100644 --- a/store/v2alpha1/multi/sub_store.go +++ b/store/v2alpha1/multi/sub_store.go @@ -2,9 +2,7 @@ package multi import ( "io" - "sync" - dbm "github.com/cosmos/cosmos-sdk/db" dbutil "github.com/cosmos/cosmos-sdk/internal/db" "github.com/cosmos/cosmos-sdk/store/cachekv" "github.com/cosmos/cosmos-sdk/store/listenkv" @@ -57,29 +55,13 @@ func (s *substore) Delete(key []byte) { _ = s.dataBucket.Delete(key) } -type contentsIterator struct { - types.Iterator - locker sync.Locker -} - -func (s *substore) newSubstoreIterator(source dbm.Iterator) *contentsIterator { - locker := s.root.mtx.RLocker() - locker.Lock() - return &contentsIterator{dbutil.ToStoreIterator(source), locker} -} - -func (it *contentsIterator) Close() error { - defer it.locker.Unlock() - return it.Iterator.Close() -} - // Iterator implements KVStore. func (s *substore) Iterator(start, end []byte) types.Iterator { iter, err := s.dataBucket.Iterator(start, end) if err != nil { panic(err) } - return s.newSubstoreIterator(iter) + return dbutil.DBToStoreIterator(iter) } // ReverseIterator implements KVStore. @@ -88,7 +70,7 @@ func (s *substore) ReverseIterator(start, end []byte) types.Iterator { if err != nil { panic(err) } - return s.newSubstoreIterator(iter) + return dbutil.DBToStoreIterator(iter) } // GetStoreType implements Store. diff --git a/types/query/pagination_test.go b/types/query/pagination_test.go index 784ebd5a5c01..2283ead4afca 100644 --- a/types/query/pagination_test.go +++ b/types/query/pagination_test.go @@ -7,13 +7,11 @@ import ( "github.com/stretchr/testify/suite" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" "github.com/cosmos/cosmos-sdk/simapp" - "github.com/cosmos/cosmos-sdk/store" "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/address" @@ -339,11 +337,5 @@ func setupTest(t *testing.T) (*simapp.SimApp, sdk.Context, codec.Codec) { app := simapp.Setup(t, false) ctx := app.BaseApp.NewContext(false, tmproto.Header{Height: 1}) appCodec := app.AppCodec() - - db := dbm.NewMemDB() - ms := store.NewCommitMultiStore(db) - - ms.LoadLatestVersion() - return app, ctx, appCodec } diff --git a/x/group/internal/orm/testsupport.go b/x/group/internal/orm/testsupport.go index 1b4a6847898a..e041c12c1d35 100644 --- a/x/group/internal/orm/testsupport.go +++ b/x/group/internal/orm/testsupport.go @@ -3,50 +3,64 @@ package orm import ( "fmt" + "github.com/cosmos/cosmos-sdk/db/memdb" "github.com/cosmos/cosmos-sdk/store" "github.com/cosmos/cosmos-sdk/store/gaskv" storetypes "github.com/cosmos/cosmos-sdk/store/types" + "github.com/cosmos/cosmos-sdk/store/v2alpha1/multi" sdk "github.com/cosmos/cosmos-sdk/types" - dbm "github.com/tendermint/tm-db" ) type MockContext struct { - db *dbm.MemDB - store storetypes.CommitMultiStore + db *memdb.MemDB + store store.CommitMultiStore + params multi.StoreParams } func NewMockContext() *MockContext { - db := dbm.NewMemDB() + db := memdb.NewDB() + params := multi.DefaultStoreParams() + cms, err := multi.NewStore(db, params) + if err != nil { + panic(err) + } return &MockContext{ - db: dbm.NewMemDB(), - store: store.NewCommitMultiStore(db), + db: db, + store: cms, + params: params, } } -func (m MockContext) KVStore(key storetypes.StoreKey) sdk.KVStore { - if s := m.store.GetCommitKVStore(key); s != nil { - return s +func (m *MockContext) KVStore(key store.Key) sdk.KVStore { + if m.store.HasKVStore(key) { + return m.store.GetKVStore(key) + } + err := m.store.Close() + if err != nil { + panic(err) + } + if err = m.params.RegisterSubstore(key, storetypes.StoreTypePersistent); err != nil { + panic(err) } - m.store.MountStoreWithDB(key, storetypes.StoreTypeIAVL, m.db) - if err := m.store.LoadLatestVersion(); err != nil { + if m.store, err = multi.NewStore(m.db, m.params); err != nil { panic(err) } - return m.store.GetCommitKVStore(key) + return m.store.GetKVStore(key) } type debuggingGasMeter struct { - g storetypes.GasMeter + g store.GasMeter } -func (d debuggingGasMeter) GasConsumed() storetypes.Gas { +func (d debuggingGasMeter) GasConsumed() store.Gas { return d.g.GasConsumed() } -func (d debuggingGasMeter) GasRemaining() storetypes.Gas { +func (d debuggingGasMeter) GasRemaining() store.Gas { return d.g.GasRemaining() } -func (d debuggingGasMeter) GasConsumedToLimit() storetypes.Gas { +func (d debuggingGasMeter) GasConsumedToLimit() store.Gas { return d.g.GasConsumedToLimit() } @@ -54,11 +68,11 @@ func (d debuggingGasMeter) RefundGas(amount uint64, descriptor string) { d.g.RefundGas(amount, descriptor) } -func (d debuggingGasMeter) Limit() storetypes.Gas { +func (d debuggingGasMeter) Limit() store.Gas { return d.g.Limit() } -func (d debuggingGasMeter) ConsumeGas(amount storetypes.Gas, descriptor string) { +func (d debuggingGasMeter) ConsumeGas(amount store.Gas, descriptor string) { fmt.Printf("++ Consuming gas: %q :%d\n", descriptor, amount) d.g.ConsumeGas(amount, descriptor) } @@ -89,11 +103,11 @@ func (g GasCountingMockContext) KVStore(store sdk.KVStore) sdk.KVStore { return gaskv.NewStore(store, g.GasMeter, storetypes.KVGasConfig()) } -func (g GasCountingMockContext) GasConsumed() storetypes.Gas { +func (g GasCountingMockContext) GasConsumed() store.Gas { return g.GasMeter.GasConsumed() } -func (g GasCountingMockContext) GasRemaining() storetypes.Gas { +func (g GasCountingMockContext) GasRemaining() store.Gas { return g.GasMeter.GasRemaining() } From f0077046a04ab249e71e2f8758c00e1301ae1e96 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Fri, 24 Jun 2022 23:33:50 +0800 Subject: [PATCH 69/78] add multi.Store.HasKVStore() --- server/mock/store.go | 5 +++++ store/v2alpha1/multi/cache_store.go | 4 ++++ store/v2alpha1/multi/store.go | 6 ++++++ store/v2alpha1/multi/store_test.go | 4 ++++ store/v2alpha1/multi/v1asv2.go | 8 ++++++++ store/v2alpha1/multi/view_store.go | 5 +++++ store/v2alpha1/types.go | 2 ++ 7 files changed, 34 insertions(+) diff --git a/server/mock/store.go b/server/mock/store.go index 8d76f35bca1d..13f3c66ee98a 100644 --- a/server/mock/store.go +++ b/server/mock/store.go @@ -77,6 +77,11 @@ func (ms multiStore) GetKVStore(key storetypes.StoreKey) sdk.KVStore { return ms.kv[key] } +func (ms multiStore) HasKVStore(key storetypes.StoreKey) bool { + _, has := ms.kv[key] + return has +} + func (ms multiStore) SetInitialVersion(version uint64) error { panic("not implemented") } diff --git a/store/v2alpha1/multi/cache_store.go b/store/v2alpha1/multi/cache_store.go index fd1ff78804dd..390deca28361 100644 --- a/store/v2alpha1/multi/cache_store.go +++ b/store/v2alpha1/multi/cache_store.go @@ -33,6 +33,10 @@ func (cs *cacheStore) GetKVStore(skey types.StoreKey) types.KVStore { return cs.wrapTraceListen(sub, skey) } +func (cs *cacheStore) HasKVStore(skey types.StoreKey) bool { + return cs.source.HasKVStore(skey) +} + // Write implements CacheMultiStore. func (cs *cacheStore) Write() { for _, sub := range cs.substores { diff --git a/store/v2alpha1/multi/store.go b/store/v2alpha1/multi/store.go index 166218b04a9b..bd17009cb986 100644 --- a/store/v2alpha1/multi/store.go +++ b/store/v2alpha1/multi/store.go @@ -459,6 +459,12 @@ func (s *Store) GetKVStore(skey types.StoreKey) types.KVStore { return s.wrapTraceListen(ret, skey) } +// HasKVStore implements MultiStore. +func (rs *Store) HasKVStore(skey types.StoreKey) bool { + _, has := rs.schema[skey] + return has +} + // Gets a persistent substore. This reads, but does not update the substore cache. // Use it in cases where we need to access a store internally (e.g. read/write Merkle keys, queries) func (s *Store) getSubstore(key string) (*substore, error) { diff --git a/store/v2alpha1/multi/store_test.go b/store/v2alpha1/multi/store_test.go index 666befd970ee..662d5872cac3 100644 --- a/store/v2alpha1/multi/store_test.go +++ b/store/v2alpha1/multi/store_test.go @@ -99,6 +99,10 @@ func doTestMultiStoreBasic(t *testing.T, ctor storeConstructor) { store, err := ctor(memdb.NewDB(), opts) require.NoError(t, err) + require.True(t, store.HasKVStore(skey_1)) + require.False(t, store.HasKVStore(skey_2)) + require.Panics(t, func() { store.GetKVStore(skey_2) }) + store_1 := store.GetKVStore(skey_1) require.NotNil(t, store_1) store_1.Set([]byte{0}, []byte{0}) diff --git a/store/v2alpha1/multi/v1asv2.go b/store/v2alpha1/multi/v1asv2.go index 2ce3ee834c2d..ee25df37c4a9 100644 --- a/store/v2alpha1/multi/v1asv2.go +++ b/store/v2alpha1/multi/v1asv2.go @@ -97,6 +97,10 @@ func (s *store1as2) GetVersion(ver int64) (v2.MultiStore, error) { return viewStore1as2{cacheStore1as2{ret}}, err } +func (s *store1as2) HasKVStore(skey v2.StoreKey) bool { + return s.Store.GetCommitKVStore(skey) != nil +} + // CommitMultiStore func (s *store1as2) Close() error { @@ -145,6 +149,10 @@ func (s cacheStore1as2) SetTracingContext(tc v2.TraceContext) { s.CacheMultiStore.SetTracingContext(tc) } +func (s cacheStore1as2) HasKVStore(skey v2.StoreKey) bool { + return s.CacheMultiStore.GetKVStore(skey) != nil +} + func (s viewStore1as2) GetKVStore(skey v2.StoreKey) v2.KVStore { sub := s.CacheMultiStore.GetKVStore(skey) return readonlyKVStore{sub} diff --git a/store/v2alpha1/multi/view_store.go b/store/v2alpha1/multi/view_store.go index 13a0512fc49c..13295a1a3577 100644 --- a/store/v2alpha1/multi/view_store.go +++ b/store/v2alpha1/multi/view_store.go @@ -45,6 +45,11 @@ func (s *viewStore) GetKVStore(skey types.StoreKey) types.KVStore { return ret } +func (vs *viewStore) HasKVStore(skey types.StoreKey) bool { + _, has := vs.schema[skey.Name()] + return has +} + // Reads but does not update substore cache func (s *viewStore) getSubstore(key string) (*viewSubstore, error) { if cached, has := s.substoreCache[key]; has { diff --git a/store/v2alpha1/types.go b/store/v2alpha1/types.go index 932fb0c508e0..c0a67bb762e7 100644 --- a/store/v2alpha1/types.go +++ b/store/v2alpha1/types.go @@ -59,6 +59,8 @@ var ( // MultiStore defines a minimal interface for accessing root state. type MultiStore interface { + // Returns true iff the store key is present in the schema. + HasKVStore(StoreKey) bool // Returns a KVStore which has access only to the namespace of the StoreKey. // Panics if the key is not found in the schema. GetKVStore(StoreKey) KVStore From bc67cfe8a861250752f207384b4efa216f016c0d Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Wed, 29 Jun 2022 13:07:15 +0800 Subject: [PATCH 70/78] bank keeper: validate account store address --- x/bank/keeper/view.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/x/bank/keeper/view.go b/x/bank/keeper/view.go index 5fc3446c2da5..8b41efc7052d 100644 --- a/x/bank/keeper/view.go +++ b/x/bank/keeper/view.go @@ -233,6 +233,9 @@ func (k BaseViewKeeper) ValidateBalance(ctx sdk.Context, addr sdk.AccAddress) er // getAccountStore gets the account store of the given address. func (k BaseViewKeeper) getAccountStore(ctx sdk.Context, addr sdk.AccAddress) prefix.Store { + if len(addr) == 0 { + panic("address cannot be empty") + } store := ctx.KVStore(k.storeKey) return prefix.NewStore(store, types.CreateAccountBalancesPrefix(addr)) } From e409ca3129f4531db8dfc0f1c7277288f3971924 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Wed, 29 Jun 2022 18:31:27 +0800 Subject: [PATCH 71/78] fix typos, docs, rm redundant --- baseapp/util_test.go | 9 --------- simapp/sim_test.go | 2 +- simapp/utils.go | 2 +- x/slashing/keeper/signing_info.go | 2 +- 4 files changed, 3 insertions(+), 12 deletions(-) diff --git a/baseapp/util_test.go b/baseapp/util_test.go index 0f77d26ebd08..0c4f14f02acc 100644 --- a/baseapp/util_test.go +++ b/baseapp/util_test.go @@ -1,7 +1,6 @@ package baseapp import ( - "github.com/cosmos/cosmos-sdk/snapshots" "github.com/cosmos/cosmos-sdk/types" ) @@ -31,14 +30,6 @@ func (app *BaseApp) Store() types.CommitMultiStore { return app.store } -// GetSnapshotManager() is an exported method to be able to access baseapp's snapshot -// manager in tests. -// -// This method is only accessible in baseapp tests. -func (app *BaseApp) GetSnapshotManager() *snapshots.Manager { - return app.snapshotManager -} - // GetMaximumBlockGas return maximum blocks gas. // // This method is only accessible in baseapp tests. diff --git a/simapp/sim_test.go b/simapp/sim_test.go index 502b985cc0eb..c04e7e797743 100644 --- a/simapp/sim_test.go +++ b/simapp/sim_test.go @@ -47,7 +47,7 @@ type StoreKeysPrefixes struct { } // fauxMerkleModeOpt returns a BaseApp option to use a dbStoreAdapter instead of -// an IAVLStore for faster simulation speed. +// a Merkle tree store for faster simulation speed. var fauxMerkleModeOpt = baseapp.AppOptionFunc(func(bapp *baseapp.BaseApp) { bapp.SetFauxMerkleMode() }) diff --git a/simapp/utils.go b/simapp/utils.go index b20dba86318b..b0391b096ee8 100644 --- a/simapp/utils.go +++ b/simapp/utils.go @@ -19,7 +19,7 @@ import ( // SetupSimulation creates the config, db (levelDB), temporary directory and logger for // the simulation tests. If `FlagEnabledValue` is false it skips the current test. -// Returns error on an invalid db intantiation or temp dir creation. +// Returns error on an invalid db instantiation or temp dir creation. func SetupSimulation(dirPrefix, dbName string) (simtypes.Config, dbm.DBConnection, string, log.Logger, bool, error) { if !FlagEnabledValue { return simtypes.Config{}, nil, "", nil, true, nil diff --git a/x/slashing/keeper/signing_info.go b/x/slashing/keeper/signing_info.go index d65b773ebc66..d986db27c45d 100644 --- a/x/slashing/keeper/signing_info.go +++ b/x/slashing/keeper/signing_info.go @@ -9,7 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/slashing/types" ) -// GetValidatorSigningInfo retruns the ValidatorSigningInfo for a specific validator +// GetValidatorSigningInfo returns the ValidatorSigningInfo for a specific validator // ConsAddress func (k Keeper) GetValidatorSigningInfo(ctx sdk.Context, address sdk.ConsAddress) (info types.ValidatorSigningInfo, found bool) { store := ctx.KVStore(k.storeKey) From 4823d1ac846d2ed7c3774be533233fc682fbcfe4 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Wed, 20 Jul 2022 14:07:33 -0500 Subject: [PATCH 72/78] feat: list snapshots query (#246) (#252) (cherry picked from commit f69c198375023861132ed5b3a75374940000f46a) Co-authored-by: Roman --- baseapp/abci.go | 17 +++++++++++++++++ baseapp/baseapp_test.go | 27 ++++++++++++++++++++++----- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/baseapp/abci.go b/baseapp/abci.go index 29680cb86b31..73358397f257 100644 --- a/baseapp/abci.go +++ b/baseapp/abci.go @@ -2,6 +2,7 @@ package baseapp import ( "crypto/sha256" + "encoding/json" "errors" "fmt" "os" @@ -742,6 +743,22 @@ func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) abci.Res Value: []byte(app.version), } + case "snapshots": + var responseValue []byte + + response := app.ListSnapshots(abci.RequestListSnapshots{}) + + responseValue, err := json.Marshal(response) + if err != nil { + sdkerrors.QueryResult(sdkerrors.Wrap(err, fmt.Sprintf("failed to marshal list snapshots response %v", response))) + } + + return abci.ResponseQuery{ + Codespace: sdkerrors.RootCodespace, + Height: req.Height, + Value: responseValue, + } + default: return sdkerrors.QueryResult(sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown query: %s", path), app.trace) } diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 35cd39f03b4c..7616c5b52cec 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -1608,17 +1608,34 @@ func TestListSnapshots(t *testing.T) { app, err := setupBaseAppWithSnapshots(t, setupConfig) require.NoError(t, err) + expected := abci.ResponseListSnapshots{Snapshots: []*abci.Snapshot{ + {Height: 4, Format: 1, Chunks: 2}, + {Height: 2, Format: 1, Chunks: 1}, + }} + resp := app.ListSnapshots(abci.RequestListSnapshots{}) - for _, s := range resp.Snapshots { + queryResponse := app.Query(abci.RequestQuery{ + Path: "/app/snapshots", + }) + + queryListSnapshotsResp := abci.ResponseListSnapshots{} + err = json.Unmarshal(queryResponse.Value, &queryListSnapshotsResp) + require.NoError(t, err) + + for i, s := range resp.Snapshots { + querySnapshot := queryListSnapshotsResp.Snapshots[i] + // we check that the query snapshot and function snapshot are equal + // Then we check that the hash and metadata are not empty. We atm + // do not have a good way to generate the expected value for these. + assert.Equal(t, *s, *querySnapshot) assert.NotEmpty(t, s.Hash) assert.NotEmpty(t, s.Metadata) + // Set hash and metadata to nil, so we can check the other snapshot + // fields against expected s.Hash = nil s.Metadata = nil } - assert.Equal(t, abci.ResponseListSnapshots{Snapshots: []*abci.Snapshot{ - {Height: 4, Format: snapshottypes.CurrentFormat, Chunks: 2}, - {Height: 2, Format: snapshottypes.CurrentFormat, Chunks: 1}, - }}, resp) + assert.Equal(t, expected, resp) } func TestSnapshotWithPruning(t *testing.T) { From 69fd2f7881c7abe9fcac02aba480c81cf416121e Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Wed, 22 Jun 2022 21:03:28 +0800 Subject: [PATCH 73/78] Restore baseapp pruning & snapshot tests --- baseapp/abci.go | 2 +- baseapp/baseapp_test.go | 163 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 162 insertions(+), 3 deletions(-) diff --git a/baseapp/abci.go b/baseapp/abci.go index 73358397f257..fbadd34c673c 100644 --- a/baseapp/abci.go +++ b/baseapp/abci.go @@ -750,7 +750,7 @@ func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) abci.Res responseValue, err := json.Marshal(response) if err != nil { - sdkerrors.QueryResult(sdkerrors.Wrap(err, fmt.Sprintf("failed to marshal list snapshots response %v", response))) + sdkerrors.QueryResult(sdkerrors.Wrap(err, fmt.Sprintf("failed to marshal list snapshots response %v", response)), false) } return abci.ResponseQuery{ diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 7616c5b52cec..012f7373730a 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -1609,8 +1609,8 @@ func TestListSnapshots(t *testing.T) { require.NoError(t, err) expected := abci.ResponseListSnapshots{Snapshots: []*abci.Snapshot{ - {Height: 4, Format: 1, Chunks: 2}, - {Height: 2, Format: 1, Chunks: 1}, + {Height: 4, Format: snapshottypes.CurrentFormat, Chunks: 2}, + {Height: 2, Format: snapshottypes.CurrentFormat, Chunks: 1}, }} resp := app.ListSnapshots(abci.RequestListSnapshots{}) @@ -2034,3 +2034,162 @@ func TestBaseApp_EndBlock(t *testing.T) { require.Equal(t, int64(100), res.GetValidatorUpdates()[0].Power) require.Equal(t, cp.Block.MaxGas, res.ConsensusParamUpdates.Block.MaxGas) } + +func TestBaseApp_Init(t *testing.T) { + db := memdb.NewDB() + name := t.Name() + logger := defaultLogger() + + snapshotStore, err := snapshots.NewStore(memdb.NewDB(), testutil.GetTempDir(t)) + require.NoError(t, err) + + testCases := map[string]struct { + bapp *BaseApp + expectedPruning pruningtypes.PruningOptions + expectedSnapshot snapshottypes.SnapshotOptions + expectedErr error + }{ + "snapshot but no pruning": { + NewBaseApp(name, logger, db, nil, + SetSnapshot(snapshotStore, snapshottypes.NewSnapshotOptions(1500, 2)), + ), + pruningtypes.NewPruningOptions(pruningtypes.PruningNothing), + snapshottypes.NewSnapshotOptions(1500, 2), + // if no pruning is set, the default is PruneNothing + nil, + }, + "pruning everything only": { + NewBaseApp(name, logger, db, nil, + SetPruning(pruningtypes.NewPruningOptions(pruningtypes.PruningEverything)), + ), + pruningtypes.NewPruningOptions(pruningtypes.PruningEverything), + snapshottypes.NewSnapshotOptions(snapshottypes.SnapshotIntervalOff, 0), + nil, + }, + "pruning nothing only": { + NewBaseApp(name, logger, db, nil, + SetPruning(pruningtypes.NewPruningOptions(pruningtypes.PruningNothing)), + ), + pruningtypes.NewPruningOptions(pruningtypes.PruningNothing), + snapshottypes.NewSnapshotOptions(snapshottypes.SnapshotIntervalOff, 0), + nil, + }, + "pruning default only": { + NewBaseApp(name, logger, db, nil, + SetPruning(pruningtypes.NewPruningOptions(pruningtypes.PruningDefault)), + ), + pruningtypes.NewPruningOptions(pruningtypes.PruningDefault), + snapshottypes.NewSnapshotOptions(snapshottypes.SnapshotIntervalOff, 0), + nil, + }, + "pruning custom only": { + NewBaseApp(name, logger, db, nil, + SetPruning(pruningtypes.NewCustomPruningOptions(10, 10)), + ), + pruningtypes.NewCustomPruningOptions(10, 10), + snapshottypes.NewSnapshotOptions(snapshottypes.SnapshotIntervalOff, 0), + nil, + }, + "pruning everything and snapshots": { + NewBaseApp(name, logger, db, nil, + SetPruning(pruningtypes.NewPruningOptions(pruningtypes.PruningEverything)), + SetSnapshot(snapshotStore, snapshottypes.NewSnapshotOptions(1500, 2)), + ), + pruningtypes.NewPruningOptions(pruningtypes.PruningEverything), + snapshottypes.NewSnapshotOptions(1500, 2), + nil, + }, + "pruning nothing and snapshots": { + NewBaseApp(name, logger, db, nil, + SetPruning(pruningtypes.NewPruningOptions(pruningtypes.PruningNothing)), + SetSnapshot(snapshotStore, snapshottypes.NewSnapshotOptions(1500, 2)), + ), + pruningtypes.NewPruningOptions(pruningtypes.PruningNothing), + snapshottypes.NewSnapshotOptions(1500, 2), + nil, + }, + "pruning default and snapshots": { + NewBaseApp(name, logger, db, nil, + SetPruning(pruningtypes.NewPruningOptions(pruningtypes.PruningDefault)), + SetSnapshot(snapshotStore, snapshottypes.NewSnapshotOptions(1500, 2)), + ), + pruningtypes.NewPruningOptions(pruningtypes.PruningDefault), + snapshottypes.NewSnapshotOptions(1500, 2), + nil, + }, + "pruning custom and snapshots": { + NewBaseApp(name, logger, db, nil, + SetPruning(pruningtypes.NewCustomPruningOptions(10, 10)), + SetSnapshot(snapshotStore, snapshottypes.NewSnapshotOptions(1500, 2)), + ), + pruningtypes.NewCustomPruningOptions(10, 10), + snapshottypes.NewSnapshotOptions(1500, 2), + nil, + }, + "error custom pruning 0 interval": { + NewBaseApp(name, logger, db, nil, + SetPruning(pruningtypes.NewCustomPruningOptions(10, 0)), + SetSnapshot(snapshotStore, snapshottypes.NewSnapshotOptions(1500, 2)), + ), + pruningtypes.NewCustomPruningOptions(10, 0), + snapshottypes.NewSnapshotOptions(1500, 2), + pruningtypes.ErrPruningIntervalZero, + }, + "error custom pruning too small interval": { + NewBaseApp(name, logger, db, nil, + SetPruning(pruningtypes.NewCustomPruningOptions(10, 9)), + SetSnapshot(snapshotStore, snapshottypes.NewSnapshotOptions(1500, 2)), + ), + pruningtypes.NewCustomPruningOptions(10, 9), + snapshottypes.NewSnapshotOptions(1500, 2), + pruningtypes.ErrPruningIntervalTooSmall, + }, + "error custom pruning too small keep recent": { + NewBaseApp(name, logger, db, nil, + SetPruning(pruningtypes.NewCustomPruningOptions(9, 10)), + SetSnapshot(snapshotStore, snapshottypes.NewSnapshotOptions(1500, 2)), + ), + pruningtypes.NewCustomPruningOptions(9, 10), + snapshottypes.NewSnapshotOptions(1500, 2), + pruningtypes.ErrPruningKeepRecentTooSmall, + }, + "snapshot zero interval - manager not set": { + NewBaseApp(name, logger, db, nil, + SetPruning(pruningtypes.NewCustomPruningOptions(10, 10)), + SetSnapshot(snapshotStore, snapshottypes.NewSnapshotOptions(0, 2)), + ), + pruningtypes.NewCustomPruningOptions(10, 10), + snapshottypes.NewSnapshotOptions(snapshottypes.SnapshotIntervalOff, 0), + nil, + }, + "snapshot zero keep recent - allowed": { + NewBaseApp(name, logger, db, nil, + SetPruning(pruningtypes.NewCustomPruningOptions(10, 10)), + SetSnapshot(snapshotStore, snapshottypes.NewSnapshotOptions(1500, 0)), + ), + pruningtypes.NewCustomPruningOptions(10, 10), + snapshottypes.NewSnapshotOptions(1500, 0), // 0 snapshot-keep-recent means keep all + nil, + }, + } + + for _, tc := range testCases { + // Init and validate + require.Equal(t, tc.expectedErr, tc.bapp.Init()) + if tc.expectedErr != nil { + continue + } + + // Check that settings were set correctly + actualPruning := tc.bapp.store.GetPruning() + require.Equal(t, tc.expectedPruning, actualPruning) + + if tc.expectedSnapshot.Interval == snapshottypes.SnapshotIntervalOff { + require.Nil(t, tc.bapp.snapshotManager) + continue + } + + require.Equal(t, tc.expectedSnapshot.Interval, tc.bapp.snapshotManager.GetInterval()) + require.Equal(t, tc.expectedSnapshot.KeepRecent, tc.bapp.snapshotManager.GetKeepRecent()) + } +} From d3f6289edb744f19ea113ebe523985be48c0958c Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Thu, 23 Jun 2022 21:56:43 +0800 Subject: [PATCH 74/78] pruning manager - decouple db, rm logger, fix test --- baseapp/baseapp_test.go | 4 +-- pruning/manager.go | 22 ++++++--------- pruning/manager_test.go | 62 +++++++++++++++++++++-------------------- 3 files changed, 42 insertions(+), 46 deletions(-) diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 012f7373730a..0ce55c33c767 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -2049,11 +2049,11 @@ func TestBaseApp_Init(t *testing.T) { expectedSnapshot snapshottypes.SnapshotOptions expectedErr error }{ - "snapshot but no pruning": { + "snapshot but pruning unset": { NewBaseApp(name, logger, db, nil, SetSnapshot(snapshotStore, snapshottypes.NewSnapshotOptions(1500, 2)), ), - pruningtypes.NewPruningOptions(pruningtypes.PruningNothing), + pruningtypes.NewPruningOptions(pruningtypes.PruningDefault), snapshottypes.NewSnapshotOptions(1500, 2), // if no pruning is set, the default is PruneNothing nil, diff --git a/pruning/manager.go b/pruning/manager.go index ac1172ebb86e..365d3cd3572f 100644 --- a/pruning/manager.go +++ b/pruning/manager.go @@ -6,7 +6,6 @@ import ( "fmt" "sync" - "github.com/tendermint/tendermint/libs/log" dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/pruning/types" @@ -16,8 +15,6 @@ import ( // determining when to prune old heights of the store // based on the strategy described by the pruning options. type Manager struct { - db dbm.DB - logger log.Logger opts types.PruningOptions snapshotInterval uint64 // Although pruneHeights happen in the same goroutine with the normal execution, @@ -49,14 +46,12 @@ var ( pruneSnapshotHeightsKey = []byte("s/prunesnapshotheights") ) -// NewManager returns a new Manager with the given db and logger. +// NewManager returns a new Manager with the given db. // The retuned manager uses a pruning strategy of "nothing" which // keeps all heights. Users of the Manager may change the strategy // by calling SetOptions. -func NewManager(db dbm.DB, logger log.Logger) *Manager { +func NewManager() *Manager { return &Manager{ - db: db, - logger: logger, opts: types.NewPruningOptions(types.PruningNothing), pruneHeights: []int64{}, pruneSnapshotHeights: list.New(), @@ -75,7 +70,7 @@ func (m *Manager) GetOptions() types.PruningOptions { // GetFlushAndResetPruningHeights returns all heights to be pruned during the next call to Prune(). // It also flushes and resets the pruning heights. -func (m *Manager) GetFlushAndResetPruningHeights() ([]int64, error) { +func (m *Manager) GetFlushAndResetPruningHeights(db dbm.DB) ([]int64, error) { if m.opts.GetPruningStrategy() == types.PruningNothing { return []int64{}, nil } @@ -83,7 +78,7 @@ func (m *Manager) GetFlushAndResetPruningHeights() ([]int64, error) { defer m.pruneHeightsMx.Unlock() // flush the updates to disk so that it is not lost if crash happens. - if err := m.db.SetSync(pruneHeightsKey, int64SliceToBytes(m.pruneHeights)); err != nil { + if err := db.SetSync(pruneHeightsKey, int64SliceToBytes(m.pruneHeights)); err != nil { return nil, err } @@ -99,7 +94,7 @@ func (m *Manager) GetFlushAndResetPruningHeights() ([]int64, error) { // the pruning strategy. Returns previousHeight, if it was kept to be pruned at the next call to Prune(), 0 otherwise. // previousHeight must be greater than 0 for the handling to take effect since valid heights start at 1 and 0 represents // the latest height. The latest height cannot be pruned. As a result, if previousHeight is less than or equal to 0, 0 is returned. -func (m *Manager) HandleHeight(previousHeight int64) int64 { +func (m *Manager) HandleHeight(previousHeight int64, db dbm.DB) int64 { if m.opts.GetPruningStrategy() == types.PruningNothing || previousHeight <= 0 { return 0 } @@ -128,7 +123,7 @@ func (m *Manager) HandleHeight(previousHeight int64) int64 { } // flush the updates to disk so that they are not lost if crash happens. - if err := m.db.SetSync(pruneHeightsKey, int64SliceToBytes(m.pruneHeights)); err != nil { + if err := db.SetSync(pruneHeightsKey, int64SliceToBytes(m.pruneHeights)); err != nil { panic(err) } }() @@ -155,7 +150,7 @@ func (m *Manager) HandleHeight(previousHeight int64) int64 { // height defined by the pruning strategy. Flushes the update to disk and panics if the flush fails // The input height must be greater than 0 and pruning strategy any but pruning nothing. // If one of these conditions is not met, this function does nothing. -func (m *Manager) HandleHeightSnapshot(height int64) { +func (m *Manager) HandleHeightSnapshot(height int64, db dbm.DB) { if m.opts.GetPruningStrategy() == types.PruningNothing || height <= 0 { return } @@ -163,11 +158,10 @@ func (m *Manager) HandleHeightSnapshot(height int64) { m.pruneSnapshotHeightsMx.Lock() defer m.pruneSnapshotHeightsMx.Unlock() - m.logger.Debug("HandleHeightSnapshot", "height", height) m.pruneSnapshotHeights.PushBack(height) // flush the updates to disk so that they are not lost if crash happens. - if err := m.db.SetSync(pruneSnapshotHeightsKey, listToBytes(m.pruneSnapshotHeights)); err != nil { + if err := db.SetSync(pruneSnapshotHeightsKey, listToBytes(m.pruneSnapshotHeights)); err != nil { panic(err) } } diff --git a/pruning/manager_test.go b/pruning/manager_test.go index 85d38e8c8af7..d9c59e05437e 100644 --- a/pruning/manager_test.go +++ b/pruning/manager_test.go @@ -8,7 +8,6 @@ import ( "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/libs/log" db "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/pruning" @@ -19,10 +18,11 @@ import ( const dbErr = "db error" func TestNewManager(t *testing.T) { - manager := pruning.NewManager(db.NewMemDB(), log.NewNopLogger()) + data := db.NewMemDB() + manager := pruning.NewManager() require.NotNil(t, manager) - heights, err := manager.GetFlushAndResetPruningHeights() + heights, err := manager.GetFlushAndResetPruningHeights(data) require.NoError(t, err) require.NotNil(t, heights) require.Equal(t, types.PruningNothing, manager.GetOptions().GetPruningStrategy()) @@ -79,7 +79,8 @@ func TestStrategies(t *testing.T) { }, } - manager := pruning.NewManager(db.NewMemDB(), log.NewNopLogger()) + data := db.NewMemDB() + manager := pruning.NewManager() require.NotNil(t, manager) @@ -113,10 +114,10 @@ func TestStrategies(t *testing.T) { curInterval := curStrategy.Interval for curHeight := int64(0); curHeight < 110000; curHeight++ { - handleHeightActual := manager.HandleHeight(curHeight) + handleHeightActual := manager.HandleHeight(curHeight, data) shouldPruneAtHeightActual := manager.ShouldPruneAtHeight(curHeight) - curPruningHeihts, err := manager.GetFlushAndResetPruningHeights() + curPruningHeihts, err := manager.GetFlushAndResetPruningHeights(data) require.Nil(t, err) curHeightStr := fmt.Sprintf("height: %d", curHeight) @@ -126,7 +127,7 @@ func TestStrategies(t *testing.T) { require.Equal(t, int64(0), handleHeightActual, curHeightStr) require.False(t, shouldPruneAtHeightActual, curHeightStr) - heights, err := manager.GetFlushAndResetPruningHeights() + heights, err := manager.GetFlushAndResetPruningHeights(data) require.NoError(t, err) require.Equal(t, 0, len(heights)) default: @@ -138,13 +139,13 @@ func TestStrategies(t *testing.T) { } else { require.Equal(t, int64(0), handleHeightActual, curHeightStr) - heights, err := manager.GetFlushAndResetPruningHeights() + heights, err := manager.GetFlushAndResetPruningHeights(data) require.NoError(t, err) require.Equal(t, 0, len(heights)) } require.Equal(t, curHeight%int64(curInterval) == 0, shouldPruneAtHeightActual, curHeightStr) } - heights, err := manager.GetFlushAndResetPruningHeights() + heights, err := manager.GetFlushAndResetPruningHeights(data) require.NoError(t, err) require.Equal(t, 0, len(heights)) } @@ -195,14 +196,15 @@ func TestHandleHeight_Inputs(t *testing.T) { for name, tc := range testcases { t.Run(name, func(t *testing.T) { - manager := pruning.NewManager(db.NewMemDB(), log.NewNopLogger()) + data := db.NewMemDB() + manager := pruning.NewManager() require.NotNil(t, manager) manager.SetOptions(types.NewPruningOptions(tc.strategy)) - handleHeightActual := manager.HandleHeight(tc.height) + handleHeightActual := manager.HandleHeight(tc.height, data) require.Equal(t, tc.expectedResult, handleHeightActual) - actualHeights, err := manager.GetFlushAndResetPruningHeights() + actualHeights, err := manager.GetFlushAndResetPruningHeights(data) require.NoError(t, err) require.Equal(t, len(tc.expectedHeights), len(actualHeights)) require.Equal(t, tc.expectedHeights, actualHeights) @@ -280,18 +282,18 @@ func TestHandleHeight_FlushLoadFromDisk(t *testing.T) { t.Run(name, func(t *testing.T) { // Setup db := db.NewMemDB() - manager := pruning.NewManager(db, log.NewNopLogger()) + manager := pruning.NewManager() require.NotNil(t, manager) manager.SetSnapshotInterval(tc.snapshotInterval) manager.SetOptions(types.NewCustomPruningOptions(uint64(tc.keepRecent), uint64(10))) for _, snapshotHeight := range tc.movedSnapshotHeights { - manager.HandleHeightSnapshot(snapshotHeight) + manager.HandleHeightSnapshot(snapshotHeight, db) } // Test HandleHeight and flush - handleHeightActual := manager.HandleHeight(tc.previousHeight) + handleHeightActual := manager.HandleHeight(tc.previousHeight, db) require.Equal(t, tc.expectedHandleHeightResult, handleHeightActual) loadedPruneHeights, err := pruning.LoadPruningHeights(db) @@ -302,7 +304,7 @@ func TestHandleHeight_FlushLoadFromDisk(t *testing.T) { err = manager.LoadPruningHeights(db) require.NoError(t, err) - heights, err := manager.GetFlushAndResetPruningHeights() + heights, err := manager.GetFlushAndResetPruningHeights(db) require.NoError(t, err) require.Equal(t, len(tc.expectedLoadedHeights), len(heights)) require.ElementsMatch(t, tc.expectedLoadedHeights, heights) @@ -318,7 +320,7 @@ func TestHandleHeight_DbErr_Panic(t *testing.T) { dbMock.EXPECT().SetSync(gomock.Any(), gomock.Any()).Return(errors.New(dbErr)).Times(1) - manager := pruning.NewManager(dbMock, log.NewNopLogger()) + manager := pruning.NewManager() manager.SetOptions(types.NewPruningOptions(types.PruningEverything)) require.NotNil(t, manager) @@ -328,7 +330,7 @@ func TestHandleHeight_DbErr_Panic(t *testing.T) { } }() - manager.HandleHeight(10) + manager.HandleHeight(10, dbMock) } func TestHandleHeightSnapshot_FlushLoadFromDisk(t *testing.T) { @@ -336,14 +338,14 @@ func TestHandleHeightSnapshot_FlushLoadFromDisk(t *testing.T) { // Setup db := db.NewMemDB() - manager := pruning.NewManager(db, log.NewNopLogger()) + manager := pruning.NewManager() require.NotNil(t, manager) manager.SetOptions(types.NewPruningOptions(types.PruningEverything)) for snapshotHeight := int64(-1); snapshotHeight < 100; snapshotHeight++ { // Test flush - manager.HandleHeightSnapshot(snapshotHeight) + manager.HandleHeightSnapshot(snapshotHeight, db) // Post test if snapshotHeight > 0 { @@ -372,7 +374,7 @@ func TestHandleHeightSnapshot_DbErr_Panic(t *testing.T) { dbMock.EXPECT().SetSync(gomock.Any(), gomock.Any()).Return(errors.New(dbErr)).Times(1) - manager := pruning.NewManager(dbMock, log.NewNopLogger()) + manager := pruning.NewManager() manager.SetOptions(types.NewPruningOptions(types.PruningEverything)) require.NotNil(t, manager) @@ -382,12 +384,12 @@ func TestHandleHeightSnapshot_DbErr_Panic(t *testing.T) { } }() - manager.HandleHeightSnapshot(10) + manager.HandleHeightSnapshot(10, dbMock) } func TestFlushLoad(t *testing.T) { db := db.NewMemDB() - manager := pruning.NewManager(db, log.NewNopLogger()) + manager := pruning.NewManager() require.NotNil(t, manager) curStrategy := types.NewCustomPruningOptions(100, 15) @@ -403,7 +405,7 @@ func TestFlushLoad(t *testing.T) { heightsToPruneMirror := make([]int64, 0) for curHeight := int64(0); curHeight < 1000; curHeight++ { - handleHeightActual := manager.HandleHeight(curHeight) + handleHeightActual := manager.HandleHeight(curHeight, db) curHeightStr := fmt.Sprintf("height: %d", curHeight) @@ -416,7 +418,7 @@ func TestFlushLoad(t *testing.T) { } if manager.ShouldPruneAtHeight(curHeight) && curHeight > int64(keepRecent) { - actualHeights, err := manager.GetFlushAndResetPruningHeights() + actualHeights, err := manager.GetFlushAndResetPruningHeights(db) require.NoError(t, err) require.Equal(t, len(heightsToPruneMirror), len(actualHeights)) require.Equal(t, heightsToPruneMirror, actualHeights) @@ -424,7 +426,7 @@ func TestFlushLoad(t *testing.T) { err = manager.LoadPruningHeights(db) require.NoError(t, err) - actualHeights, err = manager.GetFlushAndResetPruningHeights() + actualHeights, err = manager.GetFlushAndResetPruningHeights(db) require.NoError(t, err) require.Equal(t, len(heightsToPruneMirror), len(actualHeights)) require.Equal(t, heightsToPruneMirror, actualHeights) @@ -436,7 +438,7 @@ func TestFlushLoad(t *testing.T) { func TestLoadPruningHeights(t *testing.T) { var ( - manager = pruning.NewManager(db.NewMemDB(), log.NewNopLogger()) + manager = pruning.NewManager() err error ) require.NotNil(t, manager) @@ -506,7 +508,7 @@ func TestLoadPruningHeights(t *testing.T) { } func TestLoadPruningHeights_PruneNothing(t *testing.T) { - manager := pruning.NewManager(db.NewMemDB(), log.NewNopLogger()) + manager := pruning.NewManager() require.NotNil(t, manager) manager.SetOptions(types.NewPruningOptions(types.PruningNothing)) @@ -522,11 +524,11 @@ func TestGetFlushAndResetPruningHeights_DbErr_Panic(t *testing.T) { dbMock.EXPECT().SetSync(gomock.Any(), gomock.Any()).Return(errors.New(dbErr)).Times(1) - manager := pruning.NewManager(dbMock, log.NewNopLogger()) + manager := pruning.NewManager() manager.SetOptions(types.NewPruningOptions(types.PruningEverything)) require.NotNil(t, manager) - heights, err := manager.GetFlushAndResetPruningHeights() + heights, err := manager.GetFlushAndResetPruningHeights(dbMock) require.Error(t, err) require.Nil(t, heights) } From db3c28a8186978e40ba5a5903494800e0a6cd06d Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Wed, 20 Jul 2022 14:18:09 -0500 Subject: [PATCH 75/78] fix merge * app wiring, etc. * tests pass --- baseapp/abci_test.go | 3 +- baseapp/baseapp.go | 7 ++-- baseapp/baseapp_test.go | 55 ++++------------------------- baseapp/block_gas_test.go | 4 +-- db/badgerdb/creator.go | 2 +- db/reexport.go | 6 ++-- runtime/app.go | 8 +---- runtime/builder.go | 10 +++--- runtime/module.go | 4 +++ server/export_test.go | 4 +-- server/mock/app.go | 4 +-- server/util_test.go | 1 - simapp/app.go | 8 ++--- simapp/app_test.go | 8 +++-- simapp/sim_bench_test.go | 4 +-- simapp/sim_test.go | 13 ++++--- simapp/simd/cmd/root.go | 8 ++--- simapp/test_helpers.go | 14 +++++--- simapp/utils.go | 6 ++-- store/store.go | 2 +- store/v2alpha1/multi/store.go | 25 ++++++------- store/v2alpha1/multi/sub_store.go | 4 +-- testutil/context.go | 22 ++++++------ testutil/mock/param_store.go | 49 +++++++++++++++++++++++++ testutil/network/network.go | 5 +-- testutil/network/util.go | 1 + testutil/sims/app_helpers.go | 8 +++-- x/bank/keeper/view.go | 3 -- x/capability/capability_test.go | 7 ++-- x/gov/genesis_test.go | 4 +-- x/gov/module_test.go | 3 +- x/mint/module_test.go | 5 +-- x/params/types/subspace_test.go | 4 +-- x/staking/module_test.go | 2 +- x/upgrade/keeper/keeper.go | 5 --- x/upgrade/types/storeloader_test.go | 4 +-- 36 files changed, 163 insertions(+), 159 deletions(-) create mode 100644 testutil/mock/param_store.go diff --git a/baseapp/abci_test.go b/baseapp/abci_test.go index fa00c3d0abfc..ea7406090cbb 100644 --- a/baseapp/abci_test.go +++ b/baseapp/abci_test.go @@ -13,6 +13,7 @@ import ( "github.com/cosmos/cosmos-sdk/snapshots" snapshottypes "github.com/cosmos/cosmos-sdk/snapshots/types" "github.com/cosmos/cosmos-sdk/testutil" + "github.com/cosmos/cosmos-sdk/testutil/mock" ) func TestGetBlockRentionHeight(t *testing.T) { @@ -108,7 +109,7 @@ func TestGetBlockRentionHeight(t *testing.T) { for name, tc := range testCases { tc := tc - tc.bapp.SetParamStore(newParamStore(memdb.NewDB())) + tc.bapp.SetParamStore(mock.NewParamStore(memdb.NewDB())) tc.bapp.InitChain(abci.RequestInitChain{ ConsensusParams: &tmprototypes.ConsensusParams{ Evidence: &tmprototypes.EvidenceParams{ diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index f0faf54af6dd..eef3eb6a5300 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -192,8 +192,7 @@ func NewBaseApp( } } - err := app.loadStore() - if err != nil { + if err := app.loadStore(); err != nil { panic(err) } for _, option := range afterStoreOpts { @@ -238,7 +237,9 @@ func (app *BaseApp) loadStore() error { latest := versions.Last() config := multi.DefaultStoreParams() for _, opt := range app.storeOpts { - opt(&config, latest) + if err = opt(&config, latest); err != nil { + return err + } } app.store, err = multi.NewV1MultiStoreAsV2(app.db, config) if err != nil { diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 0ce55c33c767..c830ef3ac04d 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -29,6 +29,7 @@ import ( stypes "github.com/cosmos/cosmos-sdk/store/v2alpha1" "github.com/cosmos/cosmos-sdk/store/v2alpha1/multi" "github.com/cosmos/cosmos-sdk/testutil" + "github.com/cosmos/cosmos-sdk/testutil/mock" "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -44,15 +45,6 @@ var ( testTxPriority = int64(42) ) -type paramStore struct { - db *memdb.MemDB - rw dbm.DBReadWriter -} - -func newParamStore(db *memdb.MemDB) *paramStore { - return ¶mStore{db: db, rw: db.ReadWriter()} -} - type setupConfig struct { blocks uint64 blockTxs int @@ -61,39 +53,6 @@ type setupConfig struct { pruningOpts pruningtypes.PruningOptions } -func (ps *paramStore) Set(_ sdk.Context, key []byte, value interface{}) { - bz, err := json.Marshal(value) - if err != nil { - panic(err) - } - - ps.rw.Set(key, bz) -} - -func (ps *paramStore) Has(_ sdk.Context, key []byte) bool { - ok, err := ps.rw.Has(key) - if err != nil { - panic(err) - } - - return ok -} - -func (ps *paramStore) Get(_ sdk.Context, key []byte, ptr interface{}) { - bz, err := ps.rw.Get(key) - if err != nil { - panic(err) - } - - if len(bz) == 0 { - return - } - - if err := json.Unmarshal(bz, ptr); err != nil { - panic(err) - } -} - func defaultLogger() log.Logger { return log.MustNewDefaultLogger("plain", "info", false).With("module", "sdk/app") } @@ -132,7 +91,7 @@ func setupBaseApp(t *testing.T, options ...AppOption) *BaseApp { app := newBaseApp(t.Name(), options...) require.Equal(t, t.Name(), app.Name()) - app.SetParamStore(newParamStore(memdb.NewDB())) + app.SetParamStore(mock.NewParamStore(memdb.NewDB())) // stores are mounted err := app.Init() @@ -254,7 +213,7 @@ func TestLoadVersion(t *testing.T) { testLoadVersionHelper(t, app, int64(2), commitID2) } -func initStore(t *testing.T, db dbm.DBConnection, storeKey string, k, v []byte) { +func initStore(t *testing.T, db dbm.Connection, storeKey string, k, v []byte) { key := sdk.NewKVStoreKey(storeKey) opts := multi.DefaultStoreParams() opts.Pruning = pruningtypes.NewPruningOptions(pruningtypes.PruningNothing) @@ -272,7 +231,7 @@ func initStore(t *testing.T, db dbm.DBConnection, storeKey string, k, v []byte) require.NoError(t, rs.Close()) } -func checkStore(t *testing.T, db dbm.DBConnection, ver int64, storeKey string, k, v []byte) { +func checkStore(t *testing.T, db dbm.Connection, ver int64, storeKey string, k, v []byte) { opts := multi.DefaultStoreParams() opts.Pruning = pruningtypes.NewPruningOptions(pruningtypes.PruningNothing) key := sdk.NewKVStoreKey(storeKey) @@ -2015,7 +1974,7 @@ func TestBaseApp_EndBlock(t *testing.T) { } app := NewBaseApp(name, logger, db, nil) - app.SetParamStore(newParamStore(memdb.NewDB())) + app.SetParamStore(mock.NewParamStore(memdb.NewDB())) app.InitChain(abci.RequestInitChain{ ConsensusParams: cp, }) @@ -2146,10 +2105,10 @@ func TestBaseApp_Init(t *testing.T) { }, "error custom pruning too small keep recent": { NewBaseApp(name, logger, db, nil, - SetPruning(pruningtypes.NewCustomPruningOptions(9, 10)), + SetPruning(pruningtypes.NewCustomPruningOptions(1, 10)), SetSnapshot(snapshotStore, snapshottypes.NewSnapshotOptions(1500, 2)), ), - pruningtypes.NewCustomPruningOptions(9, 10), + pruningtypes.NewCustomPruningOptions(1, 10), snapshottypes.NewSnapshotOptions(1500, 2), pruningtypes.ErrPruningKeepRecentTooSmall, }, diff --git a/baseapp/block_gas_test.go b/baseapp/block_gas_test.go index 17b9aefbf3c7..c6d0f13ef31f 100644 --- a/baseapp/block_gas_test.go +++ b/baseapp/block_gas_test.go @@ -100,8 +100,8 @@ func TestBaseApp_BlockGas(t *testing.T) { &appBuilder) require.NoError(t, err) - bapp := appBuilder.Build(log.NewNopLogger(), dbm.NewMemDB(), nil) - err = bapp.Load(true) + bapp := appBuilder.Build(log.NewNopLogger(), memdb.NewDB(), nil) + err = bapp.Load() require.NoError(t, err) t.Run(tc.name, func(t *testing.T) { diff --git a/db/badgerdb/creator.go b/db/badgerdb/creator.go index 5da5704fc560..fab6f22760a7 100644 --- a/db/badgerdb/creator.go +++ b/db/badgerdb/creator.go @@ -1,4 +1,4 @@ -//go:build badgerdb +// Enabled by default package badgerdb diff --git a/db/reexport.go b/db/reexport.go index 50da6b2de29b..05214a42e234 100644 --- a/db/reexport.go +++ b/db/reexport.go @@ -1,9 +1,9 @@ package db import ( - "github.com/cosmos/cosmos-sdk/db/types" - _ "github.com/cosmos/cosmos-sdk/db/internal/backends" + "github.com/cosmos/cosmos-sdk/db/memdb" + "github.com/cosmos/cosmos-sdk/db/types" ) type ( @@ -27,4 +27,6 @@ var ( NewDB = types.NewDB ReaderAsReadWriter = types.ReaderAsReadWriter NewVersionManager = types.NewVersionManager + + NewMemDB = memdb.NewDB ) diff --git a/runtime/app.go b/runtime/app.go index f9e03d41943b..f7ec8163ec23 100644 --- a/runtime/app.go +++ b/runtime/app.go @@ -75,7 +75,7 @@ func (a *App) RegisterModules(modules ...module.AppModule) error { } // Load finishes all initialization operations and loads the app. -func (a *App) Load(loadLatest bool) error { +func (a *App) Load() error { a.configurator = module.NewConfigurator(a.cdc, a.MsgServiceRouter(), a.GRPCQueryRouter()) a.ModuleManager.RegisterServices(a.configurator) @@ -100,12 +100,6 @@ func (a *App) Load(loadLatest bool) error { a.SetEndBlocker(a.EndBlocker) } - if loadLatest { - if err := a.LoadLatestVersion(); err != nil { - return err - } - } - return nil } diff --git a/runtime/builder.go b/runtime/builder.go index f73805c17cac..51d09b27578c 100644 --- a/runtime/builder.go +++ b/runtime/builder.go @@ -5,9 +5,9 @@ import ( "io" "github.com/tendermint/tendermint/libs/log" - dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/baseapp" + dbm "github.com/cosmos/cosmos-sdk/db" "github.com/cosmos/cosmos-sdk/version" ) @@ -27,20 +27,20 @@ func (a *AppBuilder) DefaultGenesis() map[string]json.RawMessage { // Build builds an *App instance. func (a *AppBuilder) Build( logger log.Logger, - db dbm.DB, + db dbm.Connection, traceStore io.Writer, - baseAppOptions ...func(*baseapp.BaseApp), + baseAppOptions ...baseapp.AppOption, ) *App { for _, option := range a.app.baseAppOptions { - baseAppOptions = append(baseAppOptions, option) + baseAppOptions = append(baseAppOptions, baseapp.AppOptionFunc(option)) } + baseAppOptions = append(baseAppOptions, baseapp.SetSubstores(a.app.storeKeys...)) bApp := baseapp.NewBaseApp(a.app.config.AppName, logger, db, nil, baseAppOptions...) bApp.SetMsgServiceRouter(a.app.msgServiceRouter) bApp.SetCommitMultiStoreTracer(traceStore) bApp.SetVersion(version.Version) bApp.SetInterfaceRegistry(a.app.interfaceRegistry) - bApp.MountStores(a.app.storeKeys...) a.app.BaseApp = bApp return a.app diff --git a/runtime/module.go b/runtime/module.go index 07ff9a430d73..865a2af9c120 100644 --- a/runtime/module.go +++ b/runtime/module.go @@ -23,6 +23,10 @@ type BaseAppOption func(*baseapp.BaseApp) // IsManyPerContainerType indicates that this is a depinject.ManyPerContainerType. func (b BaseAppOption) IsManyPerContainerType() {} +// // Implement base +// func (opt BaseAppOption) Apply(app *baseapp.BaseApp) { opt(app) } +// func (opt BaseAppOption) Order() baseapp.OptionOrder { return baseapp.OptionOrderDefault } + // appWrapper is used to pass around an instance of *App internally between // runtime dependency inject providers that is partially constructed (no // baseapp yet). diff --git a/server/export_test.go b/server/export_test.go index 282e04dfd036..21c800de63cb 100644 --- a/server/export_test.go +++ b/server/export_test.go @@ -130,7 +130,7 @@ func setupApp(t *testing.T, tempDir string) (*simapp.SimApp, context.Context, *t logger, _ := log.NewDefaultLogger("plain", "info", false) db := memdb.NewDB() encCfg := simapp.MakeTestEncodingConfig() - app := simapp.NewSimApp(logger, db, nil, true, encCfg, simtestutil.NewAppOptionsWithFlagHome(tempDir)) + app := simapp.NewSimApp(logger, db, nil, encCfg, simtestutil.NewAppOptionsWithFlagHome(tempDir)) genesisState := simapp.GenesisStateWithSingleValidator(t, app) stateBytes, err := tmjson.MarshalIndent(genesisState, "", " ") @@ -159,7 +159,7 @@ func setupApp(t *testing.T, tempDir string) (*simapp.SimApp, context.Context, *t func(_ log.Logger, _ dbm.Connection, _ io.Writer, height int64, forZeroHeight bool, jailAllowedAddrs []string, appOptions types.AppOptions) (types.ExportedApp, error) { require.NoError(t, app.CloseStore()) encCfg := simapp.MakeTestEncodingConfig() - simApp := simapp.NewSimApp(logger, db, nil, false, encCfg, appOptions) + simApp := simapp.NewSimApp(logger, db, nil, encCfg, appOptions) require.NoError(t, simApp.Init()) return simApp.ExportAppStateAndValidatorsAt(forZeroHeight, jailAllowedAddrs, height) diff --git a/server/mock/app.go b/server/mock/app.go index 67874318a026..d7e36aef9b1a 100644 --- a/server/mock/app.go +++ b/server/mock/app.go @@ -12,7 +12,7 @@ import ( bam "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/db" + "github.com/cosmos/cosmos-sdk/db/badgerdb" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -21,7 +21,7 @@ import ( // similar to a real app. Make sure rootDir is empty before running the test, // in order to guarantee consistent results func NewApp(rootDir string, logger log.Logger) (abci.Application, error) { - db, err := db.NewDB("mock", db.BadgerDBBackend, filepath.Join(rootDir, "data")) + db, err := badgerdb.NewDB(filepath.Join(rootDir, "mock")) if err != nil { return nil, err } diff --git a/server/util_test.go b/server/util_test.go index a7085747a724..c3bd0f9063cd 100644 --- a/server/util_test.go +++ b/server/util_test.go @@ -16,7 +16,6 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" - dbm "github.com/cosmos/cosmos-sdk/db" "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/server/config" servertypes "github.com/cosmos/cosmos-sdk/server/types" diff --git a/simapp/app.go b/simapp/app.go index 289e0cce3c77..3430bbaf9f73 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -4,7 +4,6 @@ package simapp import ( _ "embed" - "encoding/json" "errors" "io" "net/http" @@ -25,13 +24,13 @@ import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" dbm "github.com/cosmos/cosmos-sdk/db" "github.com/cosmos/cosmos-sdk/runtime" - "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/server/api" "github.com/cosmos/cosmos-sdk/server/config" servertypes "github.com/cosmos/cosmos-sdk/server/types" simappparams "github.com/cosmos/cosmos-sdk/simapp/params" "github.com/cosmos/cosmos-sdk/store/streaming" storetypes "github.com/cosmos/cosmos-sdk/store/v2alpha1" + // "github.com/cosmos/cosmos-sdk/testutil/mock" "github.com/cosmos/cosmos-sdk/testutil/testdata_pulsar" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" @@ -191,7 +190,7 @@ func init() { // NewSimApp returns a reference to an initialized SimApp. func NewSimApp( logger log.Logger, - db dbm.DBConnection, + db dbm.Connection, traceStore io.Writer, encodingConfig simappparams.EncodingConfig, appOpts servertypes.AppOptions, @@ -310,10 +309,9 @@ func NewSimApp( // initialize BaseApp app.SetInitChainer(app.InitChainer) - if err := app.Load(loadLatest); err != nil { + if err := app.Load(); err != nil { panic(err) } - return app } diff --git a/simapp/app_test.go b/simapp/app_test.go index 2c908df15160..33f12341bcb9 100644 --- a/simapp/app_test.go +++ b/simapp/app_test.go @@ -30,6 +30,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/gov" group "github.com/cosmos/cosmos-sdk/x/group/module" "github.com/cosmos/cosmos-sdk/x/mint" + // nft "github.com/cosmos/cosmos-sdk/x/nft/module" "github.com/cosmos/cosmos-sdk/x/params" "github.com/cosmos/cosmos-sdk/x/slashing" "github.com/cosmos/cosmos-sdk/x/staking" @@ -60,7 +61,7 @@ func TestSimAppExportAndBlockedAddrs(t *testing.T) { logger2, _ := log.NewDefaultLogger("plain", "info", false) // Making a new app object with the db, so that initchain hasn't been called - app2 := NewSimApp(logger2, db, nil, true, encCfg, simtestutil.NewAppOptionsWithFlagHome(DefaultNodeHome)) + app2 := NewSimApp(logger2, db, nil, encCfg, simtestutil.NewAppOptionsWithFlagHome(DefaultNodeHome)) require.NoError(t, app2.Init()) _, err := app2.ExportAppStateAndValidators(false, []string{}) require.NoError(t, err, "ExportAppStateAndValidators should not have an error") @@ -74,7 +75,7 @@ func TestGetMaccPerms(t *testing.T) { func TestRunMigrations(t *testing.T) { encCfg := MakeTestEncodingConfig() logger, _ := log.NewDefaultLogger("plain", "info", false) - app := NewSimApp(logger, memdb.NewDB(), nil, true, encCfg, simtestutil.NewAppOptionsWithFlagHome(DefaultNodeHome)) + app := NewSimApp(logger, memdb.NewDB(), nil, encCfg, simtestutil.NewAppOptionsWithFlagHome(DefaultNodeHome)) // Create a new baseapp and configurator for the purpose of this test. bApp := baseapp.NewBaseApp(app.Name(), logger, memdb.NewDB(), encCfg.TxConfig.TxDecoder()) @@ -208,7 +209,7 @@ func TestInitGenesisOnMigration(t *testing.T) { db := memdb.NewDB() encCfg := MakeTestEncodingConfig() logger, _ := log.NewDefaultLogger("plain", "info", false) - app := NewSimApp(logger, db, nil, true, encCfg, simtestutil.NewAppOptionsWithFlagHome(DefaultNodeHome)) + app := NewSimApp(logger, db, nil, encCfg, simtestutil.NewAppOptionsWithFlagHome(DefaultNodeHome)) require.NoError(t, app.Init()) ctx := app.NewContext(true, tmproto.Header{Height: app.LastBlockHeight()}) @@ -244,6 +245,7 @@ func TestInitGenesisOnMigration(t *testing.T) { "crisis": crisis.AppModule{}.ConsensusVersion(), "genutil": genutil.AppModule{}.ConsensusVersion(), "capability": capability.AppModule{}.ConsensusVersion(), + // "nft": nft.AppModule{}.ConsensusVersion(), }, ) require.NoError(t, err) diff --git a/simapp/sim_bench_test.go b/simapp/sim_bench_test.go index 115dd62b6073..e76331fafb47 100644 --- a/simapp/sim_bench_test.go +++ b/simapp/sim_bench_test.go @@ -37,7 +37,7 @@ func BenchmarkFullAppSimulation(b *testing.B) { appOptions[flags.FlagHome] = DefaultNodeHome appOptions[server.FlagInvCheckPeriod] = FlagPeriodValue - app := NewSimApp(logger, db, nil, true, MakeTestEncodingConfig(), appOptions, interBlockCacheOpt()) + app := NewSimApp(logger, db, nil, MakeTestEncodingConfig(), appOptions, interBlockCacheOpt()) // run randomized simulation _, simParams, simErr := simulation.SimulateFromSeed( @@ -88,7 +88,7 @@ func BenchmarkInvariants(b *testing.B) { appOptions[flags.FlagHome] = DefaultNodeHome appOptions[server.FlagInvCheckPeriod] = FlagPeriodValue - app := NewSimApp(logger, db, nil, true, MakeTestEncodingConfig(), appOptions, interBlockCacheOpt()) + app := NewSimApp(logger, db, nil, MakeTestEncodingConfig(), appOptions, interBlockCacheOpt()) // run randomized simulation _, simParams, simErr := simulation.SimulateFromSeed( diff --git a/simapp/sim_test.go b/simapp/sim_test.go index 8041018b1196..879f65b3f7af 100644 --- a/simapp/sim_test.go +++ b/simapp/sim_test.go @@ -20,7 +20,6 @@ import ( "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/db/memdb" - "github.com/cosmos/cosmos-sdk/store" simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" sdk "github.com/cosmos/cosmos-sdk/types" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" @@ -78,7 +77,7 @@ func TestFullAppSimulation(t *testing.T) { appOptions[flags.FlagHome] = DefaultNodeHome appOptions[server.FlagInvCheckPeriod] = FlagPeriodValue - app := NewSimApp(logger, db, nil, true, MakeTestEncodingConfig(), appOptions, fauxMerkleModeOpt) + app := NewSimApp(logger, db, nil, MakeTestEncodingConfig(), appOptions, fauxMerkleModeOpt) require.Equal(t, "SimApp", app.Name()) // run randomized simulation @@ -120,7 +119,7 @@ func TestAppImportExport(t *testing.T) { appOptions[flags.FlagHome] = DefaultNodeHome appOptions[server.FlagInvCheckPeriod] = FlagPeriodValue - app := NewSimApp(logger, db, nil, true, MakeTestEncodingConfig(), appOptions, fauxMerkleModeOpt) + app := NewSimApp(logger, db, nil, MakeTestEncodingConfig(), appOptions, fauxMerkleModeOpt) require.Equal(t, "SimApp", app.Name()) // Run randomized simulation @@ -160,7 +159,7 @@ func TestAppImportExport(t *testing.T) { require.NoError(t, os.RemoveAll(newDir)) }() - newApp := NewSimApp(log.NewNopLogger(), newDB, nil, true, MakeTestEncodingConfig(), appOptions, fauxMerkleModeOpt) + newApp := NewSimApp(log.NewNopLogger(), newDB, nil, MakeTestEncodingConfig(), appOptions, fauxMerkleModeOpt) require.Equal(t, "SimApp", newApp.Name()) var genesisState GenesisState @@ -233,7 +232,7 @@ func TestAppSimulationAfterImport(t *testing.T) { appOptions[flags.FlagHome] = DefaultNodeHome appOptions[server.FlagInvCheckPeriod] = FlagPeriodValue - app := NewSimApp(logger, db, nil, true, MakeTestEncodingConfig(), appOptions, fauxMerkleModeOpt) + app := NewSimApp(logger, db, nil, MakeTestEncodingConfig(), appOptions, fauxMerkleModeOpt) require.Equal(t, "SimApp", app.Name()) // Run randomized simulation @@ -278,7 +277,7 @@ func TestAppSimulationAfterImport(t *testing.T) { require.NoError(t, os.RemoveAll(newDir)) }() - newApp := NewSimApp(log.NewNopLogger(), newDB, nil, true, MakeTestEncodingConfig(), appOptions, fauxMerkleModeOpt) + newApp := NewSimApp(log.NewNopLogger(), newDB, nil, MakeTestEncodingConfig(), appOptions, fauxMerkleModeOpt) require.Equal(t, "SimApp", newApp.Name()) newApp.InitChain(abci.RequestInitChain{ @@ -333,7 +332,7 @@ func TestAppStateDeterminism(t *testing.T) { } db := memdb.NewDB() - app := NewSimApp(logger, db, nil, true, MakeTestEncodingConfig(), appOptions, interBlockCacheOpt()) + app := NewSimApp(logger, db, nil, MakeTestEncodingConfig(), appOptions, interBlockCacheOpt()) fmt.Printf( "running non-determinism simulation; seed %d: %d/%d, attempt: %d/%d\n", diff --git a/simapp/simd/cmd/root.go b/simapp/simd/cmd/root.go index 9ffc4979215c..aa1ed0624cd0 100644 --- a/simapp/simd/cmd/root.go +++ b/simapp/simd/cmd/root.go @@ -248,7 +248,7 @@ type appCreator struct { // newApp is an appCreator func (a appCreator) newApp( logger log.Logger, - db dbm.DBConnection, + db dbm.Connection, traceStore io.Writer, appOpts servertypes.AppOptions, ) servertypes.Application { @@ -284,7 +284,7 @@ func (a appCreator) newApp( ) return simapp.NewSimApp( - logger, db, traceStore, true, + logger, db, traceStore, a.encCfg, appOpts, baseapp.SetPruning(pruningOpts), @@ -302,7 +302,7 @@ func (a appCreator) newApp( // appExport creates a new simapp (optionally at a given height) // and exports state. func (a appCreator) appExport( - logger log.Logger, db dbm.DBConnection, traceStore io.Writer, height int64, forZeroHeight bool, jailAllowedAddrs []string, appOpts servertypes.AppOptions, + logger log.Logger, db dbm.Connection, traceStore io.Writer, height int64, forZeroHeight bool, jailAllowedAddrs []string, appOpts servertypes.AppOptions, ) (servertypes.ExportedApp, error) { var simApp *simapp.SimApp @@ -322,7 +322,7 @@ func (a appCreator) appExport( viperAppOpts.Set(server.FlagInvCheckPeriod, 1) appOpts = viperAppOpts - simApp = simapp.NewSimApp(logger, db, traceStore, true, a.encCfg, appOpts) + simApp = simapp.NewSimApp(logger, db, traceStore, a.encCfg, appOpts) if height != -1 { return simApp.ExportAppStateAndValidatorsAt(forZeroHeight, jailAllowedAddrs, height) } diff --git a/simapp/test_helpers.go b/simapp/test_helpers.go index b23dbf840314..47a023ae9443 100644 --- a/simapp/test_helpers.go +++ b/simapp/test_helpers.go @@ -41,7 +41,7 @@ import ( // SetupOptions defines arguments that are passed into `Simapp` constructor. type SetupOptions struct { Logger log.Logger - DB *dbm.MemDB + DB *memdb.MemDB EncConfig simappparams.EncodingConfig AppOpts types.AppOptions } @@ -53,7 +53,7 @@ func setup(withGenesis bool, invCheckPeriod uint) (*SimApp, GenesisState) { appOptions[flags.FlagHome] = DefaultNodeHome appOptions[server.FlagInvCheckPeriod] = invCheckPeriod - app := NewSimApp(log.NewNopLogger(), memdb.NewDB(), nil, true, encCdc, appOptions) + app := NewSimApp(log.NewNopLogger(), memdb.NewDB(), nil, encCdc, appOptions) if withGenesis { return app, NewDefaultGenesisState(encCdc.Codec) } @@ -79,7 +79,9 @@ func NewSimappWithCustomOptions(t *testing.T, isCheckTx bool, options SetupOptio Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100000000000000))), } - app := NewSimApp(options.Logger, options.DB, nil, true, options.EncConfig, options.AppOpts) + app := NewSimApp(options.Logger, options.DB, nil, options.EncConfig, options.AppOpts) + app.SetParamStore(mock.NewParamStore(memdb.NewDB())) + require.NoError(t, app.Init()) genesisState := NewDefaultGenesisState(app.appCodec) genesisState, err = simtestutil.GenesisStateWithValSet(app.AppCodec(), genesisState, valSet, []authtypes.GenesisAccount{acc}, balance) require.NoError(t, err) @@ -366,13 +368,15 @@ func NewTestNetworkFixture() network.TestFixture { InterfaceRegistry: encodingCfg.InterfaceRegistry, } appCtr := func(val testutil.Validator) servertypes.Application { - return NewSimApp( - val.GetCtx().Logger, dbm.NewMemDB(), nil, true, + app := NewSimApp( + val.GetCtx().Logger, memdb.NewDB(), nil, encodingCfg, simtestutil.NewAppOptionsWithFlagHome(val.GetCtx().Config.RootDir), bam.SetPruning(pruningtypes.NewPruningOptionsFromString(val.GetAppConfig().Pruning)), bam.SetMinGasPrices(val.GetAppConfig().MinGasPrices), ) + // app.SetParamStore(mock.NewParamStore(memdb.NewDB())) + return app } return network.TestFixture{ diff --git a/simapp/utils.go b/simapp/utils.go index 87d74e644d6e..5c87395e9a94 100644 --- a/simapp/utils.go +++ b/simapp/utils.go @@ -8,7 +8,7 @@ import ( "github.com/tendermint/tendermint/libs/log" "github.com/cosmos/cosmos-sdk/codec" - dbm "github.com/cosmos/cosmos-sdk/db" + db "github.com/cosmos/cosmos-sdk/db" simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/kv" @@ -19,7 +19,7 @@ import ( // SetupSimulation creates the config, db (levelDB), temporary directory and logger for // the simulation tests. If `FlagEnabledValue` is false it skips the current test. // Returns error on an invalid db instantiation or temp dir creation. -func SetupSimulation(dirPrefix, dbName string) (simtypes.Config, dbm.DBConnection, string, log.Logger, bool, error) { +func SetupSimulation(dirPrefix, dbName string) (simtypes.Config, db.Connection, string, log.Logger, bool, error) { if !FlagEnabledValue { return simtypes.Config{}, nil, "", nil, true, nil } @@ -105,7 +105,7 @@ func CheckExportSimulation( // PrintStats prints the corresponding statistics from the app DB. // TODO: implement stats collection for DBConnection -func PrintStats(db dbm.Connection) { +func PrintStats(db db.Connection) { // stats := db.Stats() fmt.Println("\nDB Stats: not available") } diff --git a/store/store.go b/store/store.go index 96c33d8784ba..a84899313210 100644 --- a/store/store.go +++ b/store/store.go @@ -8,7 +8,7 @@ import ( "github.com/cosmos/cosmos-sdk/store/v2alpha1/multi" ) -func NewCommitMultiStore(db dbm.DBConnection) types.CommitMultiStore { +func NewCommitMultiStore(db dbm.Connection) types.CommitMultiStore { store, err := multi.NewV1MultiStoreAsV2(db, multi.DefaultStoreParams()) if err != nil { panic(err) diff --git a/store/v2alpha1/multi/store.go b/store/v2alpha1/multi/store.go index bd17009cb986..f14cfb29dd9b 100644 --- a/store/v2alpha1/multi/store.go +++ b/store/v2alpha1/multi/store.go @@ -211,7 +211,7 @@ func readSavedSchema(bucket dbm.Reader) (*SchemaBuilder, error) { // NewStore constructs a MultiStore directly from a database. // Creates a new store if no data exists; otherwise loads existing data. -func NewStore(db dbm.DBConnection, opts StoreParams) (ret *Store, err error) { +func NewStore(db dbm.Connection, opts StoreParams) (ret *Store, err error) { pruningManager := pruning.NewManager() pruningManager.SetOptions(opts.Pruning) { // load any pruned heights we missed from disk to be pruned on the next run @@ -600,7 +600,7 @@ func (s *Store) commit(target uint64) (id *types.CommitID, err error) { } // Update substore Merkle roots for key, storeHash := range storeHashes { - w := prefixdb.NewPrefixReadWriter(s.stateTxn, substorePrefix(key)) + w := prefixdb.NewReadWriter(s.stateTxn, substorePrefix(key)) if err = w.Set(substoreMerkleRootKey, storeHash); err != nil { return } @@ -922,12 +922,12 @@ func (pr *SchemaBuilder) migrateSchema(upgrades types.StoreUpgrades) error { func (reg *SchemaBuilder) storeInfo(key string) (sst types.StoreType, ix int, err error) { ix, has := binarySearch(reg.reserved, key) if !has { - err = fmt.Errorf("prefix does not exist: %v", key) + err = fmt.Errorf("name does not exist: %v", key) return } sst, has = reg.StoreSchema[key] if !has { - err = fmt.Errorf("prefix is registered but not in schema: %v", key) + err = fmt.Errorf("name is registered but not in schema: %v", key) } return @@ -938,14 +938,15 @@ func (reg *SchemaBuilder) registerName(key string, typ types.StoreType) error { // Find the neighboring reserved prefix, and check for duplicates and conflicts i, has := binarySearch(reg.reserved, key) if has { - return fmt.Errorf("prefix already exists: %v", key) - } - if i > 0 && strings.HasPrefix(key, reg.reserved[i-1]) { - return fmt.Errorf("prefix conflict: '%v' exists, cannot add '%v'", reg.reserved[i-1], key) - } - if i < len(reg.reserved) && strings.HasPrefix(reg.reserved[i], key) { - return fmt.Errorf("prefix conflict: '%v' exists, cannot add '%v'", reg.reserved[i], key) - } + return fmt.Errorf("name already exists: %v", key) + } + // TODO auth vs authz ? + // if i > 0 && strings.HasPrefix(key, reg.reserved[i-1]) { + // return fmt.Errorf("name conflict: '%v' exists, cannot add '%v'", reg.reserved[i-1], key) + // } + // if i < len(reg.reserved) && strings.HasPrefix(reg.reserved[i], key) { + // return fmt.Errorf("name conflict: '%v' exists, cannot add '%v'", reg.reserved[i], key) + // } reserved := reg.reserved[:i] reserved = append(reserved, key) reg.reserved = append(reserved, reg.reserved[i:]...) diff --git a/store/v2alpha1/multi/sub_store.go b/store/v2alpha1/multi/sub_store.go index 97b74f1186ba..c62b107a907b 100644 --- a/store/v2alpha1/multi/sub_store.go +++ b/store/v2alpha1/multi/sub_store.go @@ -61,7 +61,7 @@ func (s *substore) Iterator(start, end []byte) types.Iterator { if err != nil { panic(err) } - return dbutil.DBToStoreIterator(iter) + return dbutil.ToStoreIterator(iter) } // ReverseIterator implements KVStore. @@ -70,7 +70,7 @@ func (s *substore) ReverseIterator(start, end []byte) types.Iterator { if err != nil { panic(err) } - return dbutil.DBToStoreIterator(iter) + return dbutil.ToStoreIterator(iter) } // GetStoreType implements Store. diff --git a/testutil/context.go b/testutil/context.go index c320353fbda8..5007921be222 100644 --- a/testutil/context.go +++ b/testutil/context.go @@ -8,6 +8,8 @@ import ( tmproto "github.com/tendermint/tendermint/proto/tendermint/types" "github.com/cosmos/cosmos-sdk/db/memdb" + "github.com/cosmos/cosmos-sdk/store" + storetypes "github.com/cosmos/cosmos-sdk/store/types" stypes "github.com/cosmos/cosmos-sdk/store/v2alpha1" "github.com/cosmos/cosmos-sdk/store/v2alpha1/multi" sdk "github.com/cosmos/cosmos-sdk/types" @@ -23,12 +25,10 @@ func DefaultContext(key, tkey stypes.StoreKey) (ret sdk.Context) { }() db := memdb.NewDB() opts := multi.DefaultStoreParams() - err = opts.RegisterSubstore(key, stypes.StoreTypePersistent) - if err != nil { + if err = opts.RegisterSubstore(key, stypes.StoreTypePersistent); err != nil { return } - err = opts.RegisterSubstore(tkey, stypes.StoreTypeTransient) - if err != nil { + if err = opts.RegisterSubstore(tkey, stypes.StoreTypeTransient); err != nil { return } rs, err := multi.NewV1MultiStoreAsV2(db, opts) @@ -41,16 +41,16 @@ func DefaultContext(key, tkey stypes.StoreKey) (ret sdk.Context) { type TestContext struct { Ctx sdk.Context - DB *dbm.MemDB + DB *memdb.MemDB CMS store.CommitMultiStore } -func DefaultContextWithDB(t *testing.T, key storetypes.StoreKey, tkey storetypes.StoreKey) TestContext { - db := dbm.NewMemDB() - cms := store.NewCommitMultiStore(db) - cms.MountStoreWithDB(key, storetypes.StoreTypeIAVL, db) - cms.MountStoreWithDB(tkey, storetypes.StoreTypeTransient, db) - err := cms.LoadLatestVersion() +func DefaultContextWithDB(t *testing.T, key store.Key, tkey storetypes.StoreKey) TestContext { + db := memdb.NewDB() + opts := multi.DefaultStoreParams() + assert.NoError(t, opts.RegisterSubstore(key, stypes.StoreTypePersistent)) + assert.NoError(t, opts.RegisterSubstore(tkey, stypes.StoreTypeTransient)) + cms, err := multi.NewV1MultiStoreAsV2(db, opts) assert.NoError(t, err) ctx := sdk.NewContext(cms, tmproto.Header{}, false, log.NewNopLogger()) diff --git a/testutil/mock/param_store.go b/testutil/mock/param_store.go new file mode 100644 index 000000000000..bb544c32ab31 --- /dev/null +++ b/testutil/mock/param_store.go @@ -0,0 +1,49 @@ +package mock + +import ( + "encoding/json" + + "github.com/cosmos/cosmos-sdk/db" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +type ParamStore struct { + Txn db.ReadWriter +} + +func NewParamStore(db db.Connection) *ParamStore { + return &ParamStore{Txn: db.ReadWriter()} +} + +func (ps *ParamStore) Set(_ sdk.Context, key []byte, value interface{}) { + bz, err := json.Marshal(value) + if err != nil { + panic(err) + } + + ps.Txn.Set(key, bz) +} + +func (ps *ParamStore) Has(_ sdk.Context, key []byte) bool { + ok, err := ps.Txn.Has(key) + if err != nil { + panic(err) + } + + return ok +} + +func (ps *ParamStore) Get(_ sdk.Context, key []byte, ptr interface{}) { + bz, err := ps.Txn.Get(key) + if err != nil { + panic(err) + } + + if len(bz) == 0 { + return + } + + if err := json.Unmarshal(bz, ptr); err != nil { + panic(err) + } +} diff --git a/testutil/network/network.go b/testutil/network/network.go index 799178f2f7e5..4a6703344795 100644 --- a/testutil/network/network.go +++ b/testutil/network/network.go @@ -35,6 +35,7 @@ import ( "github.com/cosmos/cosmos-sdk/crypto/hd" "github.com/cosmos/cosmos-sdk/crypto/keyring" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/db" pruningtypes "github.com/cosmos/cosmos-sdk/pruning/types" "github.com/cosmos/cosmos-sdk/runtime" "github.com/cosmos/cosmos-sdk/server" @@ -158,13 +159,13 @@ func DefaultConfigWithAppConfig(appConfig depinject.Config) (Config, error) { } app := appBuilder.Build( val.GetCtx().Logger, - dbm.NewMemDB(), + db.NewMemDB(), nil, baseapp.SetPruning(pruningtypes.NewPruningOptionsFromString(val.GetAppConfig().Pruning)), baseapp.SetMinGasPrices(val.GetAppConfig().MinGasPrices), ) - if err := app.Load(true); err != nil { + if err := app.Load(); err != nil { panic(err) } diff --git a/testutil/network/util.go b/testutil/network/util.go index e3ace5544445..3d0b4d83cf24 100644 --- a/testutil/network/util.go +++ b/testutil/network/util.go @@ -34,6 +34,7 @@ func startInProcess(cfg Config, val *Validator) error { } app := cfg.AppConstructor(*val) + // app.SetParamStore() genDoc, err := types.GenesisDocFromFile(tmCfg.GenesisFile()) if err != nil { diff --git a/testutil/sims/app_helpers.go b/testutil/sims/app_helpers.go index 03d6323ebfa7..984d46c784c4 100644 --- a/testutil/sims/app_helpers.go +++ b/testutil/sims/app_helpers.go @@ -11,14 +11,15 @@ import ( "github.com/tendermint/tendermint/libs/log" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmtypes "github.com/tendermint/tendermint/types" - dbm "github.com/tendermint/tm-db" "cosmossdk.io/depinject" + "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + dbm "github.com/cosmos/cosmos-sdk/db" "github.com/cosmos/cosmos-sdk/runtime" servertypes "github.com/cosmos/cosmos-sdk/server/types" "github.com/cosmos/cosmos-sdk/testutil/mock" @@ -107,13 +108,14 @@ func SetupWithConfiguration(appConfig depinject.Config, validatorSet func() (*tm } if baseAppOption != nil { - app = appBuilder.Build(log.NewNopLogger(), dbm.NewMemDB(), nil, baseAppOption) + app = appBuilder.Build(log.NewNopLogger(), dbm.NewMemDB(), nil, baseapp.AppOptionFunc(baseAppOption)) } else { app = appBuilder.Build(log.NewNopLogger(), dbm.NewMemDB(), nil) } - if err := app.Load(true); err != nil { + if err := app.Load(); err != nil { return nil, fmt.Errorf("failed to load app: %w", err) } + app.SetParamStore(mock.NewParamStore(dbm.NewMemDB())) // create validator set valSet, err := validatorSet() diff --git a/x/bank/keeper/view.go b/x/bank/keeper/view.go index 9c35bf04effd..35e640823762 100644 --- a/x/bank/keeper/view.go +++ b/x/bank/keeper/view.go @@ -243,9 +243,6 @@ func (k BaseViewKeeper) ValidateBalance(ctx sdk.Context, addr sdk.AccAddress) er // getAccountStore gets the account store of the given address. func (k BaseViewKeeper) getAccountStore(ctx sdk.Context, addr sdk.AccAddress) prefix.Store { - if len(addr) == 0 { - panic("address cannot be empty") - } store := ctx.KVStore(k.storeKey) return prefix.NewStore(store, types.CreateAccountBalancesPrefix(addr)) } diff --git a/x/capability/capability_test.go b/x/capability/capability_test.go index ce14eb4d321f..6e974b21c9dc 100644 --- a/x/capability/capability_test.go +++ b/x/capability/capability_test.go @@ -7,7 +7,7 @@ import ( abci "github.com/tendermint/tendermint/abci/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - "github.com/cosmos/cosmos-sdk/baseapp" + ba "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/runtime" storetypes "github.com/cosmos/cosmos-sdk/store/types" @@ -34,10 +34,7 @@ func (suite *CapabilityTestSuite) SetupTest() { suite.memKey = storetypes.NewMemoryStoreKey("testingkey") app, err := simtestutil.SetupWithBaseAppOption(testutil.AppConfig, - func(ba *baseapp.BaseApp) { - ba.MountStores(suite.memKey) - }, - &suite.cdc, + ba.SetSubstores(suite.memKey).Apply, &suite.cdc, &suite.keeper, ) suite.Require().NoError(err) diff --git a/x/gov/genesis_test.go b/x/gov/genesis_test.go index 7356468c6029..ac67b842e22e 100644 --- a/x/gov/genesis_test.go +++ b/x/gov/genesis_test.go @@ -73,8 +73,8 @@ func TestImportExportQueues(t *testing.T) { panic(err) } - db := dbm.NewMemDB() - app2 := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, simapp.MakeTestEncodingConfig(), simtestutil.NewAppOptionsWithFlagHome(simapp.DefaultNodeHome)) + db := memdb.NewDB() + app2 := simapp.NewSimApp(log.NewNopLogger(), db, nil, simapp.MakeTestEncodingConfig(), simtestutil.NewAppOptionsWithFlagHome(simapp.DefaultNodeHome)) app2.InitChain( abci.RequestInitChain{ diff --git a/x/gov/module_test.go b/x/gov/module_test.go index 704d7840b8d7..43a17fb06d22 100644 --- a/x/gov/module_test.go +++ b/x/gov/module_test.go @@ -10,6 +10,7 @@ import ( tmproto "github.com/tendermint/tendermint/proto/tendermint/types" "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/db/memdb" "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/simapp" simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" @@ -25,7 +26,7 @@ func TestItCreatesModuleAccountOnInitBlock(t *testing.T) { appOptions[flags.FlagHome] = simapp.DefaultNodeHome appOptions[server.FlagInvCheckPeriod] = 5 - app := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, encCdc, appOptions) + app := simapp.NewSimApp(log.NewNopLogger(), db, nil, encCdc, appOptions) genesisState := simapp.GenesisStateWithSingleValidator(t, app) stateBytes, err := tmjson.Marshal(genesisState) diff --git a/x/mint/module_test.go b/x/mint/module_test.go index 374c36374747..9cf473196dc8 100644 --- a/x/mint/module_test.go +++ b/x/mint/module_test.go @@ -6,7 +6,6 @@ import ( "github.com/stretchr/testify/require" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - "github.com/cosmos/cosmos-sdk/db/memdb" simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" @@ -15,9 +14,7 @@ import ( ) func TestItCreatesModuleAccountOnInitBlock(t *testing.T) { - db := memdb.NewDB() - encCdc := simapp.MakeTestEncodingConfig() - app := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, simapp.DefaultNodeHome, 5, encCdc, simapp.EmptyAppOptions{}) + var accountKeeper authkeeper.AccountKeeper app, err := simtestutil.SetupAtGenesis(testutil.AppConfig, &accountKeeper) require.NoError(t, err) diff --git a/x/params/types/subspace_test.go b/x/params/types/subspace_test.go index e2d24a598ade..ef5442239e50 100644 --- a/x/params/types/subspace_test.go +++ b/x/params/types/subspace_test.go @@ -13,7 +13,7 @@ import ( "cosmossdk.io/depinject" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/db/memdb" - "github.com/cosmos/cosmos-sdk/store" + storetypes "github.com/cosmos/cosmos-sdk/store/types" "github.com/cosmos/cosmos-sdk/store/v2alpha1/multi" sdk "github.com/cosmos/cosmos-sdk/types" @@ -39,7 +39,7 @@ func (suite *SubspaceTestSuite) SetupTest() { ms, err := multi.NewV1MultiStoreAsV2(db, config) suite.NoError(err) - err := depinject.Inject(testutil.AppConfig, + err = depinject.Inject(testutil.AppConfig, &suite.cdc, &suite.amino, ) diff --git a/x/staking/module_test.go b/x/staking/module_test.go index 7c7adc914ec0..37e5f9310e5d 100644 --- a/x/staking/module_test.go +++ b/x/staking/module_test.go @@ -26,7 +26,7 @@ func TestItCreatesModuleAccountOnInitBlock(t *testing.T) { appOptions[flags.FlagHome] = simapp.DefaultNodeHome appOptions[server.FlagInvCheckPeriod] = 5 - app := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, encCdc, appOptions) + app := simapp.NewSimApp(log.NewNopLogger(), db, nil, encCdc, appOptions) genesisState := simapp.GenesisStateWithSingleValidator(t, app) stateBytes, err := tmjson.Marshal(genesisState) diff --git a/x/upgrade/keeper/keeper.go b/x/upgrade/keeper/keeper.go index c5a2821fb721..21e3ee70fb4a 100644 --- a/x/upgrade/keeper/keeper.go +++ b/x/upgrade/keeper/keeper.go @@ -435,11 +435,6 @@ func (k Keeper) ReadUpgradeInfoFromDisk() (types.Plan, error) { return upgradeInfo, nil } -// SetVersionSetter upgrades versionSetter. -func (k *Keeper) SetVersionSetter(vs xp.ProtocolVersionSetter) { - k.versionSetter = vs -} - // SetDowngradeVerified updates downgradeVerified. func (k *Keeper) SetDowngradeVerified(v bool) { k.downgradeVerified = v diff --git a/x/upgrade/types/storeloader_test.go b/x/upgrade/types/storeloader_test.go index a8c34e4c4282..a54d20b5f58b 100644 --- a/x/upgrade/types/storeloader_test.go +++ b/x/upgrade/types/storeloader_test.go @@ -29,7 +29,7 @@ func defaultLogger() log.Logger { } } -func initStore(t *testing.T, db dbm.DBConnection, config multi.StoreParams, key storetypes.StoreKey, k, v []byte) { +func initStore(t *testing.T, db dbm.Connection, config multi.StoreParams, key storetypes.StoreKey, k, v []byte) { rs, err := multi.NewV1MultiStoreAsV2(db, config) require.NoError(t, err) rs.SetPruning(pruningtypes.NewPruningOptions(pruningtypes.PruningNothing)) @@ -44,7 +44,7 @@ func initStore(t *testing.T, db dbm.DBConnection, config multi.StoreParams, key require.NoError(t, rs.Close()) } -func checkStore(t *testing.T, db dbm.DBConnection, config multi.StoreParams, ver int64, key storetypes.StoreKey, k, v []byte) { +func checkStore(t *testing.T, db dbm.Connection, config multi.StoreParams, ver int64, key storetypes.StoreKey, k, v []byte) { rs, err := multi.NewV1MultiStoreAsV2(db, config) require.NoError(t, err) rs.SetPruning(pruningtypes.NewPruningOptions(pruningtypes.PruningNothing)) From eaa83be6fc4b49f553be7e954dab3d3e1b333ee4 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Wed, 20 Jul 2022 17:48:38 -0500 Subject: [PATCH 76/78] go.mod/sum --- go.mod | 1 + go.sum | 6 +----- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index e93d7d69a229..8a6e92d49e48 100644 --- a/go.mod +++ b/go.mod @@ -147,6 +147,7 @@ require ( github.com/golangci/revgrep v0.0.0-20210930125155-c22e5001d4f2 // indirect github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 // indirect github.com/google/btree v1.0.1 // indirect + github.com/google/flatbuffers v2.0.0+incompatible // indirect github.com/google/go-cmp v0.5.8 // indirect github.com/google/orderedcode v0.0.1 // indirect github.com/googleapis/gax-go/v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 3ee2381c1ff2..36be613d7dcc 100644 --- a/go.sum +++ b/go.sum @@ -276,14 +276,10 @@ github.com/cosmos/cosmos-proto v1.0.0-alpha7 h1:yqYUOHF2jopwZh4dVQp3xgqwftE5/2hk github.com/cosmos/cosmos-proto v1.0.0-alpha7/go.mod h1:dosO4pSAbJF8zWCzCoTWP7nNsjcvSUBQmniFxDg5daw= github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= -github.com/cosmos/iavl v0.18.0 h1:02ur4vnalMR2GuWCFNkuseUcl/BCVmg9tOeHOGiZOkE= -github.com/cosmos/iavl v0.18.0/go.mod h1:L0VZHfq0tqMNJvXlslGExaaiZM7eSm+90Vh9QUbp6j4= github.com/cosmos/gorocksdb v1.2.0 h1:d0l3jJG8M4hBouIZq0mDUHZ+zjOx044J3nGRskwTb4Y= -github.com/cosmos/gorocksdb v1.2.0/go.mod h1:aaKvKItm514hKfNJpUJXnnOWeBnk2GL4+Qw9NHizILw= -github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76 h1:DdzS1m6o/pCqeZ8VOAit/gyATedRgjvkVI+UCrLpyuU= -github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76/go.mod h1:0mkLWIoZuQ7uBoospo5Q9zIpqq6rYCPJDSUdeCJvPM8= github.com/cosmos/gorocksdb v1.2.0 h1:d0l3jJG8M4hBouIZq0mDUHZ+zjOx044J3nGRskwTb4Y= github.com/cosmos/gorocksdb v1.2.0/go.mod h1:aaKvKItm514hKfNJpUJXnnOWeBnk2GL4+Qw9NHizILw= +github.com/cosmos/gorocksdb v1.2.0/go.mod h1:aaKvKItm514hKfNJpUJXnnOWeBnk2GL4+Qw9NHizILw= github.com/cosmos/iavl v0.19.0 h1:sgyrjqOkycXiN7Tuupuo4QAldKFg7Sipyfeg/IL7cps= github.com/cosmos/iavl v0.19.0/go.mod h1:l5h9pAB3m5fihB3pXVgwYqdY8aBsMagqz7T0MUjxZeA= github.com/cosmos/ledger-cosmos-go v0.11.1 h1:9JIYsGnXP613pb2vPjFeMMjBI5lEDsEaF6oYorTy6J4= From c1e800cc430724f4b8372691f593e154e67a63a8 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Thu, 21 Jul 2022 09:25:42 -0500 Subject: [PATCH 77/78] PR clean up rm superfluous app.cms rename memdb imports [wip]substore prefix conflict --- baseapp/abci.go | 18 ++++++------ baseapp/baseapp.go | 16 +++++------ baseapp/baseapp_test.go | 43 +++++++++++++++-------------- baseapp/options.go | 6 ++-- baseapp/test_helpers.go | 4 +-- db/types/types.go | 1 - internal/db/tmdb_adapter.go | 2 +- server/export_test.go | 3 +- simapp/sim_test.go | 2 +- simapp/simd/cmd/root.go | 3 +- store/v2alpha1/multi/compat.go | 3 +- store/v2alpha1/multi/store.go | 4 +-- store/v2alpha1/multi/store_test.go | 9 +++--- store/v2alpha1/types.go | 2 +- x/upgrade/types/storeloader_test.go | 3 +- 15 files changed, 58 insertions(+), 61 deletions(-) diff --git a/baseapp/abci.go b/baseapp/abci.go index 273a35deb580..d29c79022630 100644 --- a/baseapp/abci.go +++ b/baseapp/abci.go @@ -44,7 +44,7 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC if req.InitialHeight > 1 { app.initialHeight = req.InitialHeight initHeader = tmproto.Header{ChainID: req.ChainId, Height: req.InitialHeight, Time: req.Time} - err := app.store.SetInitialVersion(uint64(req.InitialHeight)) + err := app.cms.SetInitialVersion(uint64(req.InitialHeight)) if err != nil { panic(err) } @@ -114,7 +114,7 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC // Info implements the ABCI interface. func (app *BaseApp) Info(req abci.RequestInfo) abci.ResponseInfo { - lastCommitID := app.store.LastCommitID() + lastCommitID := app.cms.LastCommitID() return abci.ResponseInfo{ Data: app.name, @@ -146,8 +146,8 @@ func (app *BaseApp) FilterPeerByID(info string) abci.ResponseQuery { // BeginBlock implements the ABCI application interface. func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeginBlock) { - if app.store.TracingEnabled() { - app.store.SetTracingContext(sdk.TraceContext( + if app.cms.TracingEnabled() { + app.cms.SetTracingContext(sdk.TraceContext( map[string]interface{}{"blockHeight": req.Header.Height}, )) } @@ -313,9 +313,9 @@ func (app *BaseApp) Commit() (res abci.ResponseCommit) { // Write the DeliverTx state into branched storage and commit the MultiStore. // The write to the DeliverTx state writes all state transitions to the root - // MultiStore (app.store) so when Commit() is called it persists those values. + // MultiStore (app.cms) so when Commit() is called it persists those values. app.deliverState.ms.Write() - commitID := app.store.Commit() + commitID := app.cms.Commit() app.logger.Info("commit synced", "commit", fmt.Sprintf("%X", commitID)) // Reset the Check state to the latest committed. @@ -624,7 +624,7 @@ func (app *BaseApp) createQueryContext(height int64, prove bool) (sdk.Context, e ) } - version, err := app.store.GetVersion(height) + version, err := app.cms.GetVersion(height) if err != nil { return sdk.Context{}, sdkerrors.Wrapf( @@ -778,12 +778,12 @@ func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) abci.Res func handleQueryStore(app *BaseApp, path []string, req abci.RequestQuery) abci.ResponseQuery { // "/store" prefix for store queries - queryable, ok := app.store.(sdk.Queryable) + queryable, ok := app.cms.(sdk.Queryable) if !ok { return sdkerrors.QueryResult(sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "multistore doesn't support queries"), app.trace) } - req.Path = "/" + strings.Join(path[1:], "/") // = /store/main/key -> /main/key + req.Path = "/" + strings.Join(path[1:], "/") if req.Height <= 1 && req.Prove { return sdkerrors.QueryResult( diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index eef3eb6a5300..d1077fe2ca78 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -67,7 +67,7 @@ type BaseApp struct { // nolint: maligned name string // application name from abci.Info db dbm.Connection storeOpts []StoreOption // options to configure root store - store sdk.CommitMultiStore // Main (uncached) state + cms sdk.CommitMultiStore // Main (uncached) state router sdk.Router // handle any kind of legacy message queryRouter sdk.QueryRouter // router for redirecting query calls grpcQueryRouter *GRPCQueryRouter // router for redirecting gRPC query calls @@ -241,7 +241,7 @@ func (app *BaseApp) loadStore() error { return err } } - app.store, err = multi.NewV1MultiStoreAsV2(app.db, config) + app.cms, err = multi.NewV1MultiStoreAsV2(app.db, config) if err != nil { return fmt.Errorf("failed to load store: %w", err) } @@ -257,17 +257,17 @@ func (app *BaseApp) SetMsgServiceRouter(msgServiceRouter *MsgServiceRouter) { } func (app *BaseApp) CloseStore() error { - return app.store.Close() + return app.cms.Close() } // LastCommitID returns the last CommitID of the multistore. func (app *BaseApp) LastCommitID() stypes.CommitID { - return app.store.LastCommitID() + return app.cms.LastCommitID() } // LastBlockHeight returns the last committed block height. func (app *BaseApp) LastBlockHeight() int64 { - return app.store.LastCommitID().Version + return app.cms.LastCommitID().Version } // Init initializes the app. It seals the app, preventing any @@ -282,7 +282,7 @@ func (app *BaseApp) Init() error { // needed for the export command which inits from store but never calls initchain app.setCheckState(tmproto.Header{}) app.Seal() - return app.store.GetPruning().Validate() + return app.cms.GetPruning().Validate() } func (app *BaseApp) setMinGasPrices(gasPrices sdk.DecCoins) { @@ -338,7 +338,7 @@ func (app *BaseApp) IsSealed() bool { return app.sealed } // provided header, and minimum gas prices set. It is set on InitChain and reset // on Commit. func (app *BaseApp) setCheckState(header tmproto.Header) { - ms := app.store.CacheWrap() + ms := app.cms.CacheWrap() app.checkState = &state{ ms: ms, ctx: sdk.NewContext(ms, header, true, app.logger).WithMinGasPrices(app.minGasPrices), @@ -350,7 +350,7 @@ func (app *BaseApp) setCheckState(header tmproto.Header) { // and provided header. It is set on InitChain and BeginBlock and set to nil on // Commit. func (app *BaseApp) setDeliverState(header tmproto.Header) { - ms := app.store.CacheWrap() + ms := app.cms.CacheWrap() app.deliverState = &state{ ms: ms, ctx: sdk.NewContext(ms, header, false, app.logger), diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index c830ef3ac04d..474e84e50464 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -21,7 +21,6 @@ import ( "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" dbm "github.com/cosmos/cosmos-sdk/db" - "github.com/cosmos/cosmos-sdk/db/memdb" pruningtypes "github.com/cosmos/cosmos-sdk/pruning/types" "github.com/cosmos/cosmos-sdk/snapshots" snapshottypes "github.com/cosmos/cosmos-sdk/snapshots/types" @@ -59,7 +58,7 @@ func defaultLogger() log.Logger { func newBaseApp(name string, options ...AppOption) *BaseApp { logger := defaultLogger() - db := memdb.NewDB() + db := dbm.NewMemDB() codec := codec.NewLegacyAmino() registerTestCodec(codec) return NewBaseApp(name, logger, db, testTxDecoder(codec), options...) @@ -91,7 +90,7 @@ func setupBaseApp(t *testing.T, options ...AppOption) *BaseApp { app := newBaseApp(t.Name(), options...) require.Equal(t, t.Name(), app.Name()) - app.SetParamStore(mock.NewParamStore(memdb.NewDB())) + app.SetParamStore(mock.NewParamStore(dbm.NewMemDB())) // stores are mounted err := app.Init() @@ -106,13 +105,13 @@ func setupBaseAppWithSnapshots(t *testing.T, config *setupConfig) (*BaseApp, err routerOpt := func(bapp *BaseApp) { bapp.Router().AddRoute(sdk.NewRoute(routeMsgKeyValue, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { kv := msg.(*msgKeyValue) - bapp.store.GetKVStore(capKey2).Set(kv.Key, kv.Value) + bapp.cms.GetKVStore(capKey2).Set(kv.Key, kv.Value) return &sdk.Result{}, nil })) } snapshotTimeout := 1 * time.Minute - snapshotStore, err := snapshots.NewStore(memdb.NewDB(), testutil.GetTempDir(t)) + snapshotStore, err := snapshots.NewStore(dbm.NewMemDB(), testutil.GetTempDir(t)) require.NoError(t, err) app := setupBaseApp(t, @@ -168,9 +167,9 @@ func TestMountStores(t *testing.T) { app := setupBaseApp(t) // check both stores - store1 := app.store.GetKVStore(capKey1) + store1 := app.cms.GetKVStore(capKey1) require.NotNil(t, store1) - store2 := app.store.GetKVStore(capKey2) + store2 := app.cms.GetKVStore(capKey2) require.NotNil(t, store2) } @@ -178,7 +177,7 @@ func TestMountStores(t *testing.T) { func TestLoadVersion(t *testing.T) { logger := defaultLogger() pruningOpt := SetPruning(pruningtypes.NewPruningOptions(pruningtypes.PruningNothing)) - db := memdb.NewDB() + db := dbm.NewMemDB() name := t.Name() app := NewBaseApp(name, logger, db, nil, pruningOpt) @@ -250,7 +249,7 @@ func checkStore(t *testing.T, db dbm.Connection, ver int64, storeKey string, k, func TestVersionSetterGetter(t *testing.T) { logger := defaultLogger() pruningOpt := SetPruning(pruningtypes.NewPruningOptions(pruningtypes.PruningDefault)) - db := memdb.NewDB() + db := dbm.NewMemDB() name := t.Name() app := NewBaseApp(name, logger, db, nil, pruningOpt) require.Equal(t, "", app.Version()) @@ -272,7 +271,7 @@ func TestLoadVersionPruning(t *testing.T) { pruningOpt := SetPruning(pruningOptions) capKey := sdk.NewKVStoreKey("key1") schemaOpt := SetSubstores(capKey) - db := memdb.NewDB() + db := dbm.NewMemDB() name := t.Name() app := NewBaseApp(name, logger, db, nil, pruningOpt) @@ -300,12 +299,14 @@ func TestLoadVersionPruning(t *testing.T) { // TODO: behavior change - // CacheMultiStoreWithVersion returned no error on missing version (?) for _, v := range []int64{1, 2, 4} { - _, err = app.store.GetVersion(v) + s, err := app.cms.GetVersion(v) + require.NotNil(t, s) require.NoError(t, err, "version=%v", v) } for _, v := range []int64{3, 5, 6, 7} { - _, err = app.store.GetVersion(v) + s, err := app.cms.GetVersion(v) + require.NotNil(t, s) require.NoError(t, err, "version=%v", v) } require.NoError(t, app.CloseStore()) @@ -327,7 +328,7 @@ func testLoadVersionHelper(t *testing.T, app *BaseApp, expectedHeight int64, exp func TestOptionFunction(t *testing.T) { logger := defaultLogger() - db := memdb.NewDB() + db := dbm.NewMemDB() bap := NewBaseApp("starting name", logger, db, nil, testChangeNameHelper("new name")) require.Equal(t, "new name", bap.Name(), "BaseApp should have had name changed via option function") } @@ -416,7 +417,7 @@ func TestInitChainer(t *testing.T) { name := t.Name() // keep the db and logger ourselves so // we can reload the same app later - db := memdb.NewDB() + db := dbm.NewMemDB() logger := defaultLogger() capKey := sdk.NewKVStoreKey("main") capKey2 := sdk.NewKVStoreKey("key2") @@ -497,7 +498,7 @@ func TestInitChainer(t *testing.T) { func TestInitChain_WithInitialHeight(t *testing.T) { name := t.Name() - db := memdb.NewDB() + db := dbm.NewMemDB() logger := defaultLogger() app := NewBaseApp(name, logger, db, nil) @@ -513,7 +514,7 @@ func TestInitChain_WithInitialHeight(t *testing.T) { func TestBeginBlock_WithInitialHeight(t *testing.T) { name := t.Name() - db := memdb.NewDB() + db := dbm.NewMemDB() logger := defaultLogger() app := NewBaseApp(name, logger, db, nil) @@ -1963,7 +1964,7 @@ func TestWithRouter(t *testing.T) { } func TestBaseApp_EndBlock(t *testing.T) { - db := memdb.NewDB() + db := dbm.NewMemDB() name := t.Name() logger := defaultLogger() @@ -1974,7 +1975,7 @@ func TestBaseApp_EndBlock(t *testing.T) { } app := NewBaseApp(name, logger, db, nil) - app.SetParamStore(mock.NewParamStore(memdb.NewDB())) + app.SetParamStore(mock.NewParamStore(dbm.NewMemDB())) app.InitChain(abci.RequestInitChain{ ConsensusParams: cp, }) @@ -1995,11 +1996,11 @@ func TestBaseApp_EndBlock(t *testing.T) { } func TestBaseApp_Init(t *testing.T) { - db := memdb.NewDB() + db := dbm.NewMemDB() name := t.Name() logger := defaultLogger() - snapshotStore, err := snapshots.NewStore(memdb.NewDB(), testutil.GetTempDir(t)) + snapshotStore, err := snapshots.NewStore(dbm.NewMemDB(), testutil.GetTempDir(t)) require.NoError(t, err) testCases := map[string]struct { @@ -2140,7 +2141,7 @@ func TestBaseApp_Init(t *testing.T) { } // Check that settings were set correctly - actualPruning := tc.bapp.store.GetPruning() + actualPruning := tc.bapp.cms.GetPruning() require.Equal(t, tc.expectedPruning, actualPruning) if tc.expectedSnapshot.Interval == snapshottypes.SnapshotIntervalOff { diff --git a/baseapp/options.go b/baseapp/options.go index f6c3498fd978..bbbaf16ea5e6 100644 --- a/baseapp/options.go +++ b/baseapp/options.go @@ -232,8 +232,8 @@ func (app *BaseApp) SetSnapshot(snapshotStore *snapshots.Store, opts snapshottyp app.snapshotManager = nil return } - app.store.SetSnapshotInterval(opts.Interval) - app.snapshotManager = snapshots.NewManager(snapshotStore, opts, app.store, nil, app.logger) + app.cms.SetSnapshotInterval(opts.Interval) + app.snapshotManager = snapshots.NewManager(snapshotStore, opts, app.cms, nil, app.logger) } // SetInterfaceRegistry sets the InterfaceRegistry. @@ -247,7 +247,7 @@ func (app *BaseApp) SetInterfaceRegistry(registry types.InterfaceRegistry) { func (app *BaseApp) SetStreamingService(s StreamingService) { // add the listeners for each StoreKey for key, lis := range s.Listeners() { - app.store.AddListeners(key, lis) + app.cms.AddListeners(key, lis) } // register the StreamingService within the BaseApp // BaseApp will pass BeginBlock, DeliverTx, and EndBlock requests and responses to the streaming services to update their ABCI context diff --git a/baseapp/test_helpers.go b/baseapp/test_helpers.go index 08180075ce6b..49081943a2b3 100644 --- a/baseapp/test_helpers.go +++ b/baseapp/test_helpers.go @@ -47,12 +47,12 @@ func (app *BaseApp) NewContext(isCheckTx bool, header tmproto.Header) sdk.Contex } func (app *BaseApp) NewUncachedContext(isCheckTx bool, header tmproto.Header) sdk.Context { - return sdk.NewContext(app.store, header, isCheckTx, app.logger) + return sdk.NewContext(app.cms, header, isCheckTx, app.logger) } // NewContextAt creates a context using a (read-only) store at a given block height. func (app *BaseApp) NewContextAt(isCheckTx bool, header tmproto.Header, height int64) (sdk.Context, error) { - view, err := app.store.GetVersion(height) + view, err := app.cms.GetVersion(height) if err != nil { return sdk.Context{}, err } diff --git a/db/types/types.go b/db/types/types.go index 8d024c175ea2..39e48eab0ed6 100644 --- a/db/types/types.go +++ b/db/types/types.go @@ -50,7 +50,6 @@ type Connection interface { // SaveNextVersion saves the current contents of the database and returns the next version ID, // which will be `Versions().Last()+1`. // Returns an error if any open Writer transactions exist. - // TODO: rename to something more descriptive? SaveNextVersion() (uint64, error) // SaveVersion attempts to save database at a specific version ID, which must be greater than or diff --git a/internal/db/tmdb_adapter.go b/internal/db/tmdb_adapter.go index f5e1805f968e..0bece64cf675 100644 --- a/internal/db/tmdb_adapter.go +++ b/internal/db/tmdb_adapter.go @@ -8,7 +8,7 @@ package db import ( "errors" - dbm "github.com/cosmos/cosmos-sdk/db" + dbm "github.com/cosmos/cosmos-sdk/db/types" tmdb "github.com/tendermint/tm-db" ) diff --git a/server/export_test.go b/server/export_test.go index 21c800de63cb..133a1b167187 100644 --- a/server/export_test.go +++ b/server/export_test.go @@ -21,7 +21,6 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" dbm "github.com/cosmos/cosmos-sdk/db" - "github.com/cosmos/cosmos-sdk/db/memdb" "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/server/types" "github.com/cosmos/cosmos-sdk/simapp" @@ -128,7 +127,7 @@ func setupApp(t *testing.T, tempDir string) (*simapp.SimApp, context.Context, *t } logger, _ := log.NewDefaultLogger("plain", "info", false) - db := memdb.NewDB() + db := dbm.NewMemDB() encCfg := simapp.MakeTestEncodingConfig() app := simapp.NewSimApp(logger, db, nil, encCfg, simtestutil.NewAppOptionsWithFlagHome(tempDir)) diff --git a/simapp/sim_test.go b/simapp/sim_test.go index 879f65b3f7af..1d02fd7c1092 100644 --- a/simapp/sim_test.go +++ b/simapp/sim_test.go @@ -353,7 +353,7 @@ func TestAppStateDeterminism(t *testing.T) { require.NoError(t, err) if config.Commit { - PrintStats(db) //TODO + PrintStats(db) } appHash := app.LastCommitID().Hash diff --git a/simapp/simd/cmd/root.go b/simapp/simd/cmd/root.go index aa1ed0624cd0..a316f2b1a172 100644 --- a/simapp/simd/cmd/root.go +++ b/simapp/simd/cmd/root.go @@ -21,7 +21,6 @@ import ( "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/client/rpc" dbm "github.com/cosmos/cosmos-sdk/db" - "github.com/cosmos/cosmos-sdk/db/badgerdb" "github.com/cosmos/cosmos-sdk/server" serverconfig "github.com/cosmos/cosmos-sdk/server/config" servertypes "github.com/cosmos/cosmos-sdk/server/types" @@ -269,7 +268,7 @@ func (a appCreator) newApp( } snapshotDir := filepath.Join(cast.ToString(appOpts.Get(flags.FlagHome)), "data", "snapshots") - snapshotDB, err := badgerdb.NewDB(filepath.Join(snapshotDir, "metadata")) + snapshotDB, err := dbm.NewDB("metadata", dbm.BadgerDBBackend, snapshotDir) if err != nil { panic(err) } diff --git a/store/v2alpha1/multi/compat.go b/store/v2alpha1/multi/compat.go index 7047e69b7c4a..07f0088a7d5d 100644 --- a/store/v2alpha1/multi/compat.go +++ b/store/v2alpha1/multi/compat.go @@ -104,8 +104,7 @@ func (st *compatStore) LoadVersionAndUpgrade(ver int64, upgrades *v1.StoreUpgrad } func (st *compatStore) LoadVersion(ver int64) error { - // TODO - // cache a viewStore representing "current" version? + // TODO: could cache a viewStore representing "current" version panic("unsupported: LoadVersion") } diff --git a/store/v2alpha1/multi/store.go b/store/v2alpha1/multi/store.go index f14cfb29dd9b..d69c5d2e46c9 100644 --- a/store/v2alpha1/multi/store.go +++ b/store/v2alpha1/multi/store.go @@ -68,7 +68,7 @@ type StoreParams struct { // Contains the store schema and methods to modify it SchemaBuilder storeKeys - // Inter-block persistent cache to use. TODO: not implemented + // Inter-block persistent cache to use. TODO: not used/impl'd PersistentCache types.MultiStorePersistentCache // Any pending upgrades to apply on loading. Upgrades *types.StoreUpgrades @@ -940,7 +940,7 @@ func (reg *SchemaBuilder) registerName(key string, typ types.StoreType) error { if has { return fmt.Errorf("name already exists: %v", key) } - // TODO auth vs authz ? + // // Prefix conflict check: disabled; obviated by varint encoding // if i > 0 && strings.HasPrefix(key, reg.reserved[i-1]) { // return fmt.Errorf("name conflict: '%v' exists, cannot add '%v'", reg.reserved[i-1], key) // } diff --git a/store/v2alpha1/multi/store_test.go b/store/v2alpha1/multi/store_test.go index 662d5872cac3..51b2570a0884 100644 --- a/store/v2alpha1/multi/store_test.go +++ b/store/v2alpha1/multi/store_test.go @@ -79,13 +79,14 @@ func TestStoreParams(t *testing.T) { require.Error(t, opts.RegisterSubstore(skey_1, types.StoreTypeTransient)) require.NoError(t, opts.RegisterSubstore(skey_mem1, types.StoreTypeMemory)) require.NoError(t, opts.RegisterSubstore(skey_tran1, types.StoreTypeTransient)) - // Ensure that no prefix conflicts are allowed + // Unambiguous prefixes are valid require.NoError(t, opts.RegisterSubstore(skey_1, types.StoreTypePersistent)) require.NoError(t, opts.RegisterSubstore(skey_2, types.StoreTypePersistent)) require.NoError(t, opts.RegisterSubstore(skey_3b, types.StoreTypePersistent)) - require.Error(t, opts.RegisterSubstore(skey_1b, types.StoreTypePersistent)) - require.Error(t, opts.RegisterSubstore(skey_2b, types.StoreTypePersistent)) - require.Error(t, opts.RegisterSubstore(skey_3, types.StoreTypePersistent)) + // Prefix conflicts are allowed + // require.Error(t, opts.RegisterSubstore(skey_1b, types.StoreTypePersistent)) + // require.Error(t, opts.RegisterSubstore(skey_2b, types.StoreTypePersistent)) + // require.Error(t, opts.RegisterSubstore(skey_3, types.StoreTypePersistent)) } func TestMultiStoreBasic(t *testing.T) { diff --git a/store/v2alpha1/types.go b/store/v2alpha1/types.go index c0a67bb762e7..fed95acb088d 100644 --- a/store/v2alpha1/types.go +++ b/store/v2alpha1/types.go @@ -104,5 +104,5 @@ type CacheMultiStore interface { } // MultiStorePersistentCache provides inter-block (persistent) caching capabilities for a CommitMultiStore. -// TODO: placeholder. Implement and redefine this +// TODO: placeholder, not implemented yet, nor used in store type MultiStorePersistentCache = v1.MultiStorePersistentCache diff --git a/x/upgrade/types/storeloader_test.go b/x/upgrade/types/storeloader_test.go index a54d20b5f58b..2b6d7d6b9236 100644 --- a/x/upgrade/types/storeloader_test.go +++ b/x/upgrade/types/storeloader_test.go @@ -14,7 +14,6 @@ import ( "github.com/cosmos/cosmos-sdk/baseapp" dbm "github.com/cosmos/cosmos-sdk/db" - "github.com/cosmos/cosmos-sdk/db/memdb" pruningtypes "github.com/cosmos/cosmos-sdk/pruning/types" "github.com/cosmos/cosmos-sdk/server" storetypes "github.com/cosmos/cosmos-sdk/store/types" @@ -119,7 +118,7 @@ func TestSetLoader(t *testing.T) { require.NoError(t, loadConfig.RegisterSubstore(tc.loadStoreKey, storetypes.StoreTypePersistent)) // prepare a db with some data - db := memdb.NewDB() + db := dbm.NewMemDB() initStore(t, db, origConfig, tc.origStoreKey, k, v) // load the app with the existing db From 1a9e194865d256b1f45f54db89ecfb282fdf5651 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Thu, 21 Jul 2022 16:28:45 -0500 Subject: [PATCH 78/78] v2 store: allow shared substore name prefixes encode names with varint length to make them unambiguous --- store/v2alpha1/multi/store.go | 57 +++++++++++++++++++++--------- store/v2alpha1/multi/store_test.go | 46 +++++++++++++++++++----- store/v2alpha1/multi/view_store.go | 2 +- 3 files changed, 79 insertions(+), 26 deletions(-) diff --git a/store/v2alpha1/multi/store.go b/store/v2alpha1/multi/store.go index d69c5d2e46c9..7a42fd33683d 100644 --- a/store/v2alpha1/multi/store.go +++ b/store/v2alpha1/multi/store.go @@ -1,6 +1,7 @@ package multi import ( + "encoding/binary" "errors" "fmt" "io" @@ -47,9 +48,8 @@ var ( // Per-substore prefixes substoreMerkleRootKey = []byte{0} // Key for root hashes of Merkle trees - dataPrefix = []byte{1} // Prefix for state mappings - indexPrefix = []byte{2} // Prefix for Store reverse index - smtPrefix = []byte{3} // Prefix for SMT data + dataPrefix = []byte{1} // Prefix for store data + smtPrefix = []byte{2} // Prefix for tree data ) func ErrStoreNotFound(key string) error { @@ -371,14 +371,16 @@ func migrateData(store *Store, upgrades types.StoreUpgrades) error { } for _, key := range upgrades.Deleted { - pfx := substorePrefix(key) + pfx := prefixSubstore(key) subReader := prefixdb.NewReader(reader, pfx) it, err := subReader.Iterator(nil, nil) if err != nil { return err } for it.Next() { - store.stateTxn.Delete(it.Key()) + if err = store.stateTxn.Delete(it.Key()); err != nil { + return err + } } it.Close() if store.StateCommitmentDB != nil { @@ -390,12 +392,14 @@ func migrateData(store *Store, upgrades types.StoreUpgrades) error { for it.Next() { store.stateCommitmentTxn.Delete(it.Key()) } - it.Close() + if err = it.Close(); err != nil { + return err + } } } for _, rename := range upgrades.Renamed { - oldPrefix := substorePrefix(rename.OldKey) - newPrefix := substorePrefix(rename.NewKey) + oldPrefix := prefixSubstore(rename.OldKey) + newPrefix := prefixSubstore(rename.NewKey) subReader := prefixdb.NewReader(reader, oldPrefix) subWriter := prefixdb.NewWriter(store.stateTxn, newPrefix) it, err := subReader.Iterator(nil, nil) @@ -405,7 +409,9 @@ func migrateData(store *Store, upgrades types.StoreUpgrades) error { for it.Next() { subWriter.Set(it.Key(), it.Value()) } - it.Close() + if it.Close(); err != nil { + return err + } if store.StateCommitmentDB != nil { subReader = prefixdb.NewReader(scReader, oldPrefix) subWriter = prefixdb.NewWriter(store.stateCommitmentTxn, newPrefix) @@ -416,14 +422,30 @@ func migrateData(store *Store, upgrades types.StoreUpgrades) error { for it.Next() { subWriter.Set(it.Key(), it.Value()) } - it.Close() + if it.Close(); err != nil { + return err + } } } return nil } -func substorePrefix(key string) []byte { - return append(contentPrefix, key...) +// encode key length as varint +func varintLen(l int) []byte { + buf := make([]byte, binary.MaxVarintLen64) + n := binary.PutUvarint(buf, uint64(l)) + return buf[:n] +} + +func prefixSubstore(key string) []byte { + lv := varintLen(len(key)) + ret := append(lv, key...) + return append(contentPrefix, ret...) +} + +func prefixNonpersistent(key string) []byte { + lv := varintLen(len(key)) + return append(lv, key...) } // GetKVStore implements MultiStore. @@ -443,9 +465,10 @@ func (s *Store) GetKVStore(skey types.StoreKey) types.KVStore { default: panic(fmt.Errorf("StoreType not supported: %v", typ)) // should never happen } + var ret types.KVStore if parent != nil { // store is non-persistent - ret = prefix.NewStore(parent, []byte(key)) + ret = prefix.NewStore(parent, prefixNonpersistent(key)) } else { // store is persistent sub, err := s.getSubstore(key) if err != nil { @@ -471,7 +494,7 @@ func (s *Store) getSubstore(key string) (*substore, error) { if cached, has := s.substoreCache[key]; has { return cached, nil } - pfx := substorePrefix(key) + pfx := prefixSubstore(key) stateRW := prefixdb.NewReadWriter(s.stateTxn, pfx) stateCommitmentRW := prefixdb.NewReadWriter(s.stateCommitmentTxn, pfx) var stateCommitmentStore *smt.Store @@ -497,7 +520,7 @@ func (s *Store) getSubstore(key string) (*substore, error) { // Resets a substore's state after commit (because root stateTxn has been discarded) func (s *substore) refresh(rootHash []byte) { - pfx := substorePrefix(s.name) + pfx := prefixSubstore(s.name) stateRW := prefixdb.NewReadWriter(s.root.stateTxn, pfx) stateCommitmentRW := prefixdb.NewReadWriter(s.root.stateCommitmentTxn, pfx) s.dataBucket = prefixdb.NewReadWriter(stateRW, dataPrefix) @@ -600,7 +623,7 @@ func (s *Store) commit(target uint64) (id *types.CommitID, err error) { } // Update substore Merkle roots for key, storeHash := range storeHashes { - w := prefixdb.NewReadWriter(s.stateTxn, substorePrefix(key)) + w := prefixdb.NewReadWriter(s.stateTxn, prefixSubstore(key)) if err = w.Set(substoreMerkleRootKey, storeHash); err != nil { return } @@ -940,7 +963,7 @@ func (reg *SchemaBuilder) registerName(key string, typ types.StoreType) error { if has { return fmt.Errorf("name already exists: %v", key) } - // // Prefix conflict check: disabled; obviated by varint encoding + // TODO auth vs authz ? // if i > 0 && strings.HasPrefix(key, reg.reserved[i-1]) { // return fmt.Errorf("name conflict: '%v' exists, cannot add '%v'", reg.reserved[i-1], key) // } diff --git a/store/v2alpha1/multi/store_test.go b/store/v2alpha1/multi/store_test.go index 51b2570a0884..ff97c0dadec5 100644 --- a/store/v2alpha1/multi/store_test.go +++ b/store/v2alpha1/multi/store_test.go @@ -74,7 +74,7 @@ func TestStoreParams(t *testing.T) { // Fail with invalid type enum require.Error(t, opts.RegisterSubstore(skey_1, types.StoreTypeDB)) require.Error(t, opts.RegisterSubstore(skey_1, types.StoreTypeSMT)) - // Mem & tranient stores require a bespoke concrete type + // Mem & tranient stores need corresponding concrete type require.Error(t, opts.RegisterSubstore(skey_1, types.StoreTypeMemory)) require.Error(t, opts.RegisterSubstore(skey_1, types.StoreTypeTransient)) require.NoError(t, opts.RegisterSubstore(skey_mem1, types.StoreTypeMemory)) @@ -83,10 +83,10 @@ func TestStoreParams(t *testing.T) { require.NoError(t, opts.RegisterSubstore(skey_1, types.StoreTypePersistent)) require.NoError(t, opts.RegisterSubstore(skey_2, types.StoreTypePersistent)) require.NoError(t, opts.RegisterSubstore(skey_3b, types.StoreTypePersistent)) - // Prefix conflicts are allowed - // require.Error(t, opts.RegisterSubstore(skey_1b, types.StoreTypePersistent)) - // require.Error(t, opts.RegisterSubstore(skey_2b, types.StoreTypePersistent)) - // require.Error(t, opts.RegisterSubstore(skey_3, types.StoreTypePersistent)) + // Prefixes with conflicts are also allowed + require.NoError(t, opts.RegisterSubstore(skey_1b, types.StoreTypePersistent)) + require.NoError(t, opts.RegisterSubstore(skey_2b, types.StoreTypePersistent)) + require.NoError(t, opts.RegisterSubstore(skey_3, types.StoreTypePersistent)) } func TestMultiStoreBasic(t *testing.T) { @@ -95,8 +95,7 @@ func TestMultiStoreBasic(t *testing.T) { func doTestMultiStoreBasic(t *testing.T, ctor storeConstructor) { opts := DefaultStoreParams() - err := opts.RegisterSubstore(skey_1, types.StoreTypePersistent) - require.NoError(t, err) + require.NoError(t, opts.RegisterSubstore(skey_1, types.StoreTypePersistent)) store, err := ctor(memdb.NewDB(), opts) require.NoError(t, err) @@ -114,6 +113,37 @@ func doTestMultiStoreBasic(t *testing.T, ctor storeConstructor) { require.Equal(t, []byte(nil), val) } +func TestSubstoreBasic(t *testing.T) { + badkey := skey_1.Name() + string(dataPrefix) + skey_bad := types.NewKVStoreKey(badkey) + + opts := DefaultStoreParams() + require.NoError(t, opts.RegisterSubstore(skey_1, types.StoreTypePersistent)) + require.NoError(t, opts.RegisterSubstore(skey_bad, types.StoreTypePersistent)) + store, err := NewStore(memdb.NewDB(), opts) + require.NoError(t, err) + + // Test that substores do not leak into those with conflicting prefixes + // i.e., some unambiguous encoding of store keys is used + store_bad := store.GetKVStore(skey_bad) + require.NotNil(t, store_bad) + store_bad.Set([]byte("1bad"), []byte{0x1b}) + require.Equal(t, []byte{0x1b}, store_bad.Get([]byte("1bad"))) + + store_1 := store.GetKVStore(skey_1) + require.NotNil(t, store_1) + store_1.Set([]byte{0}, []byte{0}) + + count := 0 + it := store_1.Iterator(nil, nil) + for ; it.Valid(); it.Next() { + require.Equal(t, []byte{0}, it.Key()) + count++ + } + require.NoError(t, it.Close()) + require.Equal(t, 1, count) +} + func TestGetSetHasDelete(t *testing.T) { _, store := newSubStoreWithData(t, memdb.NewDB(), alohaData) key := "hello" @@ -253,7 +283,7 @@ func TestConstructors(t *testing.T) { require.NoError(t, store.Close()) // ...whether because root is misssing w = db.Writer() - s1RootKey := append(contentPrefix, substorePrefix(skey_1.Name())...) + s1RootKey := append(contentPrefix, prefixSubstore(skey_1.Name())...) s1RootKey = append(s1RootKey, merkleRootKey...) w.Delete(s1RootKey) w.Commit() diff --git a/store/v2alpha1/multi/view_store.go b/store/v2alpha1/multi/view_store.go index 13295a1a3577..9420ccc68b92 100644 --- a/store/v2alpha1/multi/view_store.go +++ b/store/v2alpha1/multi/view_store.go @@ -55,7 +55,7 @@ func (s *viewStore) getSubstore(key string) (*viewSubstore, error) { if cached, has := s.substoreCache[key]; has { return cached, nil } - pfx := substorePrefix(key) + pfx := prefixSubstore(key) stateR := prefixdb.NewReader(s.stateView, pfx) stateCommitmentR := prefixdb.NewReader(s.stateCommitmentView, pfx) rootHash, err := stateR.Get(merkleRootKey)