diff --git a/sei-cosmos/storev2/rootmulti/store.go b/sei-cosmos/storev2/rootmulti/store.go index 2f9133babf..f296fdc834 100644 --- a/sei-cosmos/storev2/rootmulti/store.go +++ b/sei-cosmos/storev2/rootmulti/store.go @@ -242,7 +242,6 @@ func (rs *Store) CacheMultiStoreWithVersion(version int64) (types.CacheMultiStor stores[k] = store } } - // TODO: May need to add historical SC store as well for nodes that doesn't enable ss but still need historical queries // add SS stores for historical queries if rs.ssStore != nil { diff --git a/sei-cosmos/storev2/rootmulti/store_test.go b/sei-cosmos/storev2/rootmulti/store_test.go index 24514d0793..25f9e1333c 100644 --- a/sei-cosmos/storev2/rootmulti/store_test.go +++ b/sei-cosmos/storev2/rootmulti/store_test.go @@ -6,6 +6,7 @@ import ( "time" "github.com/cosmos/cosmos-sdk/store/types" + "github.com/cosmos/cosmos-sdk/storev2/state" "github.com/sei-protocol/sei-db/config" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" @@ -93,3 +94,102 @@ func TestSCSS_WriteAndHistoricalRead(t *testing.T) { require.EqualValues(t, 0, resp.Code) require.Equal(t, valV1, resp.Value) } + +// TestCacheMultiStoreWithVersion_OnlyUsesSSStores verifies that CacheMultiStoreWithVersion +// never adds SC stores and only adds SS stores for historical queries, ensuring that +// historical queries only serve from SS stores. +func TestCacheMultiStoreWithVersion_OnlyUsesSSStores(t *testing.T) { + // Enable both SC and SS with default configs + home := t.TempDir() + scCfg := config.DefaultStateCommitConfig() + scCfg.Enable = true + ssCfg := config.DefaultStateStoreConfig() + ssCfg.Enable = true + + store := NewStore(home, log.NewNopLogger(), scCfg, ssCfg, false) + defer func() { _ = store.Close() }() + + // Mount IAVL stores and transient/mem stores + iavlKey1 := types.NewKVStoreKey("iavl_store1") + iavlKey2 := types.NewKVStoreKey("iavl_store2") + transientKey := types.NewTransientStoreKey("transient_store") + memKey := types.NewMemoryStoreKey("mem_store") + + store.MountStoreWithDB(iavlKey1, types.StoreTypeIAVL, nil) + store.MountStoreWithDB(iavlKey2, types.StoreTypeIAVL, nil) + store.MountStoreWithDB(transientKey, types.StoreTypeTransient, nil) + store.MountStoreWithDB(memKey, types.StoreTypeMemory, nil) + require.NoError(t, store.LoadLatestVersion()) + + // Write data to IAVL stores and commit + iavl1KV := store.GetStoreByName("iavl_store1").(types.KVStore) + iavl2KV := store.GetStoreByName("iavl_store2").(types.KVStore) + iavl1KV.Set([]byte("k1"), []byte("v1")) + iavl2KV.Set([]byte("k2"), []byte("v2")) + c1 := store.Commit(true) + require.Equal(t, int64(1), c1.Version) + + // Write more data and commit again + iavl1KV = store.GetStoreByName("iavl_store1").(types.KVStore) + iavl2KV = store.GetStoreByName("iavl_store2").(types.KVStore) + iavl1KV.Set([]byte("k1"), []byte("v1_updated")) + iavl2KV.Set([]byte("k2"), []byte("v2_updated")) + c2 := store.Commit(true) + require.Equal(t, int64(2), c2.Version) + + // Wait for SS to asynchronously catch up to v2 + waitUntilSSVersion(t, store, c2.Version) + + // Test: Call CacheMultiStoreWithVersion for historical version v1 + cmsV1, err := store.CacheMultiStoreWithVersion(c1.Version) + require.NoError(t, err) + + // Verify IAVL stores are SS stores (StoreTypeSSStore), not SC stores (StoreTypeIAVL) + iavl1Store := cmsV1.GetKVStore(iavlKey1) + iavl2Store := cmsV1.GetKVStore(iavlKey2) + require.NotNil(t, iavl1Store) + require.NotNil(t, iavl2Store) + + // The stores are wrapped in cachekv, but GetStoreType() delegates to the underlying store + require.Equal(t, types.StoreType(state.StoreTypeSSStore), iavl1Store.GetStoreType(), + "IAVL store should be SS store (StoreTypeSSStore), not SC store (StoreTypeIAVL)") + require.Equal(t, types.StoreType(state.StoreTypeSSStore), iavl2Store.GetStoreType(), + "IAVL store should be SS store (StoreTypeSSStore), not SC store (StoreTypeIAVL)") + + // Verify transient and mem stores are still present with their original types + transientStore := cmsV1.GetKVStore(transientKey) + memStore := cmsV1.GetKVStore(memKey) + require.NotNil(t, transientStore) + require.NotNil(t, memStore) + require.Equal(t, types.StoreTypeTransient, transientStore.GetStoreType(), + "Transient store should maintain its type") + require.Equal(t, types.StoreTypeMemory, memStore.GetStoreType(), + "Memory store should maintain its type") + + // Verify the stores serve historical data from SS (v1 values, not v2) + require.Equal(t, []byte("v1"), iavl1Store.Get([]byte("k1")), + "Should serve v1 data from SS store") + require.Equal(t, []byte("v2"), iavl2Store.Get([]byte("k2")), + "Should serve v1 data from SS store") + + // Verify that SC stores (StoreTypeIAVL) are NOT present in the returned cache multi store + // by checking that all IAVL stores have StoreTypeSSStore, not StoreTypeIAVL + require.NotEqual(t, types.StoreTypeIAVL, iavl1Store.GetStoreType(), + "IAVL store should NOT be SC store (StoreTypeIAVL)") + require.NotEqual(t, types.StoreTypeIAVL, iavl2Store.GetStoreType(), + "IAVL store should NOT be SC store (StoreTypeIAVL)") + + // Test with latest version as well - should still use SS stores if available + cmsV2, err := store.CacheMultiStoreWithVersion(c2.Version) + require.NoError(t, err) + iavl1StoreV2 := cmsV2.GetKVStore(iavlKey1) + iavl2StoreV2 := cmsV2.GetKVStore(iavlKey2) + require.Equal(t, types.StoreType(state.StoreTypeSSStore), iavl1StoreV2.GetStoreType(), + "Latest version IAVL store should also be SS store") + require.Equal(t, types.StoreType(state.StoreTypeSSStore), iavl2StoreV2.GetStoreType(), + "Latest version IAVL store should also be SS store") + require.Equal(t, []byte("v1_updated"), iavl1StoreV2.Get([]byte("k1")), + "Should serve v2 data from SS store") + require.Equal(t, []byte("v2_updated"), iavl2StoreV2.Get([]byte("k2")), + "Should serve v2 data from SS store") +}