cosmos-sdk/store/v2/root/store_test.go
Alexander Peters 9a92843c21
store/v2: remove auto migration and fix restore cmd (#23568)
Co-authored-by: mmsqe <mavis@crypto.com>
Co-authored-by: Marko <marko@baricevic.me>
Co-authored-by: marbar3778 <marbar3778@yahoo.com>
2025-01-31 17:34:44 +00:00

831 lines
25 KiB
Go

package root
import (
"crypto/sha256"
"fmt"
"testing"
"time"
"github.com/stretchr/testify/suite"
corestore "cosmossdk.io/core/store"
coretesting "cosmossdk.io/core/testing"
"cosmossdk.io/store/v2"
"cosmossdk.io/store/v2/commitment"
"cosmossdk.io/store/v2/commitment/iavl"
dbm "cosmossdk.io/store/v2/db"
"cosmossdk.io/store/v2/proof"
"cosmossdk.io/store/v2/pruning"
)
const (
testStoreKey = "test_store_key"
testStoreKey2 = "test_store_key2"
testStoreKey3 = "test_store_key3"
)
var testStoreKeys = []string{testStoreKey, testStoreKey2, testStoreKey3}
var (
testStoreKeyBytes = []byte(testStoreKey)
testStoreKey2Bytes = []byte(testStoreKey2)
testStoreKey3Bytes = []byte(testStoreKey3)
)
type RootStoreTestSuite struct {
suite.Suite
rootStore store.RootStore
}
func TestStorageTestSuite(t *testing.T) {
suite.Run(t, &RootStoreTestSuite{})
}
func (s *RootStoreTestSuite) SetupTest() {
noopLog := coretesting.NewNopLogger()
tree := iavl.NewIavlTree(dbm.NewMemDB(), noopLog, iavl.DefaultConfig())
tree2 := iavl.NewIavlTree(dbm.NewMemDB(), noopLog, iavl.DefaultConfig())
tree3 := iavl.NewIavlTree(dbm.NewMemDB(), noopLog, iavl.DefaultConfig())
sc, err := commitment.NewCommitStore(map[string]commitment.Tree{testStoreKey: tree, testStoreKey2: tree2, testStoreKey3: tree3}, nil, dbm.NewMemDB(), noopLog)
s.Require().NoError(err)
pm := pruning.NewManager(sc, nil)
rs, err := New(dbm.NewMemDB(), noopLog, sc, pm, nil)
s.Require().NoError(err)
s.rootStore = rs
}
func (s *RootStoreTestSuite) newStoreWithPruneConfig(config *store.PruningOption) {
noopLog := coretesting.NewNopLogger()
mdb := dbm.NewMemDB()
multiTrees := make(map[string]commitment.Tree)
for _, storeKey := range testStoreKeys {
prefixDB := dbm.NewPrefixDB(mdb, []byte(storeKey))
multiTrees[storeKey] = iavl.NewIavlTree(prefixDB, noopLog, iavl.DefaultConfig())
}
sc, err := commitment.NewCommitStore(multiTrees, nil, dbm.NewMemDB(), noopLog)
s.Require().NoError(err)
pm := pruning.NewManager(sc, config)
rs, err := New(dbm.NewMemDB(), noopLog, sc, pm, nil)
s.Require().NoError(err)
s.rootStore = rs
}
func (s *RootStoreTestSuite) newStoreWithBackendMount(sc store.Committer, pm *pruning.Manager) {
noopLog := coretesting.NewNopLogger()
rs, err := New(dbm.NewMemDB(), noopLog, sc, pm, nil)
s.Require().NoError(err)
s.rootStore = rs
}
func (s *RootStoreTestSuite) TearDownTest() {
err := s.rootStore.Close()
s.Require().NoError(err)
}
func (s *RootStoreTestSuite) TestGetStateCommitment() {
s.Require().Equal(s.rootStore.GetStateCommitment(), s.rootStore.(*Store).stateCommitment)
}
func (s *RootStoreTestSuite) TestSetInitialVersion() {
initialVersion := uint64(5)
s.Require().NoError(s.rootStore.SetInitialVersion(initialVersion))
// perform an initial, empty commit
cs := corestore.NewChangeset(initialVersion)
cs.Add(testStoreKeyBytes, []byte("foo"), []byte("bar"), false)
_, err := s.rootStore.Commit(corestore.NewChangeset(initialVersion))
s.Require().NoError(err)
// check the latest version
lVersion, err := s.rootStore.GetLatestVersion()
s.Require().NoError(err)
s.Require().Equal(initialVersion, lVersion)
// set the initial version again
rInitialVersion := uint64(100)
s.Require().NoError(s.rootStore.SetInitialVersion(rInitialVersion))
// TODO fix version munging here
// perform the commit
cs = corestore.NewChangeset(initialVersion + 1)
cs.Add(testStoreKey2Bytes, []byte("foo"), []byte("bar"), false)
_, err = s.rootStore.Commit(cs)
s.Require().NoError(err)
lVersion, err = s.rootStore.GetLatestVersion()
s.Require().NoError(err)
// SetInitialVersion only works once
s.Require().NotEqual(rInitialVersion, lVersion)
s.Require().Equal(initialVersion+1, lVersion)
}
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(1)
cs.Add(testStoreKeyBytes, []byte("foo"), []byte("bar"), false)
commitHash, err := s.rootStore.Commit(cs)
s.Require().NoError(err)
s.Require().NotNil(commitHash)
// ensure the proof is non-nil for the corresponding version
result, err := s.rootStore.Query([]byte(testStoreKey), 1, []byte("foo"), true)
s.Require().NoError(err)
s.Require().NotNil(result.ProofOps)
s.Require().Equal([]byte("foo"), result.ProofOps[0].Key)
}
func (s *RootStoreTestSuite) TestGetFallback() {
sc := s.rootStore.GetStateCommitment()
// create a changeset and commit it to SC ONLY
cs := corestore.NewChangeset(1)
cs.Add(testStoreKeyBytes, []byte("foo"), []byte("bar"), false)
err := sc.WriteChangeset(cs)
s.Require().NoError(err)
_, err = sc.Commit(cs.Version)
s.Require().NoError(err)
// ensure we can query for the key, which should fallback to SC
qResult, err := s.rootStore.Query(testStoreKeyBytes, 1, []byte("foo"), false)
s.Require().NoError(err)
s.Require().Equal([]byte("bar"), qResult.Value)
// non-existent key
qResult, err = s.rootStore.Query(testStoreKeyBytes, 1, []byte("non_existent_key"), false)
s.Require().NoError(err)
s.Require().Nil(qResult.Value)
}
func (s *RootStoreTestSuite) TestQueryProof() {
cs := corestore.NewChangeset(1)
// testStoreKey
cs.Add(testStoreKeyBytes, []byte("key1"), []byte("value1"), false)
cs.Add(testStoreKeyBytes, []byte("key2"), []byte("value2"), false)
// testStoreKey2
cs.Add(testStoreKey2Bytes, []byte("key3"), []byte("value3"), false)
// testStoreKey3
cs.Add(testStoreKey3Bytes, []byte("key4"), []byte("value4"), false)
// commit
_, err := s.rootStore.Commit(cs)
s.Require().NoError(err)
// query proof for testStoreKey
result, err := s.rootStore.Query(testStoreKeyBytes, 1, []byte("key1"), true)
s.Require().NoError(err)
s.Require().NotNil(result.ProofOps)
cInfo, err := s.rootStore.GetStateCommitment().GetCommitInfo(1)
s.Require().NoError(err)
storeHash := cInfo.GetStoreCommitID(testStoreKeyBytes).Hash
treeRoots, err := result.ProofOps[0].Run([][]byte{[]byte("value1")})
s.Require().NoError(err)
s.Require().Equal(treeRoots[0], storeHash)
expRoots, err := result.ProofOps[1].Run([][]byte{storeHash})
s.Require().NoError(err)
s.Require().Equal(expRoots[0], cInfo.Hash())
}
func (s *RootStoreTestSuite) TestLoadVersion() {
// write and commit a few changesets
for v := uint64(1); v <= 5; v++ {
val := fmt.Sprintf("val%03d", v) // val001, val002, ..., val005
cs := corestore.NewChangeset(v)
cs.Add(testStoreKeyBytes, []byte("key"), []byte(val), false)
commitHash, err := s.rootStore.Commit(cs)
s.Require().NoError(err)
s.Require().NotNil(commitHash)
}
// ensure the latest version is correct
latest, err := s.rootStore.GetLatestVersion()
s.Require().NoError(err)
s.Require().Equal(uint64(5), latest)
// attempt to load a non-existent version
err = s.rootStore.LoadVersion(6)
s.Require().Error(err)
// attempt to load a previously committed version
err = s.rootStore.LoadVersion(3)
s.Require().NoError(err)
// ensure the latest version is correct
latest, err = s.rootStore.GetLatestVersion()
s.Require().NoError(err)
s.Require().Equal(uint64(3), latest)
// query state and ensure values returned are based on the loaded version
_, ro, err := s.rootStore.StateLatest()
s.Require().NoError(err)
reader, err := ro.GetReader(testStoreKeyBytes)
s.Require().NoError(err)
val, err := reader.Get([]byte("key"))
s.Require().NoError(err)
s.Require().Equal([]byte("val003"), val)
// attempt to write and commit a few changesets
for v := 4; v <= 5; v++ {
val := fmt.Sprintf("overwritten_val%03d", v) // overwritten_val004, overwritten_val005
cs := corestore.NewChangeset(uint64(v))
cs.Add(testStoreKeyBytes, []byte("key"), []byte(val), false)
_, err := s.rootStore.Commit(cs)
s.Require().Error(err)
}
// ensure the latest version is correct
latest, err = s.rootStore.GetLatestVersion()
s.Require().NoError(err)
s.Require().Equal(uint64(3), latest) // should have stayed at 3 after failed commits
// query state and ensure values returned are based on the loaded version
_, ro, err = s.rootStore.StateLatest()
s.Require().NoError(err)
reader, err = ro.GetReader(testStoreKeyBytes)
s.Require().NoError(err)
val, err = reader.Get([]byte("key"))
s.Require().NoError(err)
s.Require().Equal([]byte("val003"), val)
}
func (s *RootStoreTestSuite) TestLoadVersionForOverwriting() {
// write and commit a few changesets
for v := uint64(1); v <= 5; v++ {
val := fmt.Sprintf("val%03d", v) // val001, val002, ..., val005
cs := corestore.NewChangeset(v)
cs.Add(testStoreKeyBytes, []byte("key"), []byte(val), false)
commitHash, err := s.rootStore.Commit(cs)
s.Require().NoError(err)
s.Require().NotNil(commitHash)
}
// ensure the latest version is correct
latest, err := s.rootStore.GetLatestVersion()
s.Require().NoError(err)
s.Require().Equal(uint64(5), latest)
// attempt to load a non-existent version
err = s.rootStore.LoadVersionForOverwriting(6)
s.Require().Error(err)
// attempt to load a previously committed version
err = s.rootStore.LoadVersionForOverwriting(3)
s.Require().NoError(err)
// ensure the latest version is correct
latest, err = s.rootStore.GetLatestVersion()
s.Require().NoError(err)
s.Require().Equal(uint64(3), latest)
// query state and ensure values returned are based on the loaded version
_, ro, err := s.rootStore.StateLatest()
s.Require().NoError(err)
reader, err := ro.GetReader(testStoreKeyBytes)
s.Require().NoError(err)
val, err := reader.Get([]byte("key"))
s.Require().NoError(err)
s.Require().Equal([]byte("val003"), val)
// attempt to write and commit a few changesets
for v := 4; v <= 5; v++ {
val := fmt.Sprintf("overwritten_val%03d", v) // overwritten_val004, overwritten_val005
cs := corestore.NewChangeset(uint64(v))
cs.Add(testStoreKeyBytes, []byte("key"), []byte(val), false)
commitHash, err := s.rootStore.Commit(cs)
s.Require().NoError(err)
s.Require().NotNil(commitHash)
}
// ensure the latest version is correct
latest, err = s.rootStore.GetLatestVersion()
s.Require().NoError(err)
s.Require().Equal(uint64(5), latest)
// query state and ensure values returned are based on the loaded version
_, ro, err = s.rootStore.StateLatest()
s.Require().NoError(err)
reader, err = ro.GetReader(testStoreKeyBytes)
s.Require().NoError(err)
val, err = reader.Get([]byte("key"))
s.Require().NoError(err)
s.Require().Equal([]byte("overwritten_val005"), val)
}
func (s *RootStoreTestSuite) TestCommit() {
lv, err := s.rootStore.GetLatestVersion()
s.Require().NoError(err)
s.Require().Zero(lv)
// perform changes
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
cs.Add(testStoreKeyBytes, []byte(key), []byte(val), false)
}
cHash, err := s.rootStore.Commit(cs)
s.Require().NoError(err)
s.Require().NotNil(cHash)
// ensure latest version is updated
lv, err = s.rootStore.GetLatestVersion()
s.Require().NoError(err)
s.Require().Equal(uint64(1), lv)
// perform reads on the updated root store
_, ro, err := s.rootStore.StateLatest()
s.Require().NoError(err)
for i := 0; i < 100; i++ {
key := fmt.Sprintf("key%03d", i) // key000, key001, ..., key099
val := fmt.Sprintf("val%03d", i) // val000, val001, ..., val099
reader, err := ro.GetReader(testStoreKeyBytes)
s.Require().NoError(err)
result, err := reader.Get([]byte(key))
s.Require().NoError(err)
s.Require().Equal([]byte(val), result)
}
}
func (s *RootStoreTestSuite) TestStateAt() {
// write keys over multiple versions
for v := uint64(1); v <= 5; v++ {
// perform changes
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
cs.Add(testStoreKeyBytes, []byte(key), []byte(val), false)
}
// execute Commit
cHash, err := s.rootStore.Commit(cs)
s.Require().NoError(err)
s.Require().NotNil(cHash)
}
lv, err := s.rootStore.GetLatestVersion()
s.Require().NoError(err)
s.Require().Equal(uint64(5), lv)
// ensure we can read state correctly at each version
for v := uint64(1); v <= 5; v++ {
ro, err := s.rootStore.StateAt(v)
s.Require().NoError(err)
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
reader, err := ro.GetReader(testStoreKeyBytes)
s.Require().NoError(err)
isExist, err := reader.Has([]byte(key))
s.Require().NoError(err)
s.Require().True(isExist)
result, err := reader.Get([]byte(key))
s.Require().NoError(err)
s.Require().Equal([]byte(val), result)
}
// non-existent key
reader, err := ro.GetReader(testStoreKey2Bytes)
s.Require().NoError(err)
isExist, err := reader.Has([]byte("key"))
s.Require().NoError(err)
s.Require().False(isExist)
v, err := reader.Get([]byte("key"))
s.Require().NoError(err)
s.Require().Nil(v)
}
}
func (s *RootStoreTestSuite) TestPrune() {
// perform changes
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
cs.Add(testStoreKeyBytes, []byte(key), []byte(val), false)
}
testCases := []struct {
name string
numVersions int64
po store.PruningOption
deleted []uint64
saved []uint64
}{
{"prune nothing", 10, store.PruningOption{
KeepRecent: 0,
Interval: 0,
}, nil, []uint64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}},
{"prune everything", 12, store.PruningOption{
KeepRecent: 1,
Interval: 10,
}, []uint64{1, 2, 3, 4, 5, 6, 7, 8}, []uint64{9, 10, 11, 12}},
{"prune some; no batch", 10, store.PruningOption{
KeepRecent: 2,
Interval: 1,
}, []uint64{1, 2, 3, 4, 6, 5, 7}, []uint64{8, 9, 10}},
{"prune some; small batch", 10, store.PruningOption{
KeepRecent: 2,
Interval: 3,
}, []uint64{1, 2, 3, 4, 5, 6}, []uint64{7, 8, 9, 10}},
{"prune some; large batch", 10, store.PruningOption{
KeepRecent: 2,
Interval: 11,
}, nil, []uint64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}},
}
for _, tc := range testCases {
s.newStoreWithPruneConfig(&tc.po)
// 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)
}
for _, v := range tc.saved {
ro, err := s.rootStore.StateAt(v)
s.Require().NoError(err, "expected no error when loading height %d at test %s", v, tc.name)
for i := 0; i < 10; i++ {
key := fmt.Sprintf("key%03d", i) // key000, key001, ..., key099
val := fmt.Sprintf("val%03d", i) // val000, val001, ..., val099
reader, err := ro.GetReader(testStoreKeyBytes)
s.Require().NoError(err)
result, err := reader.Get([]byte(key))
s.Require().NoError(err)
s.Require().Equal([]byte(val), result, "value should be equal for test: %s", tc.name)
}
}
for _, v := range tc.deleted {
var err error
checkErr := func() bool {
if _, err = s.rootStore.StateAt(v); err != nil {
return true
}
return false
}
// wait for async pruning process to finish
s.Require().Eventually(checkErr, 2*time.Second, 100*time.Millisecond)
s.Require().Error(err, "expected error when loading height %d at test %s", v, tc.name)
}
}
}
func (s *RootStoreTestSuite) TestMultiStore_Pruning_SameHeightsTwice() {
// perform changes
cs := corestore.NewChangeset(1)
cs.Add(testStoreKeyBytes, []byte("key"), []byte("val"), false)
const (
numVersions uint64 = 10
keepRecent uint64 = 1
interval uint64 = 10
)
s.newStoreWithPruneConfig(&store.PruningOption{
KeepRecent: keepRecent,
Interval: interval,
})
s.Require().NoError(s.rootStore.LoadLatestVersion())
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)
}
latestVer, err := s.rootStore.GetLatestVersion()
s.Require().NoError(err)
s.Require().Equal(numVersions, latestVer)
for v := uint64(1); v < numVersions-keepRecent; v++ {
var err error
checkErr := func() bool {
if _, err = s.rootStore.StateAt(v); err != nil {
return true
}
return false
}
// wait for async pruning process to finish
s.Require().Eventually(checkErr, 2*time.Second, 100*time.Millisecond, "expected no error when loading height: %d", v)
}
for v := (numVersions - keepRecent); v < numVersions; v++ {
_, err := s.rootStore.StateAt(v)
s.Require().NoError(err, "expected no error when loading height: %d", v)
}
// Get latest
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(1)
cs.Add(testStoreKeyBytes, []byte("key"), []byte("val"), false)
pruneOpt := &store.PruningOption{
KeepRecent: 2,
Interval: 11,
}
noopLog := coretesting.NewNopLogger()
mdb1 := dbm.NewMemDB()
mdb2 := dbm.NewMemDB()
tree := iavl.NewIavlTree(mdb1, noopLog, iavl.DefaultConfig())
sc, err := commitment.NewCommitStore(map[string]commitment.Tree{testStoreKey: tree}, nil, mdb2, noopLog)
s.Require().NoError(err)
pm := pruning.NewManager(sc, pruneOpt)
s.newStoreWithBackendMount(sc, pm)
s.Require().NoError(s.rootStore.LoadLatestVersion())
// Commit enough to build up heights to prune, where on the next block we should
// batch delete.
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)
}
latestVer, err := s.rootStore.GetLatestVersion()
s.Require().NoError(err)
ok, actualHeightToPrune := pruneOpt.ShouldPrune(latestVer)
s.Require().False(ok)
s.Require().Equal(uint64(0), actualHeightToPrune)
tree = iavl.NewIavlTree(mdb1, noopLog, iavl.DefaultConfig())
sc, err = commitment.NewCommitStore(map[string]commitment.Tree{testStoreKey: tree}, nil, mdb2, noopLog)
s.Require().NoError(err)
pm = pruning.NewManager(sc, pruneOpt)
s.newStoreWithBackendMount(sc, pm)
err = s.rootStore.LoadLatestVersion()
s.Require().NoError(err)
latestVer, err = s.rootStore.GetLatestVersion()
s.Require().NoError(err)
ok, actualHeightToPrune = pruneOpt.ShouldPrune(latestVer)
s.Require().False(ok)
s.Require().Equal(uint64(0), actualHeightToPrune)
// 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)
latestVer, err = s.rootStore.GetLatestVersion()
s.Require().NoError(err)
ok, actualHeightToPrune = pruneOpt.ShouldPrune(latestVer)
s.Require().True(ok)
s.Require().Equal(uint64(8), actualHeightToPrune)
for v := uint64(1); v <= actualHeightToPrune; v++ {
checkErr := func() bool {
if _, err = s.rootStore.StateAt(v); err != nil {
return true
}
return false
}
// wait for async pruning process to finish
s.Require().Eventually(checkErr, 10*time.Second, 1*time.Second, "expected error when loading height: %d", v)
}
}
func (s *RootStoreTestSuite) TestMultiStoreRestart() {
noopLog := coretesting.NewNopLogger()
mdb1 := dbm.NewMemDB()
mdb2 := dbm.NewMemDB()
multiTrees := make(map[string]commitment.Tree)
for _, storeKey := range testStoreKeys {
prefixDB := dbm.NewPrefixDB(mdb1, []byte(storeKey))
multiTrees[storeKey] = iavl.NewIavlTree(prefixDB, noopLog, iavl.DefaultConfig())
}
sc, err := commitment.NewCommitStore(multiTrees, nil, mdb2, noopLog)
s.Require().NoError(err)
pm := pruning.NewManager(sc, nil)
s.newStoreWithBackendMount(sc, pm)
s.Require().NoError(s.rootStore.LoadLatestVersion())
// perform changes
for i := 1; i < 3; i++ {
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
cs.Add(testStoreKeyBytes, []byte(key), []byte(val), false)
key = fmt.Sprintf("key%03d", i) // key000, key001, ..., key099
val = fmt.Sprintf("val%03d_%03d", i, 2) // val000_1, val001_1, ..., val099_1
cs.Add(testStoreKey2Bytes, []byte(key), []byte(val), false)
key = fmt.Sprintf("key%03d", i) // key000, key001, ..., key099
val = fmt.Sprintf("val%03d_%03d", i, 3) // val000_1, val001_1, ..., val099_1
cs.Add(testStoreKey3Bytes, []byte(key), []byte(val), false)
// execute Commit
cHash, err := s.rootStore.Commit(cs)
s.Require().NoError(err)
s.Require().NotNil(cHash)
latestVer, err := s.rootStore.GetLatestVersion()
s.Require().NoError(err)
s.Require().Equal(uint64(i), latestVer)
}
// more changes
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
cs1.Add(testStoreKeyBytes, []byte(key), []byte(val), false)
key = fmt.Sprintf("key%03d", 3) // key000, key001, ..., key099
val = fmt.Sprintf("val%03d_%03d", 3, 2) // val000_1, val001_1, ..., val099_1
cs1.Add(testStoreKey2Bytes, []byte(key), []byte(val), false)
// execute Commit
cHash, err := s.rootStore.Commit(cs1)
s.Require().NoError(err)
s.Require().NotNil(cHash)
latestVer, err := s.rootStore.GetLatestVersion()
s.Require().NoError(err)
s.Require().Equal(uint64(3), latestVer)
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
cs2.Add(testStoreKey3Bytes, []byte(key), []byte(val), false)
// execute Commit
cHash, err = s.rootStore.Commit(cs2)
s.Require().NoError(err)
s.Require().NotNil(cHash)
latestVer, err = s.rootStore.GetLatestVersion()
s.Require().NoError(err)
s.Require().Equal(uint64(4), latestVer)
_, ro1, err := s.rootStore.StateLatest()
s.Require().Nil(err)
reader1, err := ro1.GetReader(testStoreKeyBytes)
s.Require().NoError(err)
result1, err := reader1.Get([]byte(fmt.Sprintf("key%03d", 3)))
s.Require().NoError(err)
s.Require().Equal([]byte(fmt.Sprintf("val%03d_%03d", 3, 1)), result1, "value should be equal")
// "restart"
multiTrees = make(map[string]commitment.Tree)
for _, storeKey := range testStoreKeys {
prefixDB := dbm.NewPrefixDB(mdb1, []byte(storeKey))
multiTrees[storeKey] = iavl.NewIavlTree(prefixDB, noopLog, iavl.DefaultConfig())
}
sc, err = commitment.NewCommitStore(multiTrees, nil, mdb2, noopLog)
s.Require().NoError(err)
pm = pruning.NewManager(sc, nil)
s.newStoreWithBackendMount(sc, pm)
err = s.rootStore.LoadLatestVersion()
s.Require().Nil(err)
latestVer, ro, err := s.rootStore.StateLatest()
s.Require().Nil(err)
s.Require().Equal(uint64(4), latestVer)
reader, err := ro.GetReader(testStoreKeyBytes)
s.Require().NoError(err)
result, err := reader.Get([]byte(fmt.Sprintf("key%03d", 3)))
s.Require().NoError(err)
s.Require().Equal([]byte(fmt.Sprintf("val%03d_%03d", 3, 1)), result, "value should be equal")
reader, err = ro.GetReader(testStoreKey2Bytes)
s.Require().NoError(err)
result, err = reader.Get([]byte(fmt.Sprintf("key%03d", 2)))
s.Require().NoError(err)
s.Require().Equal([]byte(fmt.Sprintf("val%03d_%03d", 2, 2)), result, "value should be equal")
reader, err = ro.GetReader(testStoreKey3Bytes)
s.Require().NoError(err)
result, err = reader.Get([]byte(fmt.Sprintf("key%03d", 4)))
s.Require().NoError(err)
s.Require().Equal([]byte(fmt.Sprintf("val%03d_%03d", 4, 3)), result, "value should be equal")
}
func (s *RootStoreTestSuite) TestHashStableWithEmptyCommitAndRestart() {
err := s.rootStore.LoadLatestVersion()
s.Require().NoError(err)
emptyHash := sha256.Sum256([]byte{})
appHash := emptyHash[:]
commitID := proof.CommitID{Hash: appHash}
lastCommitID, err := s.rootStore.LastCommitID()
s.Require().Nil(err)
// 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(1)
cs.Add(testStoreKeyBytes, []byte("key"), []byte("val"), false)
cHash, err := s.rootStore.Commit(cs)
s.Require().Nil(err)
s.Require().NotNil(cHash)
latestVersion, err := s.rootStore.GetLatestVersion()
hash := cHash
s.Require().Nil(err)
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(2))
s.Require().Nil(err)
s.Require().NotNil(cHash)
latestVersion, err = s.rootStore.GetLatestVersion()
s.Require().Nil(err)
s.Require().Equal(uint64(2), latestVersion)
s.Require().Equal(hash, cHash)
// reload the store
s.Require().NoError(s.rootStore.LoadLatestVersion())
lastCommitID, err = s.rootStore.LastCommitID()
s.Require().NoError(err)
s.Require().Equal(lastCommitID.Hash, hash)
}