refactor(store/v2): simplify genesis flow (#22435)

Co-authored-by: Marko <marko@baricevic.me>
This commit is contained in:
Matt Kocubinski 2024-11-08 08:05:25 -06:00 committed by GitHub
parent 406f977be0
commit 43e28b43ad
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
44 changed files with 279 additions and 522 deletions

View File

@ -36,6 +36,10 @@ Ref: https://keepachangelog.com/en/1.0.0/
## [Unreleased]
### API Breaking
* [#22435](https://github.com/cosmos/cosmos-sdk/pull/22435) Add `Version uint64` field to `store.Changeset` and update `Changeset` constructors to accept a `version uint64` as their first argument.
### Features
* [#22326](https://github.com/cosmos/cosmos-sdk/pull/22326) Introduce codec package in order to facilitate removal of Cosmos SDK dependency in modules.

View File

@ -6,6 +6,7 @@ import (
// Changeset is a list of changes to be written to disk
type Changeset struct {
Version uint64
Changes []StateChanges
}
@ -29,11 +30,11 @@ type KVPair = struct {
Remove bool
}
func NewChangeset() *Changeset {
return &Changeset{}
func NewChangeset(version uint64) *Changeset {
return &Changeset{Version: version}
}
func NewChangesetWithPairs(pairs map[string]KVPairs) *Changeset {
func NewChangesetWithPairs(version uint64, pairs map[string]KVPairs) *Changeset {
changes := make([]StateChanges, len(pairs))
i := 0
for storeKey, kvPairs := range pairs {
@ -44,6 +45,7 @@ func NewChangesetWithPairs(pairs map[string]KVPairs) *Changeset {
i++
}
return &Changeset{
Version: version,
Changes: changes,
}
}

View File

@ -5,6 +5,7 @@ go 1.23
// server v2 integration
replace (
cosmossdk.io/api => ../../api
cosmossdk.io/core => ../../core
cosmossdk.io/core/testing => ../../core/testing
cosmossdk.io/server/v2/appmanager => ../../server/v2/appmanager
cosmossdk.io/server/v2/stf => ../../server/v2/stf

View File

@ -2,8 +2,6 @@ buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.35.1-20240701160653-fed
buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.35.1-20240701160653-fedbb9acfd2f.1/go.mod h1:JTBMfyi+qAXUHumX+rcD2WIq9FNWmdcNh5MjBnSw0L0=
buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.35.1-20240130113600-88ef6483f90f.1 h1:F78ecjvMtgd1aZ1Aj9cvBjURxVGCYvRM+OOy5eR+pjw=
buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.35.1-20240130113600-88ef6483f90f.1/go.mod h1:zqi/LZjZhyvjCMTEVIwAf5VRlkLduuCfqmZxgoormq0=
cosmossdk.io/core v1.0.0-alpha.5 h1:McjYXAQ6XcT20v2uHyH7PhoWH8V+mebzfVFqT3GinsI=
cosmossdk.io/core v1.0.0-alpha.5/go.mod h1:3u9cWq1FAVtiiCrDPpo4LhR+9V6k/ycSG4/Y/tREWCY=
cosmossdk.io/depinject v1.1.0 h1:wLan7LG35VM7Yo6ov0jId3RHWCGRhe8E8bsuARorl5E=
cosmossdk.io/depinject v1.1.0/go.mod h1:kkI5H9jCGHeKeYWXTqYdruogYrEeWvBQCw1Pj4/eCFI=
cosmossdk.io/errors/v2 v2.0.0-20240731132947-df72853b3ca5 h1:IQNdY2kB+k+1OM2DvqFG1+UgeU1JzZrWtwuWzI3ZfwA=

View File

@ -329,7 +329,7 @@ func (c *Consensus[T]) InitChain(ctx context.Context, req *abciproto.InitChainRe
IsGenesis: true,
}
blockresponse, genesisState, err := c.app.InitGenesis(
blockResponse, genesisState, err := c.app.InitGenesis(
ctx,
br,
req.AppStateBytes,
@ -338,17 +338,16 @@ func (c *Consensus[T]) InitChain(ctx context.Context, req *abciproto.InitChainRe
return nil, fmt.Errorf("genesis state init failure: %w", err)
}
for _, txRes := range blockresponse.TxResults {
for _, txRes := range blockResponse.TxResults {
if err := txRes.Error; err != nil {
space, code, log := errorsmod.ABCIInfo(err, c.cfg.AppTomlConfig.Trace)
c.logger.Warn("genesis tx failed", "codespace", space, "code", code, "log", log)
space, code, txLog := errorsmod.ABCIInfo(err, c.cfg.AppTomlConfig.Trace)
c.logger.Warn("genesis tx failed", "codespace", space, "code", code, "log", txLog)
}
}
validatorUpdates := intoABCIValidatorUpdates(blockresponse.ValidatorUpdates)
validatorUpdates := intoABCIValidatorUpdates(blockResponse.ValidatorUpdates)
// set the initial version of the store
if err := c.store.SetInitialVersion(uint64(req.InitialHeight)); err != nil {
if err := c.store.SetInitialVersion(uint64(req.InitialHeight - 1)); err != nil {
return nil, fmt.Errorf("failed to set initial version: %w", err)
}
@ -357,9 +356,10 @@ func (c *Consensus[T]) InitChain(ctx context.Context, req *abciproto.InitChainRe
return nil, err
}
cs := &store.Changeset{
Version: uint64(req.InitialHeight - 1),
Changes: stateChanges,
}
stateRoot, err := c.store.WorkingHash(cs)
stateRoot, err := c.store.Commit(cs)
if err != nil {
return nil, fmt.Errorf("unable to write the changeset: %w", err)
}
@ -455,18 +455,6 @@ func (c *Consensus[T]) FinalizeBlock(
return nil, err
}
// we don't need to deliver the block in the genesis block
if req.Height == int64(c.initialHeight) {
appHash, err := c.store.Commit(store.NewChangeset())
if err != nil {
return nil, fmt.Errorf("unable to commit the changeset: %w", err)
}
c.lastCommittedHeight.Store(req.Height)
return &abciproto.FinalizeBlockResponse{
AppHash: appHash,
}, nil
}
// TODO(tip): can we expect some txs to not decode? if so, what we do in this case? this does not seem to be the case,
// considering that prepare and process always decode txs, assuming they're the ones providing txs we should never
// have a tx that fails decoding.
@ -507,7 +495,7 @@ func (c *Consensus[T]) FinalizeBlock(
if err != nil {
return nil, err
}
appHash, err := c.store.Commit(&store.Changeset{Changes: stateChanges})
appHash, err := c.store.Commit(&store.Changeset{Version: uint64(req.Height), Changes: stateChanges})
if err != nil {
return nil, fmt.Errorf("unable to commit the changeset: %w", err)
}

View File

@ -32,6 +32,7 @@ import (
var (
sum = sha256.Sum256([]byte("test-hash"))
emptyHash = sha256.Sum256([]byte(""))
DefaulConsensusParams = &v1.ConsensusParams{
Block: &v1.BlockParams{
MaxGas: 5000000,
@ -124,6 +125,7 @@ func TestConsensus_InitChain_Without_UpdateParam(t *testing.T) {
_, err = c.FinalizeBlock(context.Background(), &abciproto.FinalizeBlockRequest{
Time: time.Now(),
Height: 1,
Hash: emptyHash[:],
})
require.NoError(t, err)
assertStoreLatestVersion(t, mockStore, 1)
@ -144,6 +146,7 @@ func TestConsensus_InitChain_With_UpdateParam(t *testing.T) {
_, err = c.FinalizeBlock(context.Background(), &abciproto.FinalizeBlockRequest{
Time: time.Now(),
Height: 1,
Hash: emptyHash[:],
})
require.NoError(t, err)
@ -159,15 +162,16 @@ func TestConsensus_InitChain_Invalid_Height(t *testing.T) {
InitialHeight: 2,
})
require.NoError(t, err)
assertStoreLatestVersion(t, mockStore, 0)
assertStoreLatestVersion(t, mockStore, 1)
// Shouldn't be able to commit genesis block 2
// Shouldn't be able to commit genesis block 3
_, err = c.FinalizeBlock(context.Background(), &abciproto.FinalizeBlockRequest{
Time: time.Now(),
Height: 2,
Height: 3,
Hash: emptyHash[:],
})
require.Error(t, err)
require.True(t, strings.Contains(err.Error(), "unable to commit the changeset"))
require.True(t, strings.Contains(err.Error(), "invalid height"))
}
func TestConsensus_FinalizeBlock_Invalid_Height(t *testing.T) {
@ -182,12 +186,14 @@ func TestConsensus_FinalizeBlock_Invalid_Height(t *testing.T) {
_, err = c.FinalizeBlock(context.Background(), &abciproto.FinalizeBlockRequest{
Time: time.Now(),
Height: 1,
Hash: emptyHash[:],
})
require.NoError(t, err)
_, err = c.FinalizeBlock(context.Background(), &abciproto.FinalizeBlockRequest{
Time: time.Now(),
Height: 3,
Hash: emptyHash[:],
})
require.Error(t, err)
}
@ -206,6 +212,7 @@ func TestConsensus_FinalizeBlock_NoTxs(t *testing.T) {
_, err = c.FinalizeBlock(context.Background(), &abciproto.FinalizeBlockRequest{
Time: time.Now(),
Height: 1,
Hash: emptyHash[:],
})
require.NoError(t, err)
@ -236,6 +243,7 @@ func TestConsensus_FinalizeBlock_MultiTxs_OutOfGas(t *testing.T) {
_, err = c.FinalizeBlock(context.Background(), &abciproto.FinalizeBlockRequest{
Time: time.Now(),
Height: 1,
Hash: emptyHash[:],
})
require.NoError(t, err)
@ -267,6 +275,7 @@ func TestConsensus_FinalizeBlock_MultiTxs(t *testing.T) {
_, err = c.FinalizeBlock(context.Background(), &abciproto.FinalizeBlockRequest{
Time: time.Now(),
Height: 1,
Hash: emptyHash[:],
})
require.NoError(t, err)
@ -554,6 +563,7 @@ func TestConsensus_Info(t *testing.T) {
_, err = c.FinalizeBlock(context.Background(), &abciproto.FinalizeBlockRequest{
Time: time.Now(),
Height: 1,
Hash: emptyHash[:],
})
require.NoError(t, err)
@ -570,7 +580,8 @@ func TestConsensus_Query(t *testing.T) {
c := setUpConsensus(t, 100_000, cometmock.MockMempool[mock.Tx]{})
// Write data to state storage
err := c.store.GetStateStorage().ApplyChangeset(1, &store.Changeset{
err := c.store.GetStateStorage().ApplyChangeset(&store.Changeset{
Version: 1,
Changes: []store.StateChanges{
{
Actor: actorName,
@ -597,6 +608,7 @@ func TestConsensus_Query(t *testing.T) {
Time: time.Now(),
Height: 1,
Txs: [][]byte{mockTx.Bytes()},
Hash: emptyHash[:],
})
require.NoError(t, err)
@ -687,22 +699,19 @@ func setUpConsensus(t *testing.T, gasLimit uint64, mempool mempool.Mempool[mock.
nil,
)
return NewConsensus[mock.Tx](log.NewNopLogger(), "testing-app", am, func() error { return nil }, mempool, map[string]struct{}{}, nil, mockStore, Config{AppTomlConfig: DefaultAppTomlConfig()}, mock.TxCodec{}, "test")
return NewConsensus[mock.Tx](log.NewNopLogger(), "testing-app", am, func() error { return nil },
mempool, map[string]struct{}{}, nil, mockStore,
Config{AppTomlConfig: DefaultAppTomlConfig()}, mock.TxCodec{}, "test")
}
// Check target version same with store's latest version
// And should have commit info of target version
// If block 0, commitInfo returned should be nil
func assertStoreLatestVersion(t *testing.T, store types.Store, target uint64) {
t.Helper()
version, err := store.GetLatestVersion()
require.NoError(t, err)
require.Equal(t, version, target)
require.Equal(t, target, version)
commitInfo, err := store.GetStateCommitment().GetCommitInfo(version)
require.NoError(t, err)
if target != 0 {
require.Equal(t, commitInfo.Version, target)
} else {
require.Nil(t, commitInfo)
}
require.Equal(t, target, commitInfo.Version)
}

View File

@ -4,6 +4,7 @@ go 1.23.1
replace (
cosmossdk.io/api => ../../../api
cosmossdk.io/core => ../../../core
cosmossdk.io/core/testing => ../../../core/testing
cosmossdk.io/server/v2 => ../
cosmossdk.io/server/v2/appmanager => ../appmanager

View File

@ -6,8 +6,6 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cosmossdk.io/collections v0.4.0 h1:PFmwj2W8szgpD5nOd8GWH6AbYNi1f2J6akWXJ7P5t9s=
cosmossdk.io/collections v0.4.0/go.mod h1:oa5lUING2dP+gdDquow+QjlF45eL1t4TJDypgGd+tv0=
cosmossdk.io/core v1.0.0-alpha.5 h1:McjYXAQ6XcT20v2uHyH7PhoWH8V+mebzfVFqT3GinsI=
cosmossdk.io/core v1.0.0-alpha.5/go.mod h1:3u9cWq1FAVtiiCrDPpo4LhR+9V6k/ycSG4/Y/tREWCY=
cosmossdk.io/depinject v1.1.0 h1:wLan7LG35VM7Yo6ov0jId3RHWCGRhe8E8bsuARorl5E=
cosmossdk.io/depinject v1.1.0/go.mod h1:kkI5H9jCGHeKeYWXTqYdruogYrEeWvBQCw1Pj4/eCFI=
cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0=

View File

@ -59,8 +59,7 @@ func (s *MockStore) StateLatest() (uint64, corestore.ReaderMap, error) {
}
func (s *MockStore) Commit(changeset *corestore.Changeset) (corestore.Hash, error) {
v, _, _ := s.StateLatest()
err := s.Storage.ApplyChangeset(v, changeset)
err := s.Storage.ApplyChangeset(changeset)
if err != nil {
return []byte{}, err
}
@ -70,8 +69,7 @@ func (s *MockStore) Commit(changeset *corestore.Changeset) (corestore.Hash, erro
return []byte{}, err
}
commitInfo, err := s.Committer.Commit(v + 1)
fmt.Println("commitInfo", commitInfo, err)
_, err = s.Committer.Commit(changeset.Version)
return []byte{}, err
}
@ -127,17 +125,3 @@ func (s *MockStore) LastCommitID() (proof.CommitID, error) {
func (s *MockStore) SetInitialVersion(v uint64) error {
return s.Committer.SetInitialVersion(v)
}
func (s *MockStore) WorkingHash(changeset *corestore.Changeset) (corestore.Hash, error) {
v, _, _ := s.StateLatest()
err := s.Storage.ApplyChangeset(v, changeset)
if err != nil {
return []byte{}, err
}
err = s.Committer.WriteChangeset(changeset)
if err != nil {
return []byte{}, err
}
return []byte{}, nil
}

View File

@ -19,10 +19,6 @@ type Store interface {
// SetInitialVersion sets the initial version of the store.
SetInitialVersion(uint64) error
// WorkingHash writes the provided changeset to the state and returns
// the working hash of the state.
WorkingHash(*store.Changeset) (store.Hash, error)
// Commit commits the provided changeset and returns
// the new state root of the state.
Commit(*store.Changeset) (store.Hash, error)

View File

@ -4,6 +4,7 @@ go 1.23
replace (
cosmossdk.io/api => ../../api
cosmossdk.io/core => ../../core
cosmossdk.io/server/v2/appmanager => ./appmanager
cosmossdk.io/server/v2/stf => ./stf
cosmossdk.io/store/v2 => ../../store/v2

View File

@ -1,7 +1,5 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cosmossdk.io/core v1.0.0-alpha.5 h1:McjYXAQ6XcT20v2uHyH7PhoWH8V+mebzfVFqT3GinsI=
cosmossdk.io/core v1.0.0-alpha.5/go.mod h1:3u9cWq1FAVtiiCrDPpo4LhR+9V6k/ycSG4/Y/tREWCY=
cosmossdk.io/core/testing v0.0.0-20240923163230-04da382a9f29 h1:NxxUo0GMJUbIuVg0R70e3cbn9eFTEuMr7ev1AFvypdY=
cosmossdk.io/core/testing v0.0.0-20240923163230-04da382a9f29/go.mod h1:8s2tPeJtSiQuoyPmr2Ag7meikonISO4Fv4MoO8+ORrs=
cosmossdk.io/errors/v2 v2.0.0-20240731132947-df72853b3ca5 h1:IQNdY2kB+k+1OM2DvqFG1+UgeU1JzZrWtwuWzI3ZfwA=

View File

@ -47,7 +47,7 @@ func (s *Server[T]) Start(context.Context) error {
}
func (s *Server[T]) Stop(context.Context) error {
return s.store.Close()
return nil
}
func (s *Server[T]) CLICommands() serverv2.CLIConfig {

View File

@ -94,6 +94,7 @@ func NewTestApp(t *testing.T) (*SimApp[transaction.Tx], context.Context) {
ChainId: "theChain",
AppHash: ci.Hash,
IsGenesis: true,
Height: 1,
},
genesisBytes,
nil,
@ -103,7 +104,7 @@ func NewTestApp(t *testing.T) (*SimApp[transaction.Tx], context.Context) {
changes, err := newState.GetStateChanges()
require.NoError(t, err)
_, err = st.Commit(&store.Changeset{Changes: changes})
_, err = st.Commit(&store.Changeset{Version: 1, Changes: changes})
require.NoError(t, err)
return app, ctx
@ -119,6 +120,7 @@ func MoveNextBlock(t *testing.T, app *SimApp[transaction.Tx], ctx context.Contex
require.NoError(t, err)
height, err := app.LoadLatestHeight()
height++
require.NoError(t, err)
// TODO: this is a hack to set the comet info in the context for distribution module dependency.
@ -132,7 +134,7 @@ func MoveNextBlock(t *testing.T, app *SimApp[transaction.Tx], ctx context.Contex
_, newState, err := app.DeliverBlock(
ctx,
&server.BlockRequest[transaction.Tx]{
Height: height + 1,
Height: height,
Time: time.Now(),
Hash: bz[:],
AppHash: ci.Hash,
@ -142,7 +144,7 @@ func MoveNextBlock(t *testing.T, app *SimApp[transaction.Tx], ctx context.Contex
changes, err := newState.GetStateChanges()
require.NoError(t, err)
_, err = st.Commit(&store.Changeset{Changes: changes})
_, err = st.Commit(&store.Changeset{Version: height, Changes: changes})
require.NoError(t, err)
}

View File

@ -291,6 +291,7 @@ replace (
// server v2 integration
replace (
cosmossdk.io/api => ../../api
cosmossdk.io/core => ../../core
cosmossdk.io/core/testing => ../../core/testing
cosmossdk.io/runtime/v2 => ../../runtime/v2
cosmossdk.io/server/v2 => ../../server/v2

View File

@ -192,8 +192,6 @@ cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xX
cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg=
cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0=
cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M=
cosmossdk.io/core v1.0.0-alpha.5 h1:McjYXAQ6XcT20v2uHyH7PhoWH8V+mebzfVFqT3GinsI=
cosmossdk.io/core v1.0.0-alpha.5/go.mod h1:3u9cWq1FAVtiiCrDPpo4LhR+9V6k/ycSG4/Y/tREWCY=
cosmossdk.io/depinject v1.1.0 h1:wLan7LG35VM7Yo6ov0jId3RHWCGRhe8E8bsuARorl5E=
cosmossdk.io/depinject v1.1.0/go.mod h1:kkI5H9jCGHeKeYWXTqYdruogYrEeWvBQCw1Pj4/eCFI=
cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0=

View File

@ -51,7 +51,14 @@ func (t *IavlTree) Hash() []byte {
return t.tree.Hash()
}
// Version returns the current version of the tree.
func (t *IavlTree) Version() uint64 {
return uint64(t.tree.Version())
}
// WorkingHash returns the working hash of the tree.
// Danger! iavl.MutableTree.WorkingHash() is a mutating operation!
// It advances the tree version by 1.
func (t *IavlTree) WorkingHash() []byte {
return t.tree.WorkingHash()
}

View File

@ -26,8 +26,8 @@ func (t *Tree) Hash() []byte {
return nil
}
func (t *Tree) WorkingHash() []byte {
return nil
func (t *Tree) Version() uint64 {
return 0
}
func (t *Tree) LoadVersion(version uint64) error {

View File

@ -82,28 +82,6 @@ func (c *CommitStore) WriteChangeset(cs *corestore.Changeset) error {
return nil
}
func (c *CommitStore) WorkingCommitInfo(version uint64) *proof.CommitInfo {
storeInfos := make([]proof.StoreInfo, 0, len(c.multiTrees))
for storeKey, tree := range c.multiTrees {
if internal.IsMemoryStoreKey(storeKey) {
continue
}
bz := []byte(storeKey)
storeInfos = append(storeInfos, proof.StoreInfo{
Name: bz,
CommitID: proof.CommitID{
Version: version,
Hash: tree.WorkingHash(),
},
})
}
return &proof.CommitInfo{
Version: version,
StoreInfos: storeInfos,
}
}
func (c *CommitStore) LoadVersion(targetVersion uint64) error {
storeKeys := make([]string, 0, len(c.multiTrees))
for storeKey := range c.multiTrees {
@ -184,7 +162,10 @@ func (c *CommitStore) loadVersion(targetVersion uint64, storeKeys []string) erro
// If the target version is greater than the latest version, it is the snapshot
// restore case, we should create a new commit info for the target version.
if targetVersion > latestVersion {
cInfo := c.WorkingCommitInfo(targetVersion)
cInfo, err := c.GetCommitInfo(targetVersion)
if err != nil {
return err
}
return c.metadata.flushCommitInfo(targetVersion, cInfo)
}
@ -198,29 +179,16 @@ func (c *CommitStore) Commit(version uint64) (*proof.CommitInfo, error) {
if internal.IsMemoryStoreKey(storeKey) {
continue
}
// If a commit event execution is interrupted, a new iavl store's version
// will be larger than the RMS's metadata, when the block is replayed, we
// should avoid committing that iavl store again.
var commitID proof.CommitID
v, err := tree.GetLatestVersion()
hash, cversion, err := tree.Commit()
if err != nil {
return nil, err
}
if v >= version {
commitID.Version = version
commitID.Hash = tree.Hash()
} else {
hash, cversion, err := tree.Commit()
if err != nil {
return nil, err
}
if cversion != version {
return nil, fmt.Errorf("commit version %d does not match the target version %d", cversion, version)
}
commitID = proof.CommitID{
Version: version,
Hash: hash,
}
if cversion != version {
return nil, fmt.Errorf("commit version %d does not match the target version %d", cversion, version)
}
commitID := proof.CommitID{
Version: version,
Hash: hash,
}
storeInfos = append(storeInfos, proof.StoreInfo{
Name: []byte(storeKey),
@ -541,7 +509,39 @@ loop:
}
func (c *CommitStore) GetCommitInfo(version uint64) (*proof.CommitInfo, error) {
return c.metadata.GetCommitInfo(version)
// if the commit info is already stored, return it
ci, err := c.metadata.GetCommitInfo(version)
if err != nil {
return nil, err
}
if ci != nil {
return ci, nil
}
// otherwise built the commit info from the trees
storeInfos := make([]proof.StoreInfo, 0, len(c.multiTrees))
for storeKey, tree := range c.multiTrees {
if internal.IsMemoryStoreKey(storeKey) {
continue
}
v := tree.Version()
if v != version {
return nil, fmt.Errorf("tree version %d does not match the target version %d", v, version)
}
bz := []byte(storeKey)
storeInfos = append(storeInfos, proof.StoreInfo{
Name: bz,
CommitID: proof.CommitID{
Version: v,
Hash: tree.Hash(),
},
})
}
ci = &proof.CommitInfo{
Version: version,
StoreInfos: storeInfos,
}
return ci, nil
}
func (c *CommitStore) GetLatestVersion() (uint64, error) {
@ -554,6 +554,5 @@ func (c *CommitStore) Close() error {
return err
}
}
return nil
}

View File

@ -35,8 +35,8 @@ var (
)
func init() {
for i := 0; i < 1000; i++ {
cs := corestore.NewChangeset()
for i := uint64(0); i < 1000; i++ {
cs := corestore.NewChangeset(i)
for _, storeKey := range storeKeys {
for j := 0; j < 100; j++ {
key := make([]byte, 16)

View File

@ -13,6 +13,7 @@ import (
coretesting "cosmossdk.io/core/testing"
"cosmossdk.io/store/v2"
dbm "cosmossdk.io/store/v2/db"
"cosmossdk.io/store/v2/proof"
"cosmossdk.io/store/v2/snapshots"
snapshotstypes "cosmossdk.io/store/v2/snapshots/types"
)
@ -37,6 +38,7 @@ func (s *CommitStoreTestSuite) TestStore_Snapshotter() {
latestVersion := uint64(10)
kvCount := 10
var cInfo *proof.CommitInfo
for i := uint64(1); i <= latestVersion; i++ {
kvPairs := make(map[string]corestore.KVPairs)
for _, storeKey := range storeKeys {
@ -47,13 +49,12 @@ func (s *CommitStoreTestSuite) TestStore_Snapshotter() {
kvPairs[storeKey] = append(kvPairs[storeKey], corestore.KVPair{Key: key, Value: value})
}
}
s.Require().NoError(commitStore.WriteChangeset(corestore.NewChangesetWithPairs(kvPairs)))
s.Require().NoError(commitStore.WriteChangeset(corestore.NewChangesetWithPairs(i, kvPairs)))
_, err = commitStore.Commit(i)
cInfo, err = commitStore.Commit(i)
s.Require().NoError(err)
}
cInfo := commitStore.WorkingCommitInfo(latestVersion)
s.Require().Equal(len(storeKeys), len(cInfo.StoreInfos))
// create a snapshot
@ -112,7 +113,8 @@ func (s *CommitStoreTestSuite) TestStore_Snapshotter() {
}
// check the restored tree hash
targetCommitInfo := targetStore.WorkingCommitInfo(latestVersion)
targetCommitInfo, err := targetStore.GetCommitInfo(latestVersion)
s.Require().NoError(err)
for _, storeInfo := range targetCommitInfo.StoreInfos {
matched := false
for _, latestStoreInfo := range cInfo.StoreInfos {
@ -143,7 +145,7 @@ func (s *CommitStoreTestSuite) TestStore_LoadVersion() {
kvPairs[storeKey] = append(kvPairs[storeKey], corestore.KVPair{Key: key, Value: value})
}
}
s.Require().NoError(commitStore.WriteChangeset(corestore.NewChangesetWithPairs(kvPairs)))
s.Require().NoError(commitStore.WriteChangeset(corestore.NewChangesetWithPairs(i, kvPairs)))
_, err = commitStore.Commit(i)
s.Require().NoError(err)
}
@ -198,7 +200,7 @@ func (s *CommitStoreTestSuite) TestStore_Pruning() {
kvPairs[storeKey] = append(kvPairs[storeKey], corestore.KVPair{Key: key, Value: value})
}
}
s.Require().NoError(commitStore.WriteChangeset(corestore.NewChangesetWithPairs(kvPairs)))
s.Require().NoError(commitStore.WriteChangeset(corestore.NewChangesetWithPairs(i, kvPairs)))
_, err = commitStore.Commit(i)
s.Require().NoError(err)
@ -231,7 +233,7 @@ func (s *CommitStoreTestSuite) TestStore_GetProof() {
// commit some changes
for version := uint64(1); version <= toVersion; version++ {
cs := corestore.NewChangeset()
cs := corestore.NewChangeset(version)
for _, storeKey := range storeKeys {
for i := 0; i < keyCount; i++ {
cs.Add([]byte(storeKey), []byte(fmt.Sprintf("key-%d-%d", version, i)), []byte(fmt.Sprintf("value-%d-%d", version, i)), false)
@ -274,7 +276,7 @@ func (s *CommitStoreTestSuite) TestStore_Get() {
// commit some changes
for version := uint64(1); version <= toVersion; version++ {
cs := corestore.NewChangeset()
cs := corestore.NewChangeset(version)
for _, storeKey := range storeKeys {
for i := 0; i < keyCount; i++ {
cs.Add([]byte(storeKey), []byte(fmt.Sprintf("key-%d-%d", version, i)), []byte(fmt.Sprintf("value-%d-%d", version, i)), false)
@ -316,7 +318,7 @@ func (s *CommitStoreTestSuite) TestStore_Upgrades() {
kvPairs[storeKey] = append(kvPairs[storeKey], corestore.KVPair{Key: key, Value: value})
}
}
s.Require().NoError(commitStore.WriteChangeset(corestore.NewChangesetWithPairs(kvPairs)))
s.Require().NoError(commitStore.WriteChangeset(corestore.NewChangesetWithPairs(i, kvPairs)))
_, err = commitStore.Commit(i)
s.Require().NoError(err)
}
@ -365,7 +367,7 @@ func (s *CommitStoreTestSuite) TestStore_Upgrades() {
kvPairs[storeKey] = append(kvPairs[storeKey], corestore.KVPair{Key: key, Value: value})
}
}
s.Require().NoError(commitStore.WriteChangeset(corestore.NewChangesetWithPairs(kvPairs)))
s.Require().NoError(commitStore.WriteChangeset(corestore.NewChangesetWithPairs(i, kvPairs)))
commitInfo, err := commitStore.Commit(i)
s.Require().NoError(err)
s.Require().NotNil(commitInfo)
@ -418,7 +420,7 @@ func (s *CommitStoreTestSuite) TestStore_Upgrades() {
kvPairs[storeKey] = append(kvPairs[storeKey], corestore.KVPair{Key: key, Value: value})
}
}
s.Require().NoError(commitStore.WriteChangeset(corestore.NewChangesetWithPairs(kvPairs)))
s.Require().NoError(commitStore.WriteChangeset(corestore.NewChangesetWithPairs(i, kvPairs)))
commitInfo, err := commitStore.Commit(i)
s.Require().NoError(err)
s.Require().NotNil(commitInfo)

View File

@ -19,11 +19,10 @@ type Tree interface {
Remove(key []byte) error
GetLatestVersion() (uint64, error)
// Hash returns the hash of the latest saved version of the tree.
// Hash returns the hash of the current version of the tree
Hash() []byte
// WorkingHash returns the working hash of the tree.
WorkingHash() []byte
// Version returns the current version of the tree
Version() uint64
LoadVersion(version uint64) error
Commit() ([]byte, uint64, error)

View File

@ -13,9 +13,9 @@ type VersionedWriter interface {
VersionedReader
SetLatestVersion(version uint64) error
ApplyChangeset(version uint64, cs *corestore.Changeset) error
ApplyChangeset(cs *corestore.Changeset) error
// Close releases associated resources. It should NOT be idempotent. It must
// Closer releases associated resources. It should NOT be idempotent. It must
// only be called once and any call after may panic.
io.Closer
}
@ -44,9 +44,6 @@ type Committer interface {
// WriteChangeset writes the changeset to the commitment state.
WriteChangeset(cs *corestore.Changeset) error
// WorkingCommitInfo returns the CommitInfo for the working tree.
WorkingCommitInfo(version uint64) *proof.CommitInfo
// GetLatestVersion returns the latest version.
GetLatestVersion() (uint64, error)
@ -67,7 +64,7 @@ type Committer interface {
Get(storeKey []byte, version uint64, key []byte) ([]byte, error)
// Close releases associated resources. It should NOT be idempotent. It must
// Closer releases associated resources. It should NOT be idempotent. It must
// only be called once and any call after may panic.
io.Closer
}

View File

@ -64,3 +64,5 @@ require (
google.golang.org/protobuf v1.35.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
replace cosmossdk.io/core => ../../core

View File

@ -1,5 +1,3 @@
cosmossdk.io/core v1.0.0-alpha.5 h1:McjYXAQ6XcT20v2uHyH7PhoWH8V+mebzfVFqT3GinsI=
cosmossdk.io/core v1.0.0-alpha.5/go.mod h1:3u9cWq1FAVtiiCrDPpo4LhR+9V6k/ycSG4/Y/tREWCY=
cosmossdk.io/core/testing v0.0.0-20240923163230-04da382a9f29 h1:NxxUo0GMJUbIuVg0R70e3cbn9eFTEuMr7ev1AFvypdY=
cosmossdk.io/core/testing v0.0.0-20240923163230-04da382a9f29/go.mod h1:8s2tPeJtSiQuoyPmr2Ag7meikonISO4Fv4MoO8+ORrs=
cosmossdk.io/errors/v2 v2.0.0-20240731132947-df72853b3ca5 h1:IQNdY2kB+k+1OM2DvqFG1+UgeU1JzZrWtwuWzI3ZfwA=

View File

@ -17,7 +17,7 @@ func TestChangesetMarshal(t *testing.T) {
}{
{
name: "empty",
changeset: corestore.NewChangeset(),
changeset: corestore.NewChangeset(1),
encodedSize: 1,
encodedBytes: []byte{0x0},
},
@ -80,7 +80,7 @@ func TestChangesetMarshal(t *testing.T) {
require.Equal(t, encodedBytes, tc.encodedBytes, "encoded bytes mismatch")
}
// check the unmarshaled changeset
cs := corestore.NewChangeset()
cs := corestore.NewChangeset(1)
require.NoError(t, UnmarshalChangeset(cs, encodedBytes), "unmarshal error")
require.Equal(t, len(tc.changeset.Changes), len(cs.Changes), "unmarshaled changeset store size mismatch")
for i, changes := range tc.changeset.Changes {

View File

@ -239,7 +239,7 @@ func (m *Manager) Sync() error {
continue
}
cs := corestore.NewChangeset()
cs := corestore.NewChangeset(version)
if err := encoding.UnmarshalChangeset(cs, csBytes); err != nil {
return fmt.Errorf("failed to unmarshal changeset: %w", err)
}
@ -251,7 +251,7 @@ func (m *Manager) Sync() error {
return fmt.Errorf("failed to commit changeset to commitment: %w", err)
}
}
if err := m.stateStorage.ApplyChangeset(version, cs); err != nil {
if err := m.stateStorage.ApplyChangeset(cs); err != nil {
return fmt.Errorf("failed to write changeset to storage: %w", err)
}

View File

@ -66,7 +66,7 @@ func TestMigrateState(t *testing.T) {
toVersion := uint64(100)
keyCount := 10
for version := uint64(1); version <= toVersion; version++ {
cs := corestore.NewChangeset()
cs := corestore.NewChangeset(version)
for _, storeKey := range storeKeys {
for i := 0; i < keyCount; i++ {
cs.Add([]byte(storeKey), []byte(fmt.Sprintf("key-%d-%d", version, i)), []byte(fmt.Sprintf("value-%d-%d", version, i)), false)
@ -133,7 +133,7 @@ func TestStartMigrateState(t *testing.T) {
changesets := []corestore.Changeset{}
for version := uint64(1); version <= toVersion; version++ {
cs := corestore.NewChangeset()
cs := corestore.NewChangeset(version)
for _, storeKey := range storeKeys {
for i := 0; i < keyCount; i++ {
cs.Add([]byte(storeKey), []byte(fmt.Sprintf("key-%d-%d", version, i)), []byte(fmt.Sprintf("value-%d-%d", version, i)), false)

View File

@ -21,6 +21,7 @@ import (
type MockStateCommitter struct {
ctrl *gomock.Controller
recorder *MockStateCommitterMockRecorder
isgomock struct{}
}
// MockStateCommitterMockRecorder is the mock recorder for MockStateCommitter.
@ -197,20 +198,6 @@ func (mr *MockStateCommitterMockRecorder) SetInitialVersion(version any) *gomock
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetInitialVersion", reflect.TypeOf((*MockStateCommitter)(nil).SetInitialVersion), version)
}
// WorkingCommitInfo mocks base method.
func (m *MockStateCommitter) WorkingCommitInfo(version uint64) *proof.CommitInfo {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "WorkingCommitInfo", version)
ret0, _ := ret[0].(*proof.CommitInfo)
return ret0
}
// WorkingCommitInfo indicates an expected call of WorkingCommitInfo.
func (mr *MockStateCommitterMockRecorder) WorkingCommitInfo(version any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WorkingCommitInfo", reflect.TypeOf((*MockStateCommitter)(nil).WorkingCommitInfo), version)
}
// WriteChangeset mocks base method.
func (m *MockStateCommitter) WriteChangeset(cs *store.Changeset) error {
m.ctrl.T.Helper()
@ -229,6 +216,7 @@ func (mr *MockStateCommitterMockRecorder) WriteChangeset(cs any) *gomock.Call {
type MockStateStorage struct {
ctrl *gomock.Controller
recorder *MockStateStorageMockRecorder
isgomock struct{}
}
// MockStateStorageMockRecorder is the mock recorder for MockStateStorage.
@ -249,17 +237,17 @@ func (m *MockStateStorage) EXPECT() *MockStateStorageMockRecorder {
}
// ApplyChangeset mocks base method.
func (m *MockStateStorage) ApplyChangeset(version uint64, cs *store.Changeset) error {
func (m *MockStateStorage) ApplyChangeset(cs *store.Changeset) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ApplyChangeset", version, cs)
ret := m.ctrl.Call(m, "ApplyChangeset", cs)
ret0, _ := ret[0].(error)
return ret0
}
// ApplyChangeset indicates an expected call of ApplyChangeset.
func (mr *MockStateStorageMockRecorder) ApplyChangeset(version, cs any) *gomock.Call {
func (mr *MockStateStorageMockRecorder) ApplyChangeset(cs any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ApplyChangeset", reflect.TypeOf((*MockStateStorage)(nil).ApplyChangeset), version, cs)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ApplyChangeset", reflect.TypeOf((*MockStateStorage)(nil).ApplyChangeset), cs)
}
// Close mocks base method.

View File

@ -28,7 +28,7 @@ func NewManager(scPruner, ssPruner store.Pruner, scPruningOption, ssPruningOptio
// Prune prunes the SC and SS to the provided version.
//
// NOTE: It can be called outside of the store manually.
// NOTE: It can be called outside the store manually.
func (m *Manager) Prune(version uint64) error {
// Prune the SC.
if m.scPruningOption != nil {
@ -51,21 +51,20 @@ func (m *Manager) Prune(version uint64) error {
return nil
}
// SignalCommit signals to the manager that a commit has started or finished.
// It is used to trigger the pruning of the SC and SS.
// It pauses or resumes the pruning of the SC and SS if the pruner implements
// the PausablePruner interface.
func (m *Manager) SignalCommit(start bool, version uint64) error {
func (m *Manager) signalPruning(pause bool) {
if scPausablePruner, ok := m.scPruner.(store.PausablePruner); ok {
scPausablePruner.PausePruning(start)
scPausablePruner.PausePruning(pause)
}
if ssPausablePruner, ok := m.ssPruner.(store.PausablePruner); ok {
ssPausablePruner.PausePruning(start)
ssPausablePruner.PausePruning(pause)
}
if !start {
return m.Prune(version)
}
return nil
}
func (m *Manager) PausePruning() {
m.signalPruning(true)
}
func (m *Manager) ResumePruning(version uint64) error {
m.signalPruning(false)
return m.Prune(version)
}

View File

@ -58,7 +58,7 @@ func (s *PruningManagerTestSuite) TestPrune() {
toVersion := uint64(100)
keyCount := 10
for version := uint64(1); version <= toVersion; version++ {
cs := corestore.NewChangeset()
cs := corestore.NewChangeset(version)
for _, storeKey := range storeKeys {
for i := 0; i < keyCount; i++ {
cs.Add([]byte(storeKey), []byte(fmt.Sprintf("key-%d-%d", version, i)), []byte(fmt.Sprintf("value-%d-%d", version, i)), false)
@ -68,7 +68,7 @@ func (s *PruningManagerTestSuite) TestPrune() {
_, err := s.sc.Commit(version)
s.Require().NoError(err)
s.Require().NoError(s.ss.ApplyChangeset(version, cs))
s.Require().NoError(s.ss.ApplyChangeset(cs))
s.Require().NoError(s.manager.Prune(version))
}
@ -155,7 +155,7 @@ func TestPruningOption(t *testing.T) {
func (s *PruningManagerTestSuite) TestSignalCommit() {
// commit version 1
cs := corestore.NewChangeset()
cs := corestore.NewChangeset(1)
for _, storeKey := range storeKeys {
cs.Add([]byte(storeKey), []byte(fmt.Sprintf("key-%d-%d", 1, 0)), []byte(fmt.Sprintf("value-%d-%d", 1, 0)), false)
}
@ -164,21 +164,22 @@ func (s *PruningManagerTestSuite) TestSignalCommit() {
_, err := s.sc.Commit(1)
s.Require().NoError(err)
s.Require().NoError(s.ss.ApplyChangeset(1, cs))
s.Require().NoError(s.ss.ApplyChangeset(cs))
// commit version 2
for _, storeKey := range storeKeys {
cs.Add([]byte(storeKey), []byte(fmt.Sprintf("key-%d-%d", 2, 0)), []byte(fmt.Sprintf("value-%d-%d", 2, 0)), false)
}
cs.Version = 2
// signaling commit has started
s.Require().NoError(s.manager.SignalCommit(true, 2))
s.manager.PausePruning()
s.Require().NoError(s.sc.WriteChangeset(cs))
_, err = s.sc.Commit(2)
s.Require().NoError(err)
s.Require().NoError(s.ss.ApplyChangeset(2, cs))
s.Require().NoError(s.ss.ApplyChangeset(cs))
// try prune before signaling commit has finished
s.Require().NoError(s.manager.Prune(2))
@ -204,7 +205,8 @@ func (s *PruningManagerTestSuite) TestSignalCommit() {
s.Require().Equal(val, []byte(fmt.Sprintf("value-%d-%d", 1, 0)))
// signaling commit has finished, version 1 should be pruned
s.Require().NoError(s.manager.SignalCommit(false, 2))
err = s.manager.ResumePruning(2)
s.Require().NoError(err)
checkSCPrune = func() bool {
count := 0
@ -224,22 +226,21 @@ func (s *PruningManagerTestSuite) TestSignalCommit() {
toVersion := uint64(100)
keyCount := 10
for version := uint64(3); version <= toVersion; version++ {
cs := corestore.NewChangeset()
cs := corestore.NewChangeset(version)
for _, storeKey := range storeKeys {
for i := 0; i < keyCount; i++ {
cs.Add([]byte(storeKey), []byte(fmt.Sprintf("key-%d-%d", version, i)), []byte(fmt.Sprintf("value-%d-%d", version, i)), false)
}
}
s.Require().NoError(s.manager.SignalCommit(true, version))
s.manager.PausePruning()
s.Require().NoError(s.sc.WriteChangeset(cs))
_, err := s.sc.Commit(version)
s.Require().NoError(err)
s.Require().NoError(s.ss.ApplyChangeset(version, cs))
s.Require().NoError(s.manager.SignalCommit(false, version))
s.Require().NoError(s.ss.ApplyChangeset(cs))
err = s.manager.ResumePruning(version)
s.Require().NoError(err)
}
// wait for the pruning to finish in the commitment store

View File

@ -71,7 +71,7 @@ func (sb *builder) Build(
}
factoryOptions := &FactoryOptions{
Logger: logger,
Logger: logger.With("module", "store"),
RootDir: config.Home,
Options: config.Options,
StoreKeys: storeKeys,

View File

@ -50,7 +50,7 @@ func (s *MigrateStoreTestSuite) SetupTest() {
toVersion := uint64(200)
keyCount := 10
for version := uint64(1); version <= toVersion; version++ {
cs := corestore.NewChangeset()
cs := corestore.NewChangeset(version)
for _, storeKey := range storeKeys {
for i := 0; i < keyCount; i++ {
cs.Add([]byte(storeKey), []byte(fmt.Sprintf("key-%d-%d", version, i)), []byte(fmt.Sprintf("value-%d-%d", version, i)), false)
@ -105,7 +105,7 @@ func (s *MigrateStoreTestSuite) TestMigrateState() {
latestVersion := originalLatestVersion + 1
keyCount := 10
for ; latestVersion < 2*originalLatestVersion; latestVersion++ {
cs := corestore.NewChangeset()
cs := corestore.NewChangeset(latestVersion)
for _, storeKey := range storeKeys {
for i := 0; i < keyCount; i++ {
cs.Add([]byte(storeKey), []byte(fmt.Sprintf("key-%d-%d", latestVersion, i)), []byte(fmt.Sprintf("value-%d-%d", latestVersion, i)), false)
@ -147,7 +147,7 @@ func (s *MigrateStoreTestSuite) TestMigrateState() {
// apply changeset against the migrated store
for version := latestVersion + 1; version <= latestVersion+10; version++ {
cs := corestore.NewChangeset()
cs := corestore.NewChangeset(version)
for _, storeKey := range storeKeys {
for i := 0; i < keyCount; i++ {
cs.Add([]byte(storeKey), []byte(fmt.Sprintf("key-%d-%d", version, i)), []byte(fmt.Sprintf("value-%d-%d", version, i)), false)

View File

@ -1,7 +1,6 @@
package root
import (
"bytes"
"crypto/sha256"
"errors"
"fmt"
@ -11,7 +10,6 @@ import (
"golang.org/x/sync/errgroup"
coreheader "cosmossdk.io/core/header"
corelog "cosmossdk.io/core/log"
corestore "cosmossdk.io/core/store"
"cosmossdk.io/store/v2"
@ -31,8 +29,7 @@ var (
// backend may or may not support multiple store keys and is implementation
// dependent.
type Store struct {
logger corelog.Logger
initialVersion uint64
logger corelog.Logger
// holds the db instance for closing it
dbCloser io.Closer
@ -43,10 +40,6 @@ type Store struct {
// stateCommitment reflects the state commitment (SC) backend
stateCommitment store.Committer
// commitHeader reflects the header used when committing state
// note, this isn't required and only used for query purposes)
commitHeader *coreheader.Info
// lastCommitInfo reflects the last version/hash that has been committed
lastCommitInfo *proof.CommitInfo
@ -83,7 +76,6 @@ func New(
return &Store{
dbCloser: dbCloser,
logger: logger,
initialVersion: 1,
stateStorage: ss,
stateCommitment: sc,
pruningManager: pm,
@ -103,7 +95,6 @@ func (s *Store) Close() (err error) {
s.stateStorage = nil
s.stateCommitment = nil
s.lastCommitInfo = nil
s.commitHeader = nil
return err
}
@ -113,8 +104,6 @@ func (s *Store) SetMetrics(m metrics.Metrics) {
}
func (s *Store) SetInitialVersion(v uint64) error {
s.initialVersion = v
return s.stateCommitment.SetInitialVersion(v)
}
@ -323,8 +312,6 @@ func (s *Store) loadVersion(v uint64, upgrades *corestore.StoreUpgrades) error {
}
}
s.commitHeader = nil
// set lastCommitInfo explicitly s.t. Commit commits the correct version, i.e. v+1
var err error
s.lastCommitInfo, err = s.stateCommitment.GetCommitInfo(v)
@ -340,44 +327,6 @@ func (s *Store) loadVersion(v uint64, upgrades *corestore.StoreUpgrades) error {
return nil
}
func (s *Store) SetCommitHeader(h *coreheader.Info) {
s.commitHeader = h
}
// WorkingHash writes the changeset to SC and SS and returns the workingHash
// of the CommitInfo.
func (s *Store) WorkingHash(cs *corestore.Changeset) ([]byte, error) {
if s.telemetry != nil {
now := time.Now()
defer s.telemetry.MeasureSince(now, "root_store", "working_hash")
}
// write the changeset to the SC and SS backends
eg := new(errgroup.Group)
eg.Go(func() error {
if err := s.writeSC(cs); err != nil {
return fmt.Errorf("failed to write SC: %w", err)
}
return nil
})
eg.Go(func() error {
if err := s.stateStorage.ApplyChangeset(s.initialVersion, cs); err != nil {
return fmt.Errorf("failed to commit SS: %w", err)
}
return nil
})
if err := eg.Wait(); err != nil {
return nil, err
}
workingHash := s.lastCommitInfo.Hash()
s.lastCommitInfo.Version -= 1 // reset lastCommitInfo to allow Commit() to work correctly
return workingHash, nil
}
// Commit commits all state changes to the underlying SS and SC backends. It
// writes a batch of the changeset to the SC tree, and retrieves the CommitInfo
// from the SC tree. Finally, it commits the SC tree and returns the hash of
@ -388,32 +337,22 @@ func (s *Store) Commit(cs *corestore.Changeset) ([]byte, error) {
defer s.telemetry.MeasureSince(now, "root_store", "commit")
}
// write the changeset to the SC tree and update lastCommitInfo
if err := s.writeSC(cs); err != nil {
if err := s.handleMigration(cs); err != nil {
return nil, err
}
version := s.lastCommitInfo.Version
if s.commitHeader != nil && uint64(s.commitHeader.Height) != version {
s.logger.Debug("commit header and version mismatch", "header_height", s.commitHeader.Height, "version", version)
}
// signal to the pruning manager that a new version is about to be committed
// this may be required if the SS and SC backends implementation have the
// background pruning process which must be paused during the commit
if err := s.pruningManager.SignalCommit(true, version); err != nil {
s.logger.Error("failed to signal commit to pruning manager", "err", err)
}
// background pruning process (iavl v1 for example) which must be paused during the commit
s.pruningManager.PausePruning()
eg := new(errgroup.Group)
// if we're migrating, we don't want to commit to the state storage to avoid
// parallel writes
// if migrating the changeset will be sent to migration manager to fill SS
// otherwise commit to SS async here
if !s.isMigrating {
// commit SS async
eg.Go(func() error {
if err := s.stateStorage.ApplyChangeset(version, cs); err != nil {
if err := s.stateStorage.ApplyChangeset(cs); err != nil {
return fmt.Errorf("failed to commit SS: %w", err)
}
@ -422,11 +361,16 @@ func (s *Store) Commit(cs *corestore.Changeset) ([]byte, error) {
}
// commit SC async
var cInfo *proof.CommitInfo
eg.Go(func() error {
if err := s.commitSC(); err != nil {
return fmt.Errorf("failed to commit SC: %w", err)
if err := s.stateCommitment.WriteChangeset(cs); err != nil {
return fmt.Errorf("failed to write batch to SC store: %w", err)
}
var scErr error
cInfo, scErr = s.stateCommitment.Commit(cs.Version)
if scErr != nil {
return fmt.Errorf("failed to commit SC store: %w", scErr)
}
return nil
})
@ -434,13 +378,14 @@ func (s *Store) Commit(cs *corestore.Changeset) ([]byte, error) {
return nil, err
}
// signal to the pruning manager that the commit is done
if err := s.pruningManager.SignalCommit(false, version); err != nil {
s.logger.Error("failed to signal commit done to pruning manager", "err", err)
if cInfo.Version != cs.Version {
return nil, fmt.Errorf("commit version mismatch: got %d, expected %d", cInfo.Version, cs.Version)
}
s.lastCommitInfo = cInfo
if s.commitHeader != nil {
s.lastCommitInfo.Timestamp = s.commitHeader.Time
// signal to the pruning manager that the commit is done
if err := s.pruningManager.ResumePruning(s.lastCommitInfo.Version); err != nil {
s.logger.Error("failed to signal commit done to pruning manager", "err", err)
}
return s.lastCommitInfo.Hash(), nil
@ -475,12 +420,7 @@ func (s *Store) startMigration() {
defer mtx.Unlock()
}
// writeSC accepts a Changeset and writes that as a batch to the underlying SC
// tree, which allows us to retrieve the working hash of the SC tree. Finally,
// we construct a *CommitInfo and set that as lastCommitInfo. Note, this should
// only be called once per block!
// If migration is in progress, the changeset is sent to the migration manager.
func (s *Store) writeSC(cs *corestore.Changeset) error {
func (s *Store) handleMigration(cs *corestore.Changeset) error {
if s.isMigrating {
// if the migration manager has already migrated to the version, close the
// channels and replace the state commitment
@ -501,49 +441,10 @@ func (s *Store) writeSC(cs *corestore.Changeset) error {
}
s.logger.Info("migration completed", "version", s.lastCommitInfo.Version)
} else {
// queue the next changeset to the migration manager
s.chChangeset <- &migration.VersionedChangeset{Version: s.lastCommitInfo.Version + 1, Changeset: cs}
}
}
if err := s.stateCommitment.WriteChangeset(cs); err != nil {
return fmt.Errorf("failed to write batch to SC store: %w", err)
}
var previousHeight, version uint64
if s.lastCommitInfo.GetVersion() == 0 && s.initialVersion > 1 {
// This case means that no commit has been made in the store, we
// start from initialVersion.
version = s.initialVersion
} else {
// This case can means two things:
//
// 1. There was already a previous commit in the store, in which case we
// increment the version from there.
// 2. There was no previous commit, and initial version was not set, in which
// case we start at version 1.
previousHeight = s.lastCommitInfo.GetVersion()
version = previousHeight + 1
}
s.lastCommitInfo = s.stateCommitment.WorkingCommitInfo(version)
return nil
}
// commitSC commits the SC store. At this point, a batch of the current changeset
// should have already been written to the SC via writeSC(). This method solely
// commits that batch. An error is returned if commit fails or the hash of the
// committed state does not match the hash of the working state.
func (s *Store) commitSC() error {
cInfo, err := s.stateCommitment.Commit(s.lastCommitInfo.Version)
if err != nil {
return fmt.Errorf("failed to commit SC store: %w", err)
}
if !bytes.Equal(cInfo.Hash(), s.lastCommitInfo.Hash()) {
return fmt.Errorf("unexpected commit hash; got: %X, expected: %X", cInfo.Hash(), s.lastCommitInfo.Hash())
}
return nil
}

View File

@ -12,7 +12,6 @@ import (
"cosmossdk.io/store/v2"
"cosmossdk.io/store/v2/metrics"
"cosmossdk.io/store/v2/mock"
"cosmossdk.io/store/v2/proof"
"cosmossdk.io/store/v2/pruning"
)
@ -22,7 +21,6 @@ func newTestRootStore(ss store.VersionedWriter, sc store.Committer) *Store {
return &Store{
logger: noopLog,
telemetry: metrics.Metrics{},
initialVersion: 1,
stateStorage: ss,
stateCommitment: sc,
pruningManager: pm,
@ -120,85 +118,3 @@ func TestLoadVersion(t *testing.T) {
err = rs.LoadVersionAndUpgrade(uint64(2), v)
require.Error(t, err)
}
func TestWorkingHahs(t *testing.T) {
ctrl := gomock.NewController(t)
ss := mock.NewMockStateStorage(ctrl)
sc := mock.NewMockStateCommitter(ctrl)
rs := newTestRootStore(ss, sc)
cs := corestore.NewChangeset()
// writeSC test
sc.EXPECT().WriteChangeset(cs).Return(errors.New("error"))
err := rs.writeSC(cs)
require.Error(t, err)
sc.EXPECT().WriteChangeset(cs).Return(nil)
sc.EXPECT().WorkingCommitInfo(gomock.Any()).Return(nil)
err = rs.writeSC(cs)
require.NoError(t, err)
// WorkingHash test
sc.EXPECT().WriteChangeset(cs).Return(nil)
sc.EXPECT().WorkingCommitInfo(gomock.Any()).Return(nil)
ss.EXPECT().ApplyChangeset(gomock.Any(), cs).Return(errors.New("error"))
_, err = rs.WorkingHash(cs)
require.Error(t, err)
sc.EXPECT().WriteChangeset(cs).Return(nil)
sc.EXPECT().WorkingCommitInfo(gomock.Any()).Return(nil)
ss.EXPECT().ApplyChangeset(gomock.Any(), cs).Return(errors.New("error"))
_, err = rs.WorkingHash(cs)
require.Error(t, err)
sc.EXPECT().WriteChangeset(cs).Return(nil)
sc.EXPECT().WorkingCommitInfo(gomock.Any()).Return(&proof.CommitInfo{})
ss.EXPECT().ApplyChangeset(gomock.Any(), cs).Return(nil)
_, err = rs.WorkingHash(cs)
require.NoError(t, err)
}
func TestCommit(t *testing.T) {
ctrl := gomock.NewController(t)
ss := mock.NewMockStateStorage(ctrl)
sc := mock.NewMockStateCommitter(ctrl)
rs := newTestRootStore(ss, sc)
cs := corestore.NewChangeset()
// test commitSC
rs.lastCommitInfo = &proof.CommitInfo{}
sc.EXPECT().Commit(gomock.Any()).Return(nil, errors.New("error"))
err := rs.commitSC()
require.Error(t, err)
sc.EXPECT().Commit(gomock.Any()).Return(&proof.CommitInfo{CommitHash: []byte("wrong hash"), StoreInfos: []proof.StoreInfo{{}}}, nil) // wrong hash
err = rs.commitSC()
require.Error(t, err)
// Commit test
sc.EXPECT().WriteChangeset(cs).Return(errors.New("error"))
_, err = rs.Commit(cs)
require.Error(t, err)
sc.EXPECT().WriteChangeset(cs).Return(nil)
sc.EXPECT().WorkingCommitInfo(gomock.Any()).Return(&proof.CommitInfo{})
sc.EXPECT().PausePruning(gomock.Any()).Return()
ss.EXPECT().PausePruning(gomock.Any()).Return()
ss.EXPECT().ApplyChangeset(gomock.Any(), cs).Return(nil)
sc.EXPECT().Commit(gomock.Any()).Return(nil, errors.New("error"))
_, err = rs.Commit(cs)
require.Error(t, err)
sc.EXPECT().WriteChangeset(cs).Return(nil)
sc.EXPECT().WorkingCommitInfo(gomock.Any()).Return(&proof.CommitInfo{})
sc.EXPECT().PausePruning(gomock.Any()).Return()
ss.EXPECT().PausePruning(gomock.Any()).Return()
ss.EXPECT().ApplyChangeset(gomock.Any(), cs).Return(errors.New("error"))
sc.EXPECT().Commit(gomock.Any()).Return(&proof.CommitInfo{}, nil)
_, err = rs.Commit(cs)
require.Error(t, err)
sc.EXPECT().WriteChangeset(cs).Return(nil)
sc.EXPECT().WorkingCommitInfo(gomock.Any()).Return(&proof.CommitInfo{})
sc.EXPECT().PausePruning(true).Return()
ss.EXPECT().PausePruning(true).Return()
ss.EXPECT().ApplyChangeset(gomock.Any(), cs).Return(nil)
sc.EXPECT().Commit(gomock.Any()).Return(&proof.CommitInfo{}, nil)
sc.EXPECT().PausePruning(false).Return()
ss.EXPECT().PausePruning(false).Return()
_, err = rs.Commit(cs)
require.NoError(t, err)
}

View File

@ -8,7 +8,6 @@ import (
"github.com/stretchr/testify/suite"
coreheader "cosmossdk.io/core/header"
corestore "cosmossdk.io/core/store"
coretesting "cosmossdk.io/core/testing"
"cosmossdk.io/store/v2"
@ -116,15 +115,11 @@ func (s *RootStoreTestSuite) TestSetInitialVersion() {
initialVersion := uint64(5)
s.Require().NoError(s.rootStore.SetInitialVersion(initialVersion))
// perform the initial commit
cs := corestore.NewChangeset()
// perform an initial, empty commit
cs := corestore.NewChangeset(initialVersion)
cs.Add(testStoreKeyBytes, []byte("foo"), []byte("bar"), false)
wHash, err := s.rootStore.WorkingHash(cs)
_, err := s.rootStore.Commit(corestore.NewChangeset(initialVersion))
s.Require().NoError(err)
cHash, err := s.rootStore.Commit(corestore.NewChangeset())
s.Require().NoError(err)
s.Require().Equal(wHash, cHash)
// check the latest version
lVersion, err := s.rootStore.GetLatestVersion()
@ -135,8 +130,9 @@ func (s *RootStoreTestSuite) TestSetInitialVersion() {
rInitialVersion := uint64(100)
s.Require().NoError(s.rootStore.SetInitialVersion(rInitialVersion))
// TODO fix version munging here
// perform the commit
cs = corestore.NewChangeset()
cs = corestore.NewChangeset(initialVersion + 1)
cs.Add(testStoreKey2Bytes, []byte("foo"), []byte("bar"), false)
_, err = s.rootStore.Commit(cs)
s.Require().NoError(err)
@ -147,23 +143,12 @@ func (s *RootStoreTestSuite) TestSetInitialVersion() {
s.Require().Equal(initialVersion+1, lVersion)
}
func (s *RootStoreTestSuite) TestSetCommitHeader() {
h := &coreheader.Info{
Height: 100,
Hash: []byte("foo"),
ChainID: "test",
}
s.rootStore.SetCommitHeader(h)
s.Require().Equal(h, s.rootStore.(*Store).commitHeader)
}
func (s *RootStoreTestSuite) TestQuery() {
_, err := s.rootStore.Query([]byte{}, 1, []byte("foo"), true)
s.Require().Error(err)
// write and commit a changeset
cs := corestore.NewChangeset()
cs := corestore.NewChangeset(1)
cs.Add(testStoreKeyBytes, []byte("foo"), []byte("bar"), false)
commitHash, err := s.rootStore.Commit(cs)
@ -181,14 +166,13 @@ func (s *RootStoreTestSuite) TestGetFallback() {
sc := s.rootStore.GetStateCommitment()
// create a changeset and commit it to SC ONLY
cs := corestore.NewChangeset()
cs := corestore.NewChangeset(1)
cs.Add(testStoreKeyBytes, []byte("foo"), []byte("bar"), false)
err := sc.WriteChangeset(cs)
s.Require().NoError(err)
ci := sc.WorkingCommitInfo(1)
_, err = sc.Commit(ci.Version)
_, err = sc.Commit(cs.Version)
s.Require().NoError(err)
// ensure we can query for the key, which should fallback to SC
@ -203,7 +187,7 @@ func (s *RootStoreTestSuite) TestGetFallback() {
}
func (s *RootStoreTestSuite) TestQueryProof() {
cs := corestore.NewChangeset()
cs := corestore.NewChangeset(1)
// testStoreKey
cs.Add(testStoreKeyBytes, []byte("key1"), []byte("value1"), false)
cs.Add(testStoreKeyBytes, []byte("key2"), []byte("value2"), false)
@ -233,10 +217,10 @@ func (s *RootStoreTestSuite) TestQueryProof() {
func (s *RootStoreTestSuite) TestLoadVersion() {
// write and commit a few changesets
for v := 1; v <= 5; v++ {
for v := uint64(1); v <= 5; v++ {
val := fmt.Sprintf("val%03d", v) // val001, val002, ..., val005
cs := corestore.NewChangeset()
cs := corestore.NewChangeset(v)
cs.Add(testStoreKeyBytes, []byte("key"), []byte(val), false)
commitHash, err := s.rootStore.Commit(cs)
@ -276,7 +260,7 @@ func (s *RootStoreTestSuite) TestLoadVersion() {
for v := 4; v <= 5; v++ {
val := fmt.Sprintf("overwritten_val%03d", v) // overwritten_val004, overwritten_val005
cs := corestore.NewChangeset()
cs := corestore.NewChangeset(uint64(v))
cs.Add(testStoreKeyBytes, []byte("key"), []byte(val), false)
commitHash, err := s.rootStore.Commit(cs)
@ -306,7 +290,7 @@ func (s *RootStoreTestSuite) TestCommit() {
s.Require().Zero(lv)
// perform changes
cs := corestore.NewChangeset()
cs := corestore.NewChangeset(1)
for i := 0; i < 100; i++ {
key := fmt.Sprintf("key%03d", i) // key000, key001, ..., key099
val := fmt.Sprintf("val%03d", i) // val000, val001, ..., val099
@ -344,7 +328,7 @@ func (s *RootStoreTestSuite) TestStateAt() {
// write keys over multiple versions
for v := uint64(1); v <= 5; v++ {
// perform changes
cs := corestore.NewChangeset()
cs := corestore.NewChangeset(v)
for i := 0; i < 100; i++ {
key := fmt.Sprintf("key%03d", i) // key000, key001, ..., key099
val := fmt.Sprintf("val%03d_%03d", i, v) // val000_1, val001_1, ..., val099_1
@ -393,32 +377,9 @@ func (s *RootStoreTestSuite) TestStateAt() {
}
}
func (s *RootStoreTestSuite) TestWorkingHash() {
// write keys over multiple versions
for v := uint64(1); v <= 5; v++ {
// perform changes
cs := corestore.NewChangeset()
for _, storeKeyBytes := range [][]byte{testStoreKeyBytes, testStoreKey2Bytes, testStoreKey3Bytes} {
for i := 0; i < 100; i++ {
key := fmt.Sprintf("key_%x_%03d", i, storeKeyBytes) // key000, key001, ..., key099
val := fmt.Sprintf("val%03d_%03d", i, v) // val000_1, val001_1, ..., val099_1
cs.Add(storeKeyBytes, []byte(key), []byte(val), false)
}
}
wHash, err := s.rootStore.WorkingHash(cs)
s.Require().NoError(err)
// execute Commit with empty changeset
cHash, err := s.rootStore.Commit(corestore.NewChangeset())
s.Require().NoError(err)
s.Require().Equal(wHash, cHash)
}
}
func (s *RootStoreTestSuite) TestPrune() {
// perform changes
cs := corestore.NewChangeset()
cs := corestore.NewChangeset(1)
for i := 0; i < 10; i++ {
key := fmt.Sprintf("key%03d", i) // key000, key001, ..., key099
val := fmt.Sprintf("val%03d", i) // val000, val001, ..., val099
@ -462,6 +423,7 @@ func (s *RootStoreTestSuite) TestPrune() {
// write keys over multiple versions
for i := int64(0); i < tc.numVersions; i++ {
// execute Commit
cs.Version = uint64(i + 1)
cHash, err := s.rootStore.Commit(cs)
s.Require().NoError(err)
s.Require().NotNil(cHash)
@ -500,7 +462,7 @@ func (s *RootStoreTestSuite) TestPrune() {
func (s *RootStoreTestSuite) TestMultiStore_Pruning_SameHeightsTwice() {
// perform changes
cs := corestore.NewChangeset()
cs := corestore.NewChangeset(1)
cs.Add(testStoreKeyBytes, []byte("key"), []byte("val"), false)
const (
@ -517,6 +479,7 @@ func (s *RootStoreTestSuite) TestMultiStore_Pruning_SameHeightsTwice() {
for i := uint64(0); i < numVersions; i++ {
// execute Commit
cs.Version = i + 1
cHash, err := s.rootStore.Commit(cs)
s.Require().NoError(err)
s.Require().NotNil(cHash)
@ -544,21 +507,23 @@ func (s *RootStoreTestSuite) TestMultiStore_Pruning_SameHeightsTwice() {
}
// Get latest
err = s.rootStore.LoadVersion(numVersions - 1)
err = s.rootStore.LoadVersion(numVersions)
s.Require().NoError(err)
// Test pruning the same heights again
cs.Version++
_, err = s.rootStore.Commit(cs)
s.Require().NoError(err)
// Ensure that can commit one more height with no panic
cs.Version++
_, err = s.rootStore.Commit(cs)
s.Require().NoError(err)
}
func (s *RootStoreTestSuite) TestMultiStore_PruningRestart() {
// perform changes
cs := corestore.NewChangeset()
cs := corestore.NewChangeset(1)
cs.Add(testStoreKeyBytes, []byte("key"), []byte("val"), false)
pruneOpt := &store.PruningOption{
@ -585,8 +550,9 @@ func (s *RootStoreTestSuite) TestMultiStore_PruningRestart() {
// Commit enough to build up heights to prune, where on the next block we should
// batch delete.
for i := uint64(0); i < 10; i++ {
for i := uint64(1); i <= 10; i++ {
// execute Commit
cs.Version = i
cHash, err := s.rootStore.Commit(cs)
s.Require().NoError(err)
s.Require().NotNil(cHash)
@ -623,6 +589,7 @@ func (s *RootStoreTestSuite) TestMultiStore_PruningRestart() {
// commit one more block and ensure the heights have been pruned
// execute Commit
cs.Version++
cHash, err := s.rootStore.Commit(cs)
s.Require().NoError(err)
s.Require().NotNil(cHash)
@ -672,7 +639,7 @@ func (s *RootStoreTestSuite) TestMultiStoreRestart() {
// perform changes
for i := 1; i < 3; i++ {
cs := corestore.NewChangeset()
cs := corestore.NewChangeset(uint64(i))
key := fmt.Sprintf("key%03d", i) // key000, key001, ..., key099
val := fmt.Sprintf("val%03d_%03d", i, 1) // val000_1, val001_1, ..., val099_1
@ -699,7 +666,7 @@ func (s *RootStoreTestSuite) TestMultiStoreRestart() {
}
// more changes
cs1 := corestore.NewChangeset()
cs1 := corestore.NewChangeset(3)
key := fmt.Sprintf("key%03d", 3) // key000, key001, ..., key099
val := fmt.Sprintf("val%03d_%03d", 3, 1) // val000_1, val001_1, ..., val099_1
@ -719,7 +686,7 @@ func (s *RootStoreTestSuite) TestMultiStoreRestart() {
s.Require().NoError(err)
s.Require().Equal(uint64(3), latestVer)
cs2 := corestore.NewChangeset()
cs2 := corestore.NewChangeset(4)
key = fmt.Sprintf("key%03d", 4) // key000, key001, ..., key099
val = fmt.Sprintf("val%03d_%03d", 4, 3) // val000_1, val001_1, ..., val099_1
@ -782,7 +749,7 @@ func (s *RootStoreTestSuite) TestMultiStoreRestart() {
func (s *RootStoreTestSuite) TestHashStableWithEmptyCommitAndRestart() {
err := s.rootStore.LoadLatestVersion()
s.Require().Nil(err)
s.Require().NoError(err)
emptyHash := sha256.Sum256([]byte{})
appHash := emptyHash[:]
@ -790,9 +757,11 @@ func (s *RootStoreTestSuite) TestHashStableWithEmptyCommitAndRestart() {
lastCommitID, err := s.rootStore.LastCommitID()
s.Require().Nil(err)
s.Require().Equal(commitID, lastCommitID)
// the hash of a store with no commits is the root hash of a tree with empty hashes as leaves.
// it should not be equal an empty hash.
s.Require().NotEqual(commitID, lastCommitID)
cs := corestore.NewChangeset()
cs := corestore.NewChangeset(1)
cs.Add(testStoreKeyBytes, []byte("key"), []byte("val"), false)
cHash, err := s.rootStore.Commit(cs)
@ -804,7 +773,7 @@ func (s *RootStoreTestSuite) TestHashStableWithEmptyCommitAndRestart() {
s.Require().Equal(uint64(1), latestVersion)
// make an empty commit, it should update version, but not affect hash
cHash, err = s.rootStore.Commit(corestore.NewChangeset())
cHash, err = s.rootStore.Commit(corestore.NewChangeset(2))
s.Require().Nil(err)
s.Require().NotNil(cHash)
latestVersion, err = s.rootStore.GetLatestVersion()

View File

@ -57,7 +57,7 @@ func (s *UpgradeStoreTestSuite) SetupTest() {
toVersion := uint64(20)
keyCount := 10
for version := uint64(1); version <= toVersion; version++ {
cs := corestore.NewChangeset()
cs := corestore.NewChangeset(version)
for _, storeKey := range storeKeys {
for i := 0; i < keyCount; i++ {
cs.Add([]byte(storeKey), []byte(fmt.Sprintf("key-%d-%d", version, i)), []byte(fmt.Sprintf("value-%d-%d", version, i)), false)
@ -127,7 +127,7 @@ func (s *UpgradeStoreTestSuite) TestLoadVersionAndUpgrade() {
newStoreKeys := []string{"newStore1", "newStore2"}
toVersion := uint64(40)
for version := v + 1; version <= toVersion; version++ {
cs := corestore.NewChangeset()
cs := corestore.NewChangeset(version)
for _, storeKey := range newStoreKeys {
for i := 0; i < keyCount; i++ {
cs.Add([]byte(storeKey), []byte(fmt.Sprintf("key-%d-%d", version, i)), []byte(fmt.Sprintf("value-%d-%d", version, i)), false)

View File

@ -69,12 +69,12 @@ func BenchmarkGet(b *testing.B) {
_ = db.Close()
}()
cs := corestore.NewChangesetWithPairs(map[string]corestore.KVPairs{string(storeKey1): {}})
cs := corestore.NewChangesetWithPairs(1, map[string]corestore.KVPairs{string(storeKey1): {}})
for i := 0; i < numKeyVals; i++ {
cs.AddKVPair(storeKey1, corestore.KVPair{Key: keys[i], Value: vals[i]})
}
require.NoError(b, db.ApplyChangeset(1, cs))
require.NoError(b, db.ApplyChangeset(cs))
b.Run(fmt.Sprintf("backend_%s", ty), func(b *testing.B) {
b.ResetTimer()
@ -105,7 +105,8 @@ func BenchmarkApplyChangeset(b *testing.B) {
for i := 0; i < b.N; i++ {
b.StopTimer()
cs := corestore.NewChangesetWithPairs(map[string]corestore.KVPairs{string(storeKey1): {}})
ver := uint64(b.N + 1)
cs := corestore.NewChangesetWithPairs(ver, map[string]corestore.KVPairs{string(storeKey1): {}})
for j := 0; j < 1000; j++ {
key := make([]byte, 128)
val := make([]byte, 128)
@ -119,7 +120,7 @@ func BenchmarkApplyChangeset(b *testing.B) {
}
b.StartTimer()
require.NoError(b, db.ApplyChangeset(uint64(b.N+1), cs))
require.NoError(b, db.ApplyChangeset(cs))
}
})
}
@ -152,12 +153,12 @@ func BenchmarkIterate(b *testing.B) {
b.StopTimer()
cs := corestore.NewChangesetWithPairs(map[string]corestore.KVPairs{string(storeKey1): {}})
cs := corestore.NewChangesetWithPairs(1, map[string]corestore.KVPairs{string(storeKey1): {}})
for i := 0; i < numKeyVals; i++ {
cs.AddKVPair(storeKey1, corestore.KVPair{Key: keys[i], Value: vals[i]})
}
require.NoError(b, db.ApplyChangeset(1, cs))
require.NoError(b, db.ApplyChangeset(cs))
sort.Slice(keys, func(i, j int) bool {
return bytes.Compare(keys[i], keys[j]) < 0

View File

@ -61,7 +61,8 @@ func (s *StorageTestSuite) TestDatabase_VersionedKeys() {
defer db.Close()
for i := uint64(1); i <= 100; i++ {
s.Require().NoError(db.ApplyChangeset(i, corestore.NewChangesetWithPairs(
s.Require().NoError(db.ApplyChangeset(corestore.NewChangesetWithPairs(
i,
map[string]corestore.KVPairs{
storeKey1: {{Key: []byte("key"), Value: []byte(fmt.Sprintf("value%03d", i))}},
},
@ -81,7 +82,8 @@ func (s *StorageTestSuite) TestDatabase_GetVersionedKey() {
defer db.Close()
// store a key at version 1
s.Require().NoError(db.ApplyChangeset(1, corestore.NewChangesetWithPairs(
s.Require().NoError(db.ApplyChangeset(corestore.NewChangesetWithPairs(
1,
map[string]corestore.KVPairs{
storeKey1: {{Key: []byte("key"), Value: []byte("value001")}},
},
@ -97,7 +99,8 @@ func (s *StorageTestSuite) TestDatabase_GetVersionedKey() {
s.Require().True(ok)
// chain progresses to version 11 with an update to key
s.Require().NoError(db.ApplyChangeset(11, corestore.NewChangesetWithPairs(
s.Require().NoError(db.ApplyChangeset(corestore.NewChangesetWithPairs(
11,
map[string]corestore.KVPairs{
storeKey1: {{Key: []byte("key"), Value: []byte("value011")}},
},
@ -122,7 +125,8 @@ func (s *StorageTestSuite) TestDatabase_GetVersionedKey() {
}
// chain progresses to version 15 with a delete to key
s.Require().NoError(db.ApplyChangeset(15, corestore.NewChangesetWithPairs(
s.Require().NoError(db.ApplyChangeset(corestore.NewChangesetWithPairs(
15,
map[string]corestore.KVPairs{storeKey1: {{Key: []byte("key"), Remove: true}}},
)))
@ -154,7 +158,7 @@ func (s *StorageTestSuite) TestDatabase_ApplyChangeset() {
s.Require().NoError(err)
defer db.Close()
cs := corestore.NewChangesetWithPairs(map[string]corestore.KVPairs{storeKey1: {}})
cs := corestore.NewChangesetWithPairs(1, map[string]corestore.KVPairs{storeKey1: {}})
for i := 0; i < 100; i++ {
cs.AddKVPair(storeKey1Bytes, corestore.KVPair{Key: []byte(fmt.Sprintf("key%03d", i)), Value: []byte("value")})
}
@ -165,7 +169,7 @@ func (s *StorageTestSuite) TestDatabase_ApplyChangeset() {
}
}
s.Require().NoError(db.ApplyChangeset(1, cs))
s.Require().NoError(db.ApplyChangeset(cs))
lv, err := db.GetLatestVersion()
s.Require().NoError(err)
@ -241,7 +245,7 @@ func (s *StorageTestSuite) TestDatabase_Iterator() {
s.Require().NoError(err)
defer db.Close()
cs := corestore.NewChangesetWithPairs(map[string]corestore.KVPairs{storeKey1: {}})
cs := corestore.NewChangesetWithPairs(1, map[string]corestore.KVPairs{storeKey1: {}})
for i := 0; i < 100; i++ {
key := fmt.Sprintf("key%03d", i) // key000, key001, ..., key099
val := fmt.Sprintf("val%03d", i) // val000, val001, ..., val099
@ -249,7 +253,7 @@ func (s *StorageTestSuite) TestDatabase_Iterator() {
cs.AddKVPair(storeKey1Bytes, corestore.KVPair{Key: []byte(key), Value: []byte(val), Remove: false})
}
s.Require().NoError(db.ApplyChangeset(1, cs))
s.Require().NoError(db.ApplyChangeset(cs))
// iterator without an end key over multiple versions
for v := uint64(1); v < 5; v++ {
@ -310,7 +314,8 @@ func (s *StorageTestSuite) TestDatabase_Iterator_RangedDeletes() {
s.Require().NoError(err)
defer db.Close()
s.Require().NoError(db.ApplyChangeset(1, corestore.NewChangesetWithPairs(
s.Require().NoError(db.ApplyChangeset(corestore.NewChangesetWithPairs(
1,
map[string]corestore.KVPairs{
storeKey1: {
{Key: []byte("key001"), Value: []byte("value001"), Remove: false},
@ -319,13 +324,15 @@ func (s *StorageTestSuite) TestDatabase_Iterator_RangedDeletes() {
},
)))
s.Require().NoError(db.ApplyChangeset(5, corestore.NewChangesetWithPairs(
s.Require().NoError(db.ApplyChangeset(corestore.NewChangesetWithPairs(
5,
map[string]corestore.KVPairs{
storeKey1: {{Key: []byte("key002"), Value: []byte("value002"), Remove: false}},
},
)))
s.Require().NoError(db.ApplyChangeset(10, corestore.NewChangesetWithPairs(
s.Require().NoError(db.ApplyChangeset(corestore.NewChangesetWithPairs(
10,
map[string]corestore.KVPairs{
storeKey1: {{Key: []byte("key002"), Remove: true}},
},
@ -353,7 +360,7 @@ func (s *StorageTestSuite) TestDatabase_IteratorMultiVersion() {
// for versions 1-49, set all 10 keys
for v := uint64(1); v < 50; v++ {
cs := corestore.NewChangesetWithPairs(map[string]corestore.KVPairs{storeKey1: {}})
cs := corestore.NewChangesetWithPairs(v, map[string]corestore.KVPairs{storeKey1: {}})
for i := 0; i < 10; i++ {
key := fmt.Sprintf("key%03d", i)
val := fmt.Sprintf("val%03d-%03d", i, v)
@ -361,12 +368,12 @@ func (s *StorageTestSuite) TestDatabase_IteratorMultiVersion() {
cs.AddKVPair(storeKey1Bytes, corestore.KVPair{Key: []byte(key), Value: []byte(val)})
}
s.Require().NoError(db.ApplyChangeset(v, cs))
s.Require().NoError(db.ApplyChangeset(cs))
}
// for versions 50-100, only update even keys
for v := uint64(50); v <= 100; v++ {
cs := corestore.NewChangesetWithPairs(map[string]corestore.KVPairs{storeKey1: {}})
cs := corestore.NewChangesetWithPairs(v, map[string]corestore.KVPairs{storeKey1: {}})
for i := 0; i < 10; i++ {
if i%2 == 0 {
key := fmt.Sprintf("key%03d", i)
@ -376,7 +383,7 @@ func (s *StorageTestSuite) TestDatabase_IteratorMultiVersion() {
}
}
s.Require().NoError(db.ApplyChangeset(v, cs))
s.Require().NoError(db.ApplyChangeset(cs))
}
itr, err := db.Iterator(storeKey1Bytes, 69, []byte("key000"), nil)
@ -519,7 +526,7 @@ func (s *StorageTestSuite) TestDatabase_IteratorNoDomain() {
// for versions 1-50, set all 10 keys
for v := uint64(1); v <= 50; v++ {
cs := corestore.NewChangesetWithPairs(map[string]corestore.KVPairs{storeKey1: {}})
cs := corestore.NewChangesetWithPairs(v, map[string]corestore.KVPairs{storeKey1: {}})
for i := 0; i < 10; i++ {
key := fmt.Sprintf("key%03d", i)
val := fmt.Sprintf("val%03d-%03d", i, v)
@ -527,7 +534,7 @@ func (s *StorageTestSuite) TestDatabase_IteratorNoDomain() {
cs.AddKVPair(storeKey1Bytes, corestore.KVPair{Key: []byte(key), Value: []byte(val), Remove: false})
}
s.Require().NoError(db.ApplyChangeset(v, cs))
s.Require().NoError(db.ApplyChangeset(cs))
}
// create an iterator over the entire domain
@ -559,7 +566,7 @@ func (s *StorageTestSuite) TestDatabase_Prune() {
// for versions 1-50, set 10 keys
for v := uint64(1); v <= 50; v++ {
cs := corestore.NewChangesetWithPairs(map[string]corestore.KVPairs{storeKey1: {}})
cs := corestore.NewChangesetWithPairs(v, map[string]corestore.KVPairs{storeKey1: {}})
for i := 0; i < 10; i++ {
key := fmt.Sprintf("key%03d", i)
val := fmt.Sprintf("val%03d-%03d", i, v)
@ -567,7 +574,7 @@ func (s *StorageTestSuite) TestDatabase_Prune() {
cs.AddKVPair(storeKey1Bytes, corestore.KVPair{Key: []byte(key), Value: []byte(val)})
}
s.Require().NoError(db.ApplyChangeset(v, cs))
s.Require().NoError(db.ApplyChangeset(cs))
}
// prune the first 25 versions
@ -625,13 +632,16 @@ func (s *StorageTestSuite) TestDatabase_Prune_KeepRecent() {
key := []byte("key")
// write a key at three different versions
s.Require().NoError(db.ApplyChangeset(1, corestore.NewChangesetWithPairs(
s.Require().NoError(db.ApplyChangeset(corestore.NewChangesetWithPairs(
1,
map[string]corestore.KVPairs{storeKey1: {{Key: key, Value: []byte("val001"), Remove: false}}},
)))
s.Require().NoError(db.ApplyChangeset(100, corestore.NewChangesetWithPairs(
s.Require().NoError(db.ApplyChangeset(corestore.NewChangesetWithPairs(
100,
map[string]corestore.KVPairs{storeKey1: {{Key: key, Value: []byte("val100"), Remove: false}}},
)))
s.Require().NoError(db.ApplyChangeset(200, corestore.NewChangesetWithPairs(
s.Require().NoError(db.ApplyChangeset(corestore.NewChangesetWithPairs(
200,
map[string]corestore.KVPairs{storeKey1: {{Key: key, Value: []byte("val200"), Remove: false}}},
)))
@ -677,7 +687,7 @@ func (s *StorageTestSuite) TestDatabase_Restore() {
// for versions 1-10, set 10 keys
for v := uint64(1); v <= toVersion; v++ {
cs := corestore.NewChangesetWithPairs(map[string]corestore.KVPairs{storeKey1: {}})
cs := corestore.NewChangesetWithPairs(v, map[string]corestore.KVPairs{storeKey1: {}})
for i := 0; i < keyCount; i++ {
key := fmt.Sprintf("key%03d", i)
val := fmt.Sprintf("val%03d-%03d", i, v)
@ -685,7 +695,7 @@ func (s *StorageTestSuite) TestDatabase_Restore() {
cs.AddKVPair(storeKey1Bytes, corestore.KVPair{Key: []byte(key), Value: []byte(val)})
}
s.Require().NoError(db.ApplyChangeset(v, cs))
s.Require().NoError(db.ApplyChangeset(cs))
}
latestVersion, err := db.GetLatestVersion()
@ -1032,7 +1042,7 @@ func dbApplyChangeset(
require.Greater(t, version, uint64(0))
require.Equal(t, len(keys), len(vals))
cs := corestore.NewChangeset()
cs := corestore.NewChangeset(version)
for i := 0; i < len(keys); i++ {
remove := false
if vals[i] == nil {
@ -1042,5 +1052,5 @@ func dbApplyChangeset(
cs.AddKVPair([]byte(storeKey), corestore.KVPair{Key: keys[i], Value: vals[i], Remove: remove})
}
require.NoError(t, db.ApplyChangeset(version, cs))
require.NoError(t, db.ApplyChangeset(cs))
}

View File

@ -47,8 +47,8 @@ func (ss *StorageStore) Get(storeKey []byte, version uint64, key []byte) ([]byte
}
// ApplyChangeset applies the given changeset to the storage.
func (ss *StorageStore) ApplyChangeset(version uint64, cs *corestore.Changeset) error {
b, err := ss.db.NewBatch(version)
func (ss *StorageStore) ApplyChangeset(cs *corestore.Changeset) error {
b, err := ss.db.NewBatch(cs.Version)
if err != nil {
return err
}

View File

@ -3,7 +3,6 @@ package store
import (
"io"
coreheader "cosmossdk.io/core/header"
corestore "cosmossdk.io/core/store"
"cosmossdk.io/store/v2/metrics"
"cosmossdk.io/store/v2/proof"
@ -41,17 +40,6 @@ type RootStore interface {
// SetInitialVersion sets the initial version on the RootStore.
SetInitialVersion(v uint64) error
// SetCommitHeader sets the commit header for the next commit. This call and
// implementation is optional. However, it must be supported in cases where
// queries based on block time need to be supported.
SetCommitHeader(h *coreheader.Info)
// WorkingHash returns the current WIP commitment hash by applying the Changeset
// to the SC backend. It is only used to get the hash of the intermediate state
// before committing, the typical use case is for the genesis block.
// NOTE: It also writes the changeset to the SS backend.
WorkingHash(cs *corestore.Changeset) ([]byte, error)
// Commit should be responsible for taking the provided changeset and flushing
// it to disk. Note, it will overwrite the changeset if WorkingHash() was called.
// Commit() should ensure the changeset is committed to all SC and SS backends

View File

@ -248,6 +248,7 @@ replace (
cosmossdk.io/api => ../api
cosmossdk.io/client/v2 => ../client/v2
cosmossdk.io/collections => ../collections
cosmossdk.io/core => ../core
cosmossdk.io/core/testing => ../core/testing
cosmossdk.io/indexer/postgres => ../indexer/postgres
cosmossdk.io/runtime/v2 => ../runtime/v2

View File

@ -192,8 +192,6 @@ cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xX
cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg=
cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0=
cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M=
cosmossdk.io/core v1.0.0-alpha.5 h1:McjYXAQ6XcT20v2uHyH7PhoWH8V+mebzfVFqT3GinsI=
cosmossdk.io/core v1.0.0-alpha.5/go.mod h1:3u9cWq1FAVtiiCrDPpo4LhR+9V6k/ycSG4/Y/tREWCY=
cosmossdk.io/depinject v1.1.0 h1:wLan7LG35VM7Yo6ov0jId3RHWCGRhe8E8bsuARorl5E=
cosmossdk.io/depinject v1.1.0/go.mod h1:kkI5H9jCGHeKeYWXTqYdruogYrEeWvBQCw1Pj4/eCFI=
cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0=

View File

@ -180,12 +180,12 @@ func NewApp(
if store == nil {
return nil, fmt.Errorf("failed to build store: %w", err)
}
err = store.SetInitialVersion(1)
err = store.SetInitialVersion(0)
if err != nil {
return nil, fmt.Errorf("failed to set initial version: %w", err)
}
integrationApp := &App{App: app, Store: store, txConfig: txConfig, lastHeight: 1}
integrationApp := &App{App: app, Store: store, txConfig: txConfig, lastHeight: 0}
if startupConfig.GenesisBehavior == Genesis_SKIP {
return integrationApp, nil
}
@ -316,7 +316,7 @@ func (a *App) Commit(state corestore.WriterMap) ([]byte, error) {
if err != nil {
return nil, fmt.Errorf("failed to get state changes: %w", err)
}
cs := &corestore.Changeset{Changes: changes}
cs := &corestore.Changeset{Version: a.lastHeight, Changes: changes}
return a.Store.Commit(cs)
}