github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/store/rootmulti/store_test.go (about) 1 package rootmulti 2 3 import ( 4 "strings" 5 "testing" 6 7 "github.com/stretchr/testify/require" 8 9 abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" 10 "github.com/gnolang/gno/tm2/pkg/crypto/merkle" 11 dbm "github.com/gnolang/gno/tm2/pkg/db" 12 "github.com/gnolang/gno/tm2/pkg/db/memdb" 13 14 "github.com/gnolang/gno/tm2/pkg/store/iavl" 15 "github.com/gnolang/gno/tm2/pkg/store/types" 16 ) 17 18 func TestStoreType(t *testing.T) { 19 t.Parallel() 20 21 db := memdb.NewMemDB() 22 store := NewMultiStore(db) 23 store.MountStoreWithDB( 24 types.NewStoreKey("store1"), iavl.StoreConstructor, db) 25 } 26 27 func TestStoreMount(t *testing.T) { 28 t.Parallel() 29 30 db := memdb.NewMemDB() 31 store := NewMultiStore(db) 32 33 key1 := types.NewStoreKey("store1") 34 key2 := types.NewStoreKey("store2") 35 dup1 := types.NewStoreKey("store1") 36 37 require.NotPanics(t, func() { store.MountStoreWithDB(key1, iavl.StoreConstructor, db) }) 38 require.NotPanics(t, func() { store.MountStoreWithDB(key2, iavl.StoreConstructor, db) }) 39 40 require.Panics(t, func() { store.MountStoreWithDB(key1, iavl.StoreConstructor, db) }) 41 require.Panics(t, func() { store.MountStoreWithDB(dup1, iavl.StoreConstructor, db) }) 42 } 43 44 func TestCacheMultiStoreWithVersion(t *testing.T) { 45 t.Parallel() 46 47 var db dbm.DB = memdb.NewMemDB() 48 ms := newMultiStoreWithMounts(db) 49 err := ms.LoadLatestVersion() 50 require.Nil(t, err) 51 52 commitID := types.CommitID{} 53 checkStore(t, ms, commitID, commitID) 54 55 k, v := []byte("wind"), []byte("blows") 56 57 store1 := ms.getStoreByName("store1").(types.Store) 58 store1.Set(k, v) 59 60 cID := ms.Commit() 61 require.Equal(t, int64(1), cID.Version) 62 63 // require failure when given an invalid or pruned version 64 _, err = ms.MultiImmutableCacheWrapWithVersion(cID.Version + 1) 65 require.Error(t, err) 66 67 // require a valid version can be cache-loaded 68 cms, err := ms.MultiImmutableCacheWrapWithVersion(cID.Version) 69 require.NoError(t, err) 70 71 // require a valid key lookup yields the correct value 72 kvStore := cms.GetStore(ms.keysByName["store1"]) 73 require.NotNil(t, kvStore) 74 require.Equal(t, kvStore.Get(k), v) 75 76 // require we cannot commit (write) to a cache-versioned multi-store 77 require.Panics(t, func() { 78 kvStore.Set(k, []byte("newValue")) 79 cms.(types.Committer).Commit() 80 }) 81 } 82 83 func TestHashStableWithEmptyCommit(t *testing.T) { 84 t.Parallel() 85 86 var db dbm.DB = memdb.NewMemDB() 87 ms := newMultiStoreWithMounts(db) 88 err := ms.LoadLatestVersion() 89 require.Nil(t, err) 90 91 commitID := types.CommitID{} 92 checkStore(t, ms, commitID, commitID) 93 94 k, v := []byte("wind"), []byte("blows") 95 96 store1 := ms.getStoreByName("store1").(types.Store) 97 store1.Set(k, v) 98 99 cID := ms.Commit() 100 require.Equal(t, int64(1), cID.Version) 101 hash := cID.Hash 102 103 // make an empty commit, it should update version, but not affect hash 104 cID = ms.Commit() 105 require.Equal(t, int64(2), cID.Version) 106 require.Equal(t, hash, cID.Hash) 107 } 108 109 func TestMultistoreCommitLoad(t *testing.T) { 110 t.Parallel() 111 112 var db dbm.DB = memdb.NewMemDB() 113 store := newMultiStoreWithMounts(db) 114 err := store.LoadLatestVersion() 115 require.Nil(t, err) 116 117 // New store has empty last commit. 118 commitID := types.CommitID{} 119 checkStore(t, store, commitID, commitID) 120 121 // Make sure we can get stores by name. 122 s1 := store.getStoreByName("store1") 123 require.NotNil(t, s1) 124 s3 := store.getStoreByName("store3") 125 require.NotNil(t, s3) 126 s77 := store.getStoreByName("store77") 127 require.Nil(t, s77) 128 129 // Make a few commits and check them. 130 nCommits := int64(3) 131 for i := int64(0); i < nCommits; i++ { 132 commitID = store.Commit() 133 expectedCommitID := getExpectedCommitID(store, i+1) 134 checkStore(t, store, expectedCommitID, commitID) 135 } 136 137 // Load the latest multistore again and check version. 138 store = newMultiStoreWithMounts(db) 139 err = store.LoadLatestVersion() 140 require.Nil(t, err) 141 commitID = getExpectedCommitID(store, nCommits) 142 checkStore(t, store, commitID, commitID) 143 144 // Commit and check version. 145 commitID = store.Commit() 146 expectedCommitID := getExpectedCommitID(store, nCommits+1) 147 checkStore(t, store, expectedCommitID, commitID) 148 149 // Load an older multistore and check version. 150 ver := nCommits - 1 151 store = newMultiStoreWithMounts(db) 152 err = store.LoadVersion(ver) 153 require.Nil(t, err) 154 commitID = getExpectedCommitID(store, ver) 155 checkStore(t, store, commitID, commitID) 156 157 // XXX: commit this older version 158 commitID = store.Commit() 159 expectedCommitID = getExpectedCommitID(store, ver+1) 160 checkStore(t, store, expectedCommitID, commitID) 161 162 // XXX: confirm old commit is overwritten and we have rolled back 163 // LatestVersion 164 store = newMultiStoreWithMounts(db) 165 err = store.LoadLatestVersion() 166 require.Nil(t, err) 167 commitID = getExpectedCommitID(store, ver+1) 168 checkStore(t, store, commitID, commitID) 169 } 170 171 func TestParsePath(t *testing.T) { 172 t.Parallel() 173 174 _, _, err := parsePath("foo") 175 require.Error(t, err) 176 177 store, subpath, err := parsePath("/foo") 178 require.NoError(t, err) 179 require.Equal(t, store, "foo") 180 require.Equal(t, subpath, "") 181 182 store, subpath, err = parsePath("/fizz/bang/baz") 183 require.NoError(t, err) 184 require.Equal(t, store, "fizz") 185 require.Equal(t, subpath, "/bang/baz") 186 187 substore, subsubpath, err := parsePath(subpath) 188 require.NoError(t, err) 189 require.Equal(t, substore, "bang") 190 require.Equal(t, subsubpath, "/baz") 191 } 192 193 func TestMultiStoreQuery(t *testing.T) { 194 t.Parallel() 195 196 db := memdb.NewMemDB() 197 multi := newMultiStoreWithMounts(db) 198 err := multi.LoadLatestVersion() 199 require.Nil(t, err) 200 201 k, v := []byte("wind"), []byte("blows") 202 k2, v2 := []byte("water"), []byte("flows") 203 // v3 := []byte("is cold") 204 205 cid := multi.Commit() 206 207 // Make sure we can get by name. 208 garbage := multi.getStoreByName("bad-name") 209 require.Nil(t, garbage) 210 211 // Set and commit data in one store. 212 store1 := multi.getStoreByName("store1").(types.Store) 213 store1.Set(k, v) 214 215 // ... and another. 216 store2 := multi.getStoreByName("store2").(types.Store) 217 store2.Set(k2, v2) 218 219 // Commit the multistore. 220 cid = multi.Commit() 221 ver := cid.Version 222 223 // Reload multistore from database 224 multi = newMultiStoreWithMounts(db) 225 err = multi.LoadLatestVersion() 226 require.Nil(t, err) 227 228 // Test bad path. 229 query := abci.RequestQuery{Path: "/key", Data: k, Height: ver} 230 qres := multi.Query(query) 231 require.True(t, strings.HasPrefix(qres.Error.Error(), "unknownrequest error:")) 232 233 query.Path = "h897fy32890rf63296r92" 234 qres = multi.Query(query) 235 require.True(t, strings.HasPrefix(qres.Error.Error(), "unknownrequest error:")) 236 237 // Test invalid store name. 238 query.Path = "/garbage/key" 239 qres = multi.Query(query) 240 require.True(t, strings.HasPrefix(qres.Error.Error(), "unknownrequest error:")) 241 242 // Test valid query with data. 243 query.Path = "/store1/key" 244 qres = multi.Query(query) 245 require.Nil(t, qres.Error) 246 require.Equal(t, v, qres.Value) 247 248 // Test valid but empty query. 249 query.Path = "/store2/key" 250 query.Prove = true 251 qres = multi.Query(query) 252 require.Nil(t, qres.Error) 253 require.Nil(t, qres.Value) 254 255 // Test store2 data. 256 query.Data = k2 257 qres = multi.Query(query) 258 require.Nil(t, qres.Error) 259 require.Equal(t, v2, qres.Value) 260 } 261 262 // ----------------------------------------------------------------------- 263 // utils 264 265 func newMultiStoreWithMounts(db dbm.DB) *multiStore { 266 store := NewMultiStore(db) 267 store.storeOpts = types.StoreOptions{PruningOptions: types.PruneSyncable} 268 store.MountStoreWithDB( 269 types.NewStoreKey("store1"), iavl.StoreConstructor, nil) 270 store.MountStoreWithDB( 271 types.NewStoreKey("store2"), iavl.StoreConstructor, nil) 272 store.MountStoreWithDB( 273 types.NewStoreKey("store3"), iavl.StoreConstructor, nil) 274 return store 275 } 276 277 func checkStore(t *testing.T, store *multiStore, expect, got types.CommitID) { 278 t.Helper() 279 280 require.Equal(t, expect, got) 281 require.Equal(t, expect, store.LastCommitID()) 282 } 283 284 func getExpectedCommitID(store *multiStore, ver int64) types.CommitID { 285 return types.CommitID{ 286 Version: ver, 287 Hash: hashStores(store.stores), 288 } 289 } 290 291 func hashStores(stores map[types.StoreKey]types.CommitStore) []byte { 292 m := make(map[string][]byte, len(stores)) 293 for key, store := range stores { 294 name := key.Name() 295 m[name] = storeInfo{ 296 Name: name, 297 Core: storeCore{ 298 CommitID: store.LastCommitID(), 299 // StoreType: store.GetStoreType(), 300 }, 301 }.Hash() 302 } 303 return merkle.SimpleHashFromMap(m) 304 }