github.com/Finschia/finschia-sdk@v0.49.1/store/rootmulti/store_test.go (about)

     1  package rootmulti
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/Finschia/ostracon/libs/log"
    10  	"github.com/stretchr/testify/require"
    11  	abci "github.com/tendermint/tendermint/abci/types"
    12  	dbm "github.com/tendermint/tm-db"
    13  
    14  	"github.com/Finschia/finschia-sdk/codec"
    15  	codecTypes "github.com/Finschia/finschia-sdk/codec/types"
    16  	"github.com/Finschia/finschia-sdk/store/cachemulti"
    17  	"github.com/Finschia/finschia-sdk/store/iavl"
    18  	sdkmaps "github.com/Finschia/finschia-sdk/store/internal/maps"
    19  	"github.com/Finschia/finschia-sdk/store/listenkv"
    20  	"github.com/Finschia/finschia-sdk/store/types"
    21  	sdkerrors "github.com/Finschia/finschia-sdk/types/errors"
    22  )
    23  
    24  func TestStoreType(t *testing.T) {
    25  	db := dbm.NewMemDB()
    26  	store := NewStore(db, log.NewNopLogger())
    27  	store.MountStoreWithDB(types.NewKVStoreKey("store1"), types.StoreTypeIAVL, db)
    28  }
    29  
    30  func TestGetCommitKVStore(t *testing.T) {
    31  	var db dbm.DB = dbm.NewMemDB()
    32  	ms := newMultiStoreWithMounts(db, types.PruneDefault)
    33  	err := ms.LoadLatestVersion()
    34  	require.Nil(t, err)
    35  
    36  	key := ms.keysByName["store1"]
    37  
    38  	store1 := ms.GetCommitKVStore(key)
    39  	require.NotNil(t, store1)
    40  	require.IsType(t, &iavl.Store{}, store1)
    41  
    42  	store2 := ms.GetCommitStore(key)
    43  	require.NotNil(t, store2)
    44  	require.IsType(t, &iavl.Store{}, store2)
    45  }
    46  
    47  func TestStoreMount(t *testing.T) {
    48  	db := dbm.NewMemDB()
    49  	store := NewStore(db, log.NewNopLogger())
    50  
    51  	key1 := types.NewKVStoreKey("store1")
    52  	key2 := types.NewKVStoreKey("store2")
    53  	dup1 := types.NewKVStoreKey("store1")
    54  
    55  	require.NotPanics(t, func() { store.MountStoreWithDB(key1, types.StoreTypeIAVL, db) })
    56  	require.NotPanics(t, func() { store.MountStoreWithDB(key2, types.StoreTypeIAVL, db) })
    57  
    58  	require.Panics(t, func() { store.MountStoreWithDB(key1, types.StoreTypeIAVL, db) })
    59  	require.Panics(t, func() { store.MountStoreWithDB(nil, types.StoreTypeIAVL, db) })
    60  	require.Panics(t, func() { store.MountStoreWithDB(dup1, types.StoreTypeIAVL, db) })
    61  }
    62  
    63  func TestCacheMultiStore(t *testing.T) {
    64  	var db dbm.DB = dbm.NewMemDB()
    65  	ms := newMultiStoreWithMounts(db, types.PruneNothing)
    66  
    67  	cacheMulti := ms.CacheMultiStore()
    68  	require.IsType(t, cachemulti.Store{}, cacheMulti)
    69  }
    70  
    71  func TestCacheMultiStoreWithVersion(t *testing.T) {
    72  	var db dbm.DB = dbm.NewMemDB()
    73  	ms := newMultiStoreWithMounts(db, types.PruneNothing)
    74  	err := ms.LoadLatestVersion()
    75  	require.Nil(t, err)
    76  
    77  	commitID := types.CommitID{}
    78  	checkStore(t, ms, commitID, commitID)
    79  
    80  	k, v := []byte("wind"), []byte("blows")
    81  
    82  	store1 := ms.GetStoreByName("store1").(types.KVStore)
    83  	store1.Set(k, v)
    84  
    85  	cID := ms.Commit()
    86  	require.Equal(t, int64(1), cID.Version)
    87  
    88  	// require no failure when given an invalid or pruned version
    89  	_, err = ms.CacheMultiStoreWithVersion(cID.Version + 1)
    90  	require.NoError(t, err)
    91  
    92  	// require a valid version can be cache-loaded
    93  	cms, err := ms.CacheMultiStoreWithVersion(cID.Version)
    94  	require.NoError(t, err)
    95  
    96  	// require a valid key lookup yields the correct value
    97  	kvStore := cms.GetKVStore(ms.keysByName["store1"])
    98  	require.NotNil(t, kvStore)
    99  	require.Equal(t, kvStore.Get(k), v)
   100  
   101  	// require we cannot commit (write) to a cache-versioned multi-store
   102  	require.Panics(t, func() {
   103  		kvStore.Set(k, []byte("newValue"))
   104  		cms.Write()
   105  	})
   106  }
   107  
   108  func TestHashStableWithEmptyCommit(t *testing.T) {
   109  	var db dbm.DB = dbm.NewMemDB()
   110  	ms := newMultiStoreWithMounts(db, types.PruneNothing)
   111  	err := ms.LoadLatestVersion()
   112  	require.Nil(t, err)
   113  
   114  	commitID := types.CommitID{}
   115  	checkStore(t, ms, commitID, commitID)
   116  
   117  	k, v := []byte("wind"), []byte("blows")
   118  
   119  	store1 := ms.GetStoreByName("store1").(types.KVStore)
   120  	store1.Set(k, v)
   121  
   122  	cID := ms.Commit()
   123  	require.Equal(t, int64(1), cID.Version)
   124  	hash := cID.Hash
   125  
   126  	// make an empty commit, it should update version, but not affect hash
   127  	cID = ms.Commit()
   128  	require.Equal(t, int64(2), cID.Version)
   129  	require.Equal(t, hash, cID.Hash)
   130  }
   131  
   132  func TestMultistoreCommitLoad(t *testing.T) {
   133  	var db dbm.DB = dbm.NewMemDB()
   134  	store := newMultiStoreWithMounts(db, types.PruneNothing)
   135  	err := store.LoadLatestVersion()
   136  	require.Nil(t, err)
   137  
   138  	// New store has empty last commit.
   139  	commitID := types.CommitID{}
   140  	checkStore(t, store, commitID, commitID)
   141  
   142  	// Make sure we can get stores by name.
   143  	s1 := store.GetStoreByName("store1")
   144  	require.NotNil(t, s1)
   145  	s3 := store.GetStoreByName("store3")
   146  	require.NotNil(t, s3)
   147  	s77 := store.GetStoreByName("store77")
   148  	require.Nil(t, s77)
   149  
   150  	// Make a few commits and check them.
   151  	nCommits := int64(3)
   152  	for i := int64(0); i < nCommits; i++ {
   153  		commitID = store.Commit()
   154  		expectedCommitID := getExpectedCommitID(store, i+1)
   155  		checkStore(t, store, expectedCommitID, commitID)
   156  	}
   157  
   158  	// Load the latest multistore again and check version.
   159  	store = newMultiStoreWithMounts(db, types.PruneNothing)
   160  	err = store.LoadLatestVersion()
   161  	require.Nil(t, err)
   162  	commitID = getExpectedCommitID(store, nCommits)
   163  	checkStore(t, store, commitID, commitID)
   164  
   165  	// Commit and check version.
   166  	commitID = store.Commit()
   167  	expectedCommitID := getExpectedCommitID(store, nCommits+1)
   168  	checkStore(t, store, expectedCommitID, commitID)
   169  
   170  	// Load an older multistore and check version.
   171  	ver := nCommits - 1
   172  	store = newMultiStoreWithMounts(db, types.PruneNothing)
   173  	err = store.LoadVersion(ver)
   174  	require.Nil(t, err)
   175  	commitID = getExpectedCommitID(store, ver)
   176  	checkStore(t, store, commitID, commitID)
   177  }
   178  
   179  func TestMultistoreLoadWithUpgrade(t *testing.T) {
   180  	var db dbm.DB = dbm.NewMemDB()
   181  	store := newMultiStoreWithMounts(db, types.PruneNothing)
   182  	err := store.LoadLatestVersion()
   183  	require.Nil(t, err)
   184  
   185  	// write some data in all stores
   186  	k1, v1 := []byte("first"), []byte("store")
   187  	s1, _ := store.GetStoreByName("store1").(types.KVStore)
   188  	require.NotNil(t, s1)
   189  	s1.Set(k1, v1)
   190  
   191  	k2, v2 := []byte("second"), []byte("restore")
   192  	s2, _ := store.GetStoreByName("store2").(types.KVStore)
   193  	require.NotNil(t, s2)
   194  	s2.Set(k2, v2)
   195  
   196  	k3, v3 := []byte("third"), []byte("dropped")
   197  	s3, _ := store.GetStoreByName("store3").(types.KVStore)
   198  	require.NotNil(t, s3)
   199  	s3.Set(k3, v3)
   200  
   201  	s4, _ := store.GetStoreByName("store4").(types.KVStore)
   202  	require.Nil(t, s4)
   203  
   204  	// do one commit
   205  	commitID := store.Commit()
   206  	expectedCommitID := getExpectedCommitID(store, 1)
   207  	checkStore(t, store, expectedCommitID, commitID)
   208  
   209  	ci, err := getCommitInfo(db, 1)
   210  	require.NoError(t, err)
   211  	require.Equal(t, int64(1), ci.Version)
   212  	require.Equal(t, 3, len(ci.StoreInfos))
   213  	checkContains(t, ci.StoreInfos, []string{"store1", "store2", "store3"})
   214  
   215  	// Load without changes and make sure it is sensible
   216  	store = newMultiStoreWithMounts(db, types.PruneNothing)
   217  
   218  	err = store.LoadLatestVersion()
   219  	require.Nil(t, err)
   220  	commitID = getExpectedCommitID(store, 1)
   221  	checkStore(t, store, commitID, commitID)
   222  
   223  	// let's query data to see it was saved properly
   224  	s2, _ = store.GetStoreByName("store2").(types.KVStore)
   225  	require.NotNil(t, s2)
   226  	require.Equal(t, v2, s2.Get(k2))
   227  
   228  	// now, let's load with upgrades...
   229  	restore, upgrades := newMultiStoreWithModifiedMounts(db, types.PruneNothing)
   230  	err = restore.LoadLatestVersionAndUpgrade(upgrades)
   231  	require.Nil(t, err)
   232  
   233  	// s1 was not changed
   234  	s1, _ = restore.GetStoreByName("store1").(types.KVStore)
   235  	require.NotNil(t, s1)
   236  	require.Equal(t, v1, s1.Get(k1))
   237  
   238  	// store3 is mounted, but data deleted are gone
   239  	s3, _ = restore.GetStoreByName("store3").(types.KVStore)
   240  	require.NotNil(t, s3)
   241  	require.Nil(t, s3.Get(k3)) // data was deleted
   242  
   243  	// store4 is mounted, with empty data
   244  	s4, _ = restore.GetStoreByName("store4").(types.KVStore)
   245  	require.NotNil(t, s4)
   246  
   247  	iterator := s4.Iterator(nil, nil)
   248  
   249  	values := 0
   250  	for ; iterator.Valid(); iterator.Next() {
   251  		values += 1
   252  	}
   253  	require.Zero(t, values)
   254  
   255  	require.NoError(t, iterator.Close())
   256  
   257  	// write something inside store4
   258  	k4, v4 := []byte("fourth"), []byte("created")
   259  	s4.Set(k4, v4)
   260  
   261  	// store2 is no longer mounted
   262  	st2 := restore.GetStoreByName("store2")
   263  	require.Nil(t, st2)
   264  
   265  	// restore2 has the old data
   266  	rs2, _ := restore.GetStoreByName("restore2").(types.KVStore)
   267  	require.NotNil(t, rs2)
   268  	require.Equal(t, v2, rs2.Get(k2))
   269  
   270  	// store this migrated data, and load it again without migrations
   271  	migratedID := restore.Commit()
   272  	require.Equal(t, migratedID.Version, int64(2))
   273  
   274  	reload, _ := newMultiStoreWithModifiedMounts(db, types.PruneNothing)
   275  	err = reload.LoadLatestVersion()
   276  	require.Nil(t, err)
   277  	require.Equal(t, migratedID, reload.LastCommitID())
   278  
   279  	// query this new store
   280  	rl1, _ := reload.GetStoreByName("store1").(types.KVStore)
   281  	require.NotNil(t, rl1)
   282  	require.Equal(t, v1, rl1.Get(k1))
   283  
   284  	rl2, _ := reload.GetStoreByName("restore2").(types.KVStore)
   285  	require.NotNil(t, rl2)
   286  	require.Equal(t, v2, rl2.Get(k2))
   287  
   288  	rl4, _ := reload.GetStoreByName("store4").(types.KVStore)
   289  	require.NotNil(t, rl4)
   290  	require.Equal(t, v4, rl4.Get(k4))
   291  
   292  	// check commitInfo in storage
   293  	ci, err = getCommitInfo(db, 2)
   294  	require.NoError(t, err)
   295  	require.Equal(t, int64(2), ci.Version)
   296  	require.Equal(t, 4, len(ci.StoreInfos), ci.StoreInfos)
   297  	checkContains(t, ci.StoreInfos, []string{"store1", "restore2", "store3", "store4"})
   298  }
   299  
   300  func TestParsePath(t *testing.T) {
   301  	_, _, err := parsePath("foo")
   302  	require.Error(t, err)
   303  
   304  	store, subpath, err := parsePath("/foo")
   305  	require.NoError(t, err)
   306  	require.Equal(t, store, "foo")
   307  	require.Equal(t, subpath, "")
   308  
   309  	store, subpath, err = parsePath("/fizz/bang/baz")
   310  	require.NoError(t, err)
   311  	require.Equal(t, store, "fizz")
   312  	require.Equal(t, subpath, "/bang/baz")
   313  
   314  	substore, subsubpath, err := parsePath(subpath)
   315  	require.NoError(t, err)
   316  	require.Equal(t, substore, "bang")
   317  	require.Equal(t, subsubpath, "/baz")
   318  }
   319  
   320  func TestMultiStoreRestart(t *testing.T) {
   321  	db := dbm.NewMemDB()
   322  	pruning := types.PruningOptions{
   323  		KeepRecent: 2,
   324  		KeepEvery:  3,
   325  		Interval:   1,
   326  	}
   327  	multi := newMultiStoreWithMounts(db, pruning)
   328  	err := multi.LoadLatestVersion()
   329  	require.Nil(t, err)
   330  
   331  	initCid := multi.LastCommitID()
   332  
   333  	k, v := "wind", "blows"
   334  	k2, v2 := "water", "flows"
   335  	k3, v3 := "fire", "burns"
   336  
   337  	for i := 1; i < 3; i++ {
   338  		// Set and commit data in one store.
   339  		store1 := multi.GetStoreByName("store1").(types.KVStore)
   340  		store1.Set([]byte(k), []byte(fmt.Sprintf("%s:%d", v, i)))
   341  
   342  		// ... and another.
   343  		store2 := multi.GetStoreByName("store2").(types.KVStore)
   344  		store2.Set([]byte(k2), []byte(fmt.Sprintf("%s:%d", v2, i)))
   345  
   346  		// ... and another.
   347  		store3 := multi.GetStoreByName("store3").(types.KVStore)
   348  		store3.Set([]byte(k3), []byte(fmt.Sprintf("%s:%d", v3, i)))
   349  
   350  		multi.Commit()
   351  
   352  		cinfo, err := getCommitInfo(multi.db, int64(i))
   353  		require.NoError(t, err)
   354  		require.Equal(t, int64(i), cinfo.Version)
   355  	}
   356  
   357  	// Set and commit data in one store.
   358  	store1 := multi.GetStoreByName("store1").(types.KVStore)
   359  	store1.Set([]byte(k), []byte(fmt.Sprintf("%s:%d", v, 3)))
   360  
   361  	// ... and another.
   362  	store2 := multi.GetStoreByName("store2").(types.KVStore)
   363  	store2.Set([]byte(k2), []byte(fmt.Sprintf("%s:%d", v2, 3)))
   364  
   365  	multi.Commit()
   366  
   367  	flushedCinfo, err := getCommitInfo(multi.db, 3)
   368  	require.Nil(t, err)
   369  	require.NotEqual(t, initCid, flushedCinfo, "CID is different after flush to disk")
   370  
   371  	// ... and another.
   372  	store3 := multi.GetStoreByName("store3").(types.KVStore)
   373  	store3.Set([]byte(k3), []byte(fmt.Sprintf("%s:%d", v3, 3)))
   374  
   375  	multi.Commit()
   376  
   377  	postFlushCinfo, err := getCommitInfo(multi.db, 4)
   378  	require.NoError(t, err)
   379  	require.Equal(t, int64(4), postFlushCinfo.Version, "Commit changed after in-memory commit")
   380  
   381  	multi = newMultiStoreWithMounts(db, pruning)
   382  	err = multi.LoadLatestVersion()
   383  	require.Nil(t, err)
   384  
   385  	reloadedCid := multi.LastCommitID()
   386  	require.Equal(t, int64(4), reloadedCid.Version, "Reloaded CID is not the same as last flushed CID")
   387  
   388  	// Check that store1 and store2 retained date from 3rd commit
   389  	store1 = multi.GetStoreByName("store1").(types.KVStore)
   390  	val := store1.Get([]byte(k))
   391  	require.Equal(t, []byte(fmt.Sprintf("%s:%d", v, 3)), val, "Reloaded value not the same as last flushed value")
   392  
   393  	store2 = multi.GetStoreByName("store2").(types.KVStore)
   394  	val2 := store2.Get([]byte(k2))
   395  	require.Equal(t, []byte(fmt.Sprintf("%s:%d", v2, 3)), val2, "Reloaded value not the same as last flushed value")
   396  
   397  	// Check that store3 still has data from last commit even though update happened on 2nd commit
   398  	store3 = multi.GetStoreByName("store3").(types.KVStore)
   399  	val3 := store3.Get([]byte(k3))
   400  	require.Equal(t, []byte(fmt.Sprintf("%s:%d", v3, 3)), val3, "Reloaded value not the same as last flushed value")
   401  }
   402  
   403  func TestMultiStoreQuery(t *testing.T) {
   404  	db := dbm.NewMemDB()
   405  	multi := newMultiStoreWithMounts(db, types.PruneNothing)
   406  	err := multi.LoadLatestVersion()
   407  	require.Nil(t, err)
   408  
   409  	k, v := []byte("wind"), []byte("blows")
   410  	k2, v2 := []byte("water"), []byte("flows")
   411  	// v3 := []byte("is cold")
   412  
   413  	cid1 := multi.Commit()
   414  
   415  	// Make sure we can get by name.
   416  	garbage := multi.GetStoreByName("bad-name")
   417  	require.Nil(t, garbage)
   418  
   419  	// Set and commit data in one store.
   420  	store1 := multi.GetStoreByName("store1").(types.KVStore)
   421  	store1.Set(k, v)
   422  
   423  	// ... and another.
   424  	store2 := multi.GetStoreByName("store2").(types.KVStore)
   425  	store2.Set(k2, v2)
   426  
   427  	// Commit the multistore.
   428  	cid2 := multi.Commit()
   429  	ver := cid2.Version
   430  
   431  	// Reload multistore from database
   432  	multi = newMultiStoreWithMounts(db, types.PruneNothing)
   433  	err = multi.LoadLatestVersion()
   434  	require.Nil(t, err)
   435  
   436  	// Test bad path.
   437  	query := abci.RequestQuery{Path: "/key", Data: k, Height: ver}
   438  	qres := multi.Query(query)
   439  	require.EqualValues(t, sdkerrors.ErrUnknownRequest.ABCICode(), qres.Code)
   440  	require.EqualValues(t, sdkerrors.ErrUnknownRequest.Codespace(), qres.Codespace)
   441  
   442  	query.Path = "h897fy32890rf63296r92"
   443  	qres = multi.Query(query)
   444  	require.EqualValues(t, sdkerrors.ErrUnknownRequest.ABCICode(), qres.Code)
   445  	require.EqualValues(t, sdkerrors.ErrUnknownRequest.Codespace(), qres.Codespace)
   446  
   447  	// Test invalid store name.
   448  	query.Path = "/garbage/key"
   449  	qres = multi.Query(query)
   450  	require.EqualValues(t, sdkerrors.ErrUnknownRequest.ABCICode(), qres.Code)
   451  	require.EqualValues(t, sdkerrors.ErrUnknownRequest.Codespace(), qres.Codespace)
   452  
   453  	// Test valid query with data.
   454  	query.Path = "/store1/key"
   455  	qres = multi.Query(query)
   456  	require.EqualValues(t, 0, qres.Code)
   457  	require.Equal(t, v, qres.Value)
   458  
   459  	// Test valid but empty query.
   460  	query.Path = "/store2/key"
   461  	query.Prove = true
   462  	qres = multi.Query(query)
   463  	require.EqualValues(t, 0, qres.Code)
   464  	require.Nil(t, qres.Value)
   465  
   466  	// Test store2 data.
   467  	query.Data = k2
   468  	qres = multi.Query(query)
   469  	require.EqualValues(t, 0, qres.Code)
   470  	require.Equal(t, v2, qres.Value)
   471  
   472  	// Test proofs latest height
   473  	query.Path = fmt.Sprintf("/%s", proofsPath)
   474  	qres = multi.Query(query)
   475  	require.EqualValues(t, 0, qres.Code)
   476  	require.NotNil(t, qres.ProofOps)
   477  	require.Equal(t, []byte(proofsPath), qres.Key)
   478  	require.Equal(t, cid2.Hash, qres.Value)
   479  	require.Equal(t, cid2.Version, qres.Height)
   480  	require.Equal(t, 3, len(qres.ProofOps.Ops)) // 3 mounted stores
   481  
   482  	// Test proofs second latest height
   483  	query.Height--
   484  	qres = multi.Query(query)
   485  	require.EqualValues(t, 0, qres.Code)
   486  	require.NotNil(t, qres.ProofOps)
   487  	require.Equal(t, []byte(proofsPath), qres.Key)
   488  	require.Equal(t, cid1.Hash, qres.Value)
   489  	require.Equal(t, cid1.Version, qres.Height)
   490  	require.Equal(t, 3, len(qres.ProofOps.Ops)) // 3 mounted stores
   491  }
   492  
   493  func TestMultiStore_Pruning(t *testing.T) {
   494  	testCases := []struct {
   495  		name        string
   496  		numVersions int64
   497  		po          types.PruningOptions
   498  		deleted     []int64
   499  		saved       []int64
   500  	}{
   501  		{"prune nothing", 10, types.PruneNothing, nil, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}},
   502  		{"prune everything", 10, types.PruneEverything, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9}, []int64{10}},
   503  		{"prune some; no batch", 10, types.NewPruningOptions(2, 3, 1), []int64{1, 2, 4, 5, 7}, []int64{3, 6, 8, 9, 10}},
   504  		{"prune some; small batch", 10, types.NewPruningOptions(2, 3, 3), []int64{1, 2, 4, 5}, []int64{3, 6, 7, 8, 9, 10}},
   505  		{"prune some; large batch", 10, types.NewPruningOptions(2, 3, 11), nil, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}},
   506  	}
   507  
   508  	for _, tc := range testCases {
   509  		t.Run(tc.name, func(t *testing.T) {
   510  			db := dbm.NewMemDB()
   511  			ms := newMultiStoreWithMounts(db, tc.po)
   512  			require.NoError(t, ms.LoadLatestVersion())
   513  
   514  			for i := int64(0); i < tc.numVersions; i++ {
   515  				ms.Commit()
   516  			}
   517  
   518  			for _, v := range tc.saved {
   519  				_, err := ms.CacheMultiStoreWithVersion(v)
   520  				require.NoError(t, err, "expected error when loading height: %d", v)
   521  			}
   522  
   523  			for _, v := range tc.deleted {
   524  				_, err := ms.CacheMultiStoreWithVersion(v)
   525  				// Line: Pruning is async. store/iavl/store.GetImmutable
   526  				// returns an empty tree when the version doesn't exist.
   527  				// However, when it gets caught in between, i.e. version
   528  				// checking is done before, but iavl.GetImmutable is done
   529  				// after pruning, it fails with 'version not exist' error.
   530  				// Simply retry would do.
   531  				if err != nil {
   532  					_, err = ms.CacheMultiStoreWithVersion(v)
   533  				}
   534  				require.NoError(t, err, "expected error when loading height: %d", v)
   535  			}
   536  		})
   537  	}
   538  }
   539  
   540  func TestMultiStore_PruningRestart(t *testing.T) {
   541  	db := dbm.NewMemDB()
   542  	ms := newMultiStoreWithMounts(db, types.NewPruningOptions(2, 3, 11))
   543  	require.NoError(t, ms.LoadLatestVersion())
   544  
   545  	// Commit enough to build up heights to prune, where on the next block we should
   546  	// batch delete.
   547  	for i := int64(0); i < 10; i++ {
   548  		ms.Commit()
   549  	}
   550  
   551  	pruneHeights := []int64{1, 2, 4, 5, 7}
   552  
   553  	// ensure we've persisted the current batch of heights to prune to the store's DB
   554  	ph, err := getPruningHeights(ms.db)
   555  	require.NoError(t, err)
   556  	require.Equal(t, pruneHeights, ph)
   557  
   558  	// "restart"
   559  	ms = newMultiStoreWithMounts(db, types.NewPruningOptions(2, 3, 11))
   560  	err = ms.LoadLatestVersion()
   561  	require.NoError(t, err)
   562  	require.Equal(t, pruneHeights, ms.pruneHeights)
   563  
   564  	// commit one more block and ensure the heights have been pruned
   565  	ms.Commit()
   566  	require.Empty(t, ms.pruneHeights)
   567  
   568  	for _, v := range pruneHeights {
   569  		_, err := ms.CacheMultiStoreWithVersion(v)
   570  		require.NoError(t, err, "expected error when loading height: %d", v)
   571  	}
   572  }
   573  
   574  func TestSetInitialVersion(t *testing.T) {
   575  	db := dbm.NewMemDB()
   576  	multi := newMultiStoreWithMounts(db, types.PruneNothing)
   577  
   578  	require.NoError(t, multi.LoadLatestVersion())
   579  
   580  	err := multi.SetInitialVersion(5)
   581  	require.NoError(t, err)
   582  	require.Equal(t, int64(5), multi.initialVersion)
   583  
   584  	multi.Commit()
   585  	require.Equal(t, int64(5), multi.LastCommitID().Version)
   586  
   587  	ckvs := multi.GetCommitKVStore(multi.keysByName["store1"])
   588  	iavlStore, ok := ckvs.(*iavl.Store)
   589  	require.True(t, ok)
   590  	require.True(t, iavlStore.VersionExists(5))
   591  }
   592  
   593  func TestAddListenersAndListeningEnabled(t *testing.T) {
   594  	db := dbm.NewMemDB()
   595  	multi := newMultiStoreWithMounts(db, types.PruneNothing)
   596  	testKey := types.NewKVStoreKey("listening_test_key")
   597  	enabled := multi.ListeningEnabled(testKey)
   598  	require.False(t, enabled)
   599  
   600  	multi.AddListeners(testKey, []types.WriteListener{})
   601  	enabled = multi.ListeningEnabled(testKey)
   602  	require.False(t, enabled)
   603  
   604  	mockListener := types.NewStoreKVPairWriteListener(nil, nil)
   605  	multi.AddListeners(testKey, []types.WriteListener{mockListener})
   606  	wrongTestKey := types.NewKVStoreKey("wrong_listening_test_key")
   607  	enabled = multi.ListeningEnabled(wrongTestKey)
   608  	require.False(t, enabled)
   609  
   610  	enabled = multi.ListeningEnabled(testKey)
   611  	require.True(t, enabled)
   612  }
   613  
   614  var (
   615  	interfaceRegistry = codecTypes.NewInterfaceRegistry()
   616  	testMarshaller    = codec.NewProtoCodec(interfaceRegistry)
   617  	testKey1          = []byte{1, 2, 3, 4, 5}
   618  	testValue1        = []byte{5, 4, 3, 2, 1}
   619  	testKey2          = []byte{2, 3, 4, 5, 6}
   620  	testValue2        = []byte{6, 5, 4, 3, 2}
   621  )
   622  
   623  func TestGetListenWrappedKVStore(t *testing.T) {
   624  	buf := new(bytes.Buffer)
   625  	var db dbm.DB = dbm.NewMemDB()
   626  	ms := newMultiStoreWithMounts(db, types.PruneNothing)
   627  	err := ms.LoadLatestVersion()
   628  	require.NoError(t, err)
   629  	mockListeners := []types.WriteListener{types.NewStoreKVPairWriteListener(buf, testMarshaller)}
   630  	ms.AddListeners(testStoreKey1, mockListeners)
   631  	ms.AddListeners(testStoreKey2, mockListeners)
   632  
   633  	listenWrappedStore1 := ms.GetKVStore(testStoreKey1)
   634  	require.IsType(t, &listenkv.Store{}, listenWrappedStore1)
   635  
   636  	listenWrappedStore1.Set(testKey1, testValue1)
   637  	expectedOutputKVPairSet1, err := testMarshaller.MarshalLengthPrefixed(&types.StoreKVPair{
   638  		Key:      testKey1,
   639  		Value:    testValue1,
   640  		StoreKey: testStoreKey1.Name(),
   641  		Delete:   false,
   642  	})
   643  	require.Nil(t, err)
   644  	kvPairSet1Bytes := buf.Bytes()
   645  	buf.Reset()
   646  	require.Equal(t, expectedOutputKVPairSet1, kvPairSet1Bytes)
   647  
   648  	listenWrappedStore1.Delete(testKey1)
   649  	expectedOutputKVPairDelete1, err := testMarshaller.MarshalLengthPrefixed(&types.StoreKVPair{
   650  		Key:      testKey1,
   651  		Value:    nil,
   652  		StoreKey: testStoreKey1.Name(),
   653  		Delete:   true,
   654  	})
   655  	require.Nil(t, err)
   656  	kvPairDelete1Bytes := buf.Bytes()
   657  	buf.Reset()
   658  	require.Equal(t, expectedOutputKVPairDelete1, kvPairDelete1Bytes)
   659  
   660  	listenWrappedStore2 := ms.GetKVStore(testStoreKey2)
   661  	require.IsType(t, &listenkv.Store{}, listenWrappedStore2)
   662  
   663  	listenWrappedStore2.Set(testKey2, testValue2)
   664  	expectedOutputKVPairSet2, err := testMarshaller.MarshalLengthPrefixed(&types.StoreKVPair{
   665  		Key:      testKey2,
   666  		Value:    testValue2,
   667  		StoreKey: testStoreKey2.Name(),
   668  		Delete:   false,
   669  	})
   670  	require.NoError(t, err)
   671  	kvPairSet2Bytes := buf.Bytes()
   672  	buf.Reset()
   673  	require.Equal(t, expectedOutputKVPairSet2, kvPairSet2Bytes)
   674  
   675  	listenWrappedStore2.Delete(testKey2)
   676  	expectedOutputKVPairDelete2, err := testMarshaller.MarshalLengthPrefixed(&types.StoreKVPair{
   677  		Key:      testKey2,
   678  		Value:    nil,
   679  		StoreKey: testStoreKey2.Name(),
   680  		Delete:   true,
   681  	})
   682  	require.NoError(t, err)
   683  	kvPairDelete2Bytes := buf.Bytes()
   684  	buf.Reset()
   685  	require.Equal(t, expectedOutputKVPairDelete2, kvPairDelete2Bytes)
   686  
   687  	unwrappedStore := ms.GetKVStore(testStoreKey3)
   688  	require.IsType(t, &iavl.Store{}, unwrappedStore)
   689  
   690  	unwrappedStore.Set(testKey2, testValue2)
   691  	kvPairSet3Bytes := buf.Bytes()
   692  	buf.Reset()
   693  	require.Equal(t, []byte{}, kvPairSet3Bytes)
   694  
   695  	unwrappedStore.Delete(testKey2)
   696  	kvPairDelete3Bytes := buf.Bytes()
   697  	buf.Reset()
   698  	require.Equal(t, []byte{}, kvPairDelete3Bytes)
   699  }
   700  
   701  func TestCacheWraps(t *testing.T) {
   702  	db := dbm.NewMemDB()
   703  	multi := newMultiStoreWithMounts(db, types.PruneNothing)
   704  
   705  	cacheWrapper := multi.CacheWrap()
   706  	require.IsType(t, cachemulti.Store{}, cacheWrapper)
   707  
   708  	cacheWrappedWithTrace := multi.CacheWrapWithTrace(nil, nil)
   709  	require.IsType(t, cachemulti.Store{}, cacheWrappedWithTrace)
   710  
   711  	cacheWrappedWithListeners := multi.CacheWrapWithListeners(nil, nil)
   712  	require.IsType(t, cachemulti.Store{}, cacheWrappedWithListeners)
   713  }
   714  
   715  func TestTraceConcurrency(t *testing.T) {
   716  	db := dbm.NewMemDB()
   717  	multi := newMultiStoreWithMounts(db, types.PruneNothing)
   718  	err := multi.LoadLatestVersion()
   719  	require.NoError(t, err)
   720  
   721  	b := &bytes.Buffer{}
   722  	key := multi.keysByName["store1"]
   723  	tc := types.TraceContext(map[string]interface{}{"blockHeight": 64})
   724  
   725  	multi.SetTracer(b)
   726  	multi.SetTracingContext(tc)
   727  
   728  	cms := multi.CacheMultiStore()
   729  	store1 := cms.GetKVStore(key)
   730  	cw := store1.CacheWrapWithTrace(b, tc)
   731  	_ = cw
   732  	require.NotNil(t, store1)
   733  
   734  	stop := make(chan struct{})
   735  	stopW := make(chan struct{})
   736  
   737  	go func(stop chan struct{}) {
   738  		for {
   739  			select {
   740  			case <-stop:
   741  				return
   742  			default:
   743  				store1.Set([]byte{1}, []byte{1})
   744  				cms.Write()
   745  			}
   746  		}
   747  	}(stop)
   748  
   749  	go func(stop chan struct{}) {
   750  		for {
   751  			select {
   752  			case <-stop:
   753  				return
   754  			default:
   755  				multi.SetTracingContext(tc)
   756  			}
   757  		}
   758  	}(stopW)
   759  
   760  	time.Sleep(3 * time.Second)
   761  	stop <- struct{}{}
   762  	stopW <- struct{}{}
   763  }
   764  
   765  //-----------------------------------------------------------------------
   766  // utils
   767  
   768  var (
   769  	testStoreKey1 = types.NewKVStoreKey("store1")
   770  	testStoreKey2 = types.NewKVStoreKey("store2")
   771  	testStoreKey3 = types.NewKVStoreKey("store3")
   772  )
   773  
   774  func newMultiStoreWithMounts(db dbm.DB, pruningOpts types.PruningOptions) *Store {
   775  	store := NewStore(db, log.NewNopLogger())
   776  	store.pruningOpts = pruningOpts
   777  
   778  	store.MountStoreWithDB(testStoreKey1, types.StoreTypeIAVL, nil)
   779  	store.MountStoreWithDB(testStoreKey2, types.StoreTypeIAVL, nil)
   780  	store.MountStoreWithDB(testStoreKey3, types.StoreTypeIAVL, nil)
   781  
   782  	return store
   783  }
   784  
   785  func newMultiStoreWithModifiedMounts(db dbm.DB, pruningOpts types.PruningOptions) (*Store, *types.StoreUpgrades) {
   786  	store := NewStore(db, log.NewNopLogger())
   787  	store.pruningOpts = pruningOpts
   788  
   789  	store.MountStoreWithDB(types.NewKVStoreKey("store1"), types.StoreTypeIAVL, nil)
   790  	store.MountStoreWithDB(types.NewKVStoreKey("restore2"), types.StoreTypeIAVL, nil)
   791  	store.MountStoreWithDB(types.NewKVStoreKey("store3"), types.StoreTypeIAVL, nil)
   792  	store.MountStoreWithDB(types.NewKVStoreKey("store4"), types.StoreTypeIAVL, nil)
   793  
   794  	upgrades := &types.StoreUpgrades{
   795  		Added: []string{"store4"},
   796  		Renamed: []types.StoreRename{{
   797  			OldKey: "store2",
   798  			NewKey: "restore2",
   799  		}},
   800  		Deleted: []string{"store3"},
   801  	}
   802  
   803  	return store, upgrades
   804  }
   805  
   806  func checkStore(t *testing.T, store *Store, expect, got types.CommitID) {
   807  	t.Helper()
   808  	require.Equal(t, expect, got)
   809  	require.Equal(t, expect, store.LastCommitID())
   810  }
   811  
   812  func checkContains(tb testing.TB, info []types.StoreInfo, wanted []string) {
   813  	tb.Helper()
   814  
   815  	for _, want := range wanted {
   816  		checkHas(tb, info, want)
   817  	}
   818  }
   819  
   820  func checkHas(tb testing.TB, info []types.StoreInfo, want string) {
   821  	tb.Helper()
   822  	for _, i := range info {
   823  		if i.Name == want {
   824  			return
   825  		}
   826  	}
   827  	tb.Fatalf("storeInfo doesn't contain %s", want)
   828  }
   829  
   830  func getExpectedCommitID(store *Store, ver int64) types.CommitID {
   831  	return types.CommitID{
   832  		Version: ver,
   833  		Hash:    hashStores(store.stores),
   834  	}
   835  }
   836  
   837  func hashStores(stores map[types.StoreKey]types.CommitKVStore) []byte {
   838  	m := make(map[string][]byte, len(stores))
   839  	for key, store := range stores {
   840  		name := key.Name()
   841  		m[name] = types.StoreInfo{
   842  			Name:     name,
   843  			CommitId: store.LastCommitID(),
   844  		}.GetHash()
   845  	}
   846  	return sdkmaps.HashFromMap(m)
   847  }
   848  
   849  func TestSetIAVLDIsableFastNode(t *testing.T) {
   850  	db := dbm.NewMemDB()
   851  	multi := newMultiStoreWithMounts(db, types.PruneNothing)
   852  
   853  	multi.SetIAVLDisableFastNode(true)
   854  	require.Equal(t, multi.iavlDisableFastNode, true)
   855  
   856  	multi.SetIAVLDisableFastNode(false)
   857  	require.Equal(t, multi.iavlDisableFastNode, false)
   858  }
   859  
   860  type commitKVStoreStub struct {
   861  	types.CommitKVStore
   862  	Committed int
   863  }
   864  
   865  func (stub *commitKVStoreStub) Commit() types.CommitID {
   866  	commitID := stub.CommitKVStore.Commit()
   867  	stub.Committed += 1
   868  	return commitID
   869  }
   870  
   871  func prepareStoreMap() map[types.StoreKey]types.CommitKVStore {
   872  	var db dbm.DB = dbm.NewMemDB()
   873  	store := NewStore(db, log.NewNopLogger())
   874  	store.MountStoreWithDB(types.NewKVStoreKey("iavl1"), types.StoreTypeIAVL, nil)
   875  	store.MountStoreWithDB(types.NewKVStoreKey("iavl2"), types.StoreTypeIAVL, nil)
   876  	store.MountStoreWithDB(types.NewTransientStoreKey("trans1"), types.StoreTypeTransient, nil)
   877  	err := store.LoadLatestVersion()
   878  	if err != nil {
   879  		panic(err)
   880  	}
   881  	return map[types.StoreKey]types.CommitKVStore{
   882  		testStoreKey1: &commitKVStoreStub{
   883  			CommitKVStore: store.GetStoreByName("iavl1").(types.CommitKVStore),
   884  		},
   885  		testStoreKey2: &commitKVStoreStub{
   886  			CommitKVStore: store.GetStoreByName("iavl2").(types.CommitKVStore),
   887  		},
   888  		testStoreKey3: &commitKVStoreStub{
   889  			CommitKVStore: store.GetStoreByName("trans1").(types.CommitKVStore),
   890  		},
   891  	}
   892  }
   893  
   894  func TestCommitStores(t *testing.T) {
   895  	testCases := []struct {
   896  		name          string
   897  		committed     int
   898  		exptectCommit int
   899  	}{
   900  		{
   901  			"when upgrade not get interrupted",
   902  			0,
   903  			1,
   904  		},
   905  		{
   906  			"when upgrade get interrupted once",
   907  			1,
   908  			0,
   909  		},
   910  		{
   911  			"when upgrade get interrupted twice",
   912  			2,
   913  			0,
   914  		},
   915  	}
   916  	for _, tc := range testCases {
   917  		t.Run(tc.name, func(t *testing.T) {
   918  			storeMap := prepareStoreMap()
   919  			store := storeMap[testStoreKey1].(*commitKVStoreStub)
   920  			for i := tc.committed; i > 0; i-- {
   921  				store.Commit()
   922  			}
   923  			store.Committed = 0
   924  			var version int64 = 1
   925  			res := commitStores(version, storeMap)
   926  			for _, s := range res.StoreInfos {
   927  				require.Equal(t, version, s.CommitId.Version)
   928  			}
   929  			require.Equal(t, version, res.Version)
   930  			require.Equal(t, tc.exptectCommit, store.Committed)
   931  		})
   932  	}
   933  }