package store import ( "math/rand" "testing" "github.com/stretchr/testify/require" "github.com/tendermint/iavl" dbm "github.com/tendermint/tendermint/libs/db" sdk "github.com/cosmos/cosmos-sdk/types" ) type kvpair struct { key []byte value []byte } func genRandomKVPairs(t *testing.T) []kvpair { kvps := make([]kvpair, 20) for i := 0; i < 20; i++ { kvps[i].key = make([]byte, 32) rand.Read(kvps[i].key) kvps[i].value = make([]byte, 32) rand.Read(kvps[i].value) } return kvps } func setRandomKVPairs(t *testing.T, store KVStore) []kvpair { kvps := genRandomKVPairs(t) for _, kvp := range kvps { store.Set(kvp.key, kvp.value) } return kvps } func testPrefixStore(t *testing.T, baseStore KVStore, prefix []byte) { prefixStore := baseStore.Prefix(prefix) prefixPrefixStore := prefixStore.Prefix([]byte("prefix")) require.Panics(t, func() { prefixStore.Get(nil) }) require.Panics(t, func() { prefixStore.Set(nil, []byte{}) }) kvps := setRandomKVPairs(t, prefixPrefixStore) for i := 0; i < 20; i++ { key := kvps[i].key value := kvps[i].value require.True(t, prefixPrefixStore.Has(key)) require.Equal(t, value, prefixPrefixStore.Get(key)) key = append([]byte("prefix"), key...) require.True(t, prefixStore.Has(key)) require.Equal(t, value, prefixStore.Get(key)) key = append(prefix, key...) require.True(t, baseStore.Has(key)) require.Equal(t, value, baseStore.Get(key)) key = kvps[i].key prefixPrefixStore.Delete(key) require.False(t, prefixPrefixStore.Has(key)) require.Nil(t, prefixPrefixStore.Get(key)) key = append([]byte("prefix"), key...) require.False(t, prefixStore.Has(key)) require.Nil(t, prefixStore.Get(key)) key = append(prefix, key...) require.False(t, baseStore.Has(key)) require.Nil(t, baseStore.Get(key)) } } func TestIAVLStorePrefix(t *testing.T) { db := dbm.NewMemDB() tree := iavl.NewMutableTree(db, cacheSize) iavlStore := newIAVLStore(tree, numRecent, storeEvery) testPrefixStore(t, iavlStore, []byte("test")) } func TestCacheKVStorePrefix(t *testing.T) { cacheStore := newCacheKVStore() testPrefixStore(t, cacheStore, []byte("test")) } func TestGasKVStorePrefix(t *testing.T) { meter := sdk.NewGasMeter(100000000) mem := dbStoreAdapter{dbm.NewMemDB()} gasStore := NewGasKVStore(meter, sdk.KVGasConfig(), mem) testPrefixStore(t, gasStore, []byte("test")) } func TestPrefixStoreIterate(t *testing.T) { db := dbm.NewMemDB() baseStore := dbStoreAdapter{db} prefix := []byte("test") prefixStore := baseStore.Prefix(prefix) setRandomKVPairs(t, prefixStore) bIter := sdk.KVStorePrefixIterator(baseStore, prefix) pIter := sdk.KVStorePrefixIterator(prefixStore, nil) for bIter.Valid() && pIter.Valid() { require.Equal(t, bIter.Key(), append(prefix, pIter.Key()...)) require.Equal(t, bIter.Value(), pIter.Value()) bIter.Next() pIter.Next() } bIter.Close() pIter.Close() } func incFirstByte(bz []byte) { bz[0]++ } func TestCloneAppend(t *testing.T) { kvps := genRandomKVPairs(t) for _, kvp := range kvps { bz := cloneAppend(kvp.key, kvp.value) require.Equal(t, bz, append(kvp.key, kvp.value...)) incFirstByte(bz) require.NotEqual(t, bz, append(kvp.key, kvp.value...)) bz = cloneAppend(kvp.key, kvp.value) incFirstByte(kvp.key) require.NotEqual(t, bz, append(kvp.key, kvp.value...)) bz = cloneAppend(kvp.key, kvp.value) incFirstByte(kvp.value) require.NotEqual(t, bz, append(kvp.key, kvp.value...)) } } func TestPrefixStoreIteratorEdgeCase(t *testing.T) { db := dbm.NewMemDB() baseStore := dbStoreAdapter{db} // overflow in cpIncr prefix := []byte{0xAA, 0xFF, 0xFF} prefixStore := baseStore.Prefix(prefix) // ascending order baseStore.Set([]byte{0xAA, 0xFF, 0xFE}, []byte{}) baseStore.Set([]byte{0xAA, 0xFF, 0xFE, 0x00}, []byte{}) baseStore.Set([]byte{0xAA, 0xFF, 0xFF}, []byte{}) baseStore.Set([]byte{0xAA, 0xFF, 0xFF, 0x00}, []byte{}) baseStore.Set([]byte{0xAB}, []byte{}) baseStore.Set([]byte{0xAB, 0x00}, []byte{}) baseStore.Set([]byte{0xAB, 0x00, 0x00}, []byte{}) iter := prefixStore.Iterator(nil, nil) checkDomain(t, iter, nil, nil) checkItem(t, iter, []byte{}, bz("")) checkNext(t, iter, true) checkItem(t, iter, []byte{0x00}, bz("")) checkNext(t, iter, false) checkInvalid(t, iter) iter.Close() } func TestPrefixStoreReverseIteratorEdgeCase(t *testing.T) { db := dbm.NewMemDB() baseStore := dbStoreAdapter{db} // overflow in cpIncr prefix := []byte{0xAA, 0xFF, 0xFF} prefixStore := baseStore.Prefix(prefix) // descending order baseStore.Set([]byte{0xAB, 0x00, 0x00}, []byte{}) baseStore.Set([]byte{0xAB, 0x00}, []byte{}) baseStore.Set([]byte{0xAB}, []byte{}) baseStore.Set([]byte{0xAA, 0xFF, 0xFF, 0x00}, []byte{}) baseStore.Set([]byte{0xAA, 0xFF, 0xFF}, []byte{}) baseStore.Set([]byte{0xAA, 0xFF, 0xFE, 0x00}, []byte{}) baseStore.Set([]byte{0xAA, 0xFF, 0xFE}, []byte{}) iter := prefixStore.ReverseIterator(nil, nil) checkDomain(t, iter, nil, nil) checkItem(t, iter, []byte{0x00}, bz("")) checkNext(t, iter, true) checkItem(t, iter, []byte{}, bz("")) checkNext(t, iter, false) checkInvalid(t, iter) iter.Close() db = dbm.NewMemDB() baseStore = dbStoreAdapter{db} // underflow in cpDecr prefix = []byte{0xAA, 0x00, 0x00} prefixStore = baseStore.Prefix(prefix) baseStore.Set([]byte{0xAB, 0x00, 0x01, 0x00, 0x00}, []byte{}) baseStore.Set([]byte{0xAB, 0x00, 0x01, 0x00}, []byte{}) baseStore.Set([]byte{0xAB, 0x00, 0x01}, []byte{}) baseStore.Set([]byte{0xAA, 0x00, 0x00, 0x00}, []byte{}) baseStore.Set([]byte{0xAA, 0x00, 0x00}, []byte{}) baseStore.Set([]byte{0xA9, 0xFF, 0xFF, 0x00}, []byte{}) baseStore.Set([]byte{0xA9, 0xFF, 0xFF}, []byte{}) iter = prefixStore.ReverseIterator(nil, nil) checkDomain(t, iter, nil, nil) checkItem(t, iter, []byte{0x00}, bz("")) checkNext(t, iter, true) checkItem(t, iter, []byte{}, bz("")) checkNext(t, iter, false) checkInvalid(t, iter) iter.Close() } // Tests below are ported from https://github.com/tendermint/tendermint/blob/master/libs/db/prefix_db_test.go func mockStoreWithStuff() sdk.KVStore { db := dbm.NewMemDB() store := dbStoreAdapter{db} // Under "key" prefix store.Set(bz("key"), bz("value")) store.Set(bz("key1"), bz("value1")) store.Set(bz("key2"), bz("value2")) store.Set(bz("key3"), bz("value3")) store.Set(bz("something"), bz("else")) store.Set(bz(""), bz("")) store.Set(bz("k"), bz("val")) store.Set(bz("ke"), bz("valu")) store.Set(bz("kee"), bz("valuu")) return store } func checkValue(t *testing.T, store sdk.KVStore, key []byte, expected []byte) { bz := store.Get(key) require.Equal(t, expected, bz) } func checkValid(t *testing.T, itr sdk.Iterator, expected bool) { valid := itr.Valid() require.Equal(t, expected, valid) } func checkNext(t *testing.T, itr sdk.Iterator, expected bool) { itr.Next() valid := itr.Valid() require.Equal(t, expected, valid) } func checkDomain(t *testing.T, itr sdk.Iterator, start, end []byte) { ds, de := itr.Domain() require.Equal(t, start, ds) require.Equal(t, end, de) } func checkItem(t *testing.T, itr sdk.Iterator, key, value []byte) { require.Exactly(t, key, itr.Key()) require.Exactly(t, value, itr.Value()) } func checkInvalid(t *testing.T, itr sdk.Iterator) { checkValid(t, itr, false) checkKeyPanics(t, itr) checkValuePanics(t, itr) checkNextPanics(t, itr) } func checkKeyPanics(t *testing.T, itr sdk.Iterator) { require.Panics(t, func() { itr.Key() }) } func checkValuePanics(t *testing.T, itr sdk.Iterator) { require.Panics(t, func() { itr.Value() }) } func checkNextPanics(t *testing.T, itr sdk.Iterator) { require.Panics(t, func() { itr.Next() }) } func TestPrefixDBSimple(t *testing.T) { store := mockStoreWithStuff() pstore := store.Prefix(bz("key")) checkValue(t, pstore, bz("key"), nil) checkValue(t, pstore, bz(""), bz("value")) checkValue(t, pstore, bz("key1"), nil) checkValue(t, pstore, bz("1"), bz("value1")) checkValue(t, pstore, bz("key2"), nil) checkValue(t, pstore, bz("2"), bz("value2")) checkValue(t, pstore, bz("key3"), nil) checkValue(t, pstore, bz("3"), bz("value3")) checkValue(t, pstore, bz("something"), nil) checkValue(t, pstore, bz("k"), nil) checkValue(t, pstore, bz("ke"), nil) checkValue(t, pstore, bz("kee"), nil) } func TestPrefixDBIterator1(t *testing.T) { store := mockStoreWithStuff() pstore := store.Prefix(bz("key")) itr := pstore.Iterator(nil, nil) checkDomain(t, itr, nil, nil) checkItem(t, itr, bz(""), bz("value")) checkNext(t, itr, true) checkItem(t, itr, bz("1"), bz("value1")) checkNext(t, itr, true) checkItem(t, itr, bz("2"), bz("value2")) checkNext(t, itr, true) checkItem(t, itr, bz("3"), bz("value3")) checkNext(t, itr, false) checkInvalid(t, itr) itr.Close() } func TestPrefixDBIterator2(t *testing.T) { store := mockStoreWithStuff() pstore := store.Prefix(bz("key")) itr := pstore.Iterator(nil, bz("")) checkDomain(t, itr, nil, bz("")) checkInvalid(t, itr) itr.Close() } func TestPrefixDBIterator3(t *testing.T) { store := mockStoreWithStuff() pstore := store.Prefix(bz("key")) itr := pstore.Iterator(bz(""), nil) checkDomain(t, itr, bz(""), nil) checkItem(t, itr, bz(""), bz("value")) checkNext(t, itr, true) checkItem(t, itr, bz("1"), bz("value1")) checkNext(t, itr, true) checkItem(t, itr, bz("2"), bz("value2")) checkNext(t, itr, true) checkItem(t, itr, bz("3"), bz("value3")) checkNext(t, itr, false) checkInvalid(t, itr) itr.Close() } func TestPrefixDBIterator4(t *testing.T) { store := mockStoreWithStuff() pstore := store.Prefix(bz("key")) itr := pstore.Iterator(bz(""), bz("")) checkDomain(t, itr, bz(""), bz("")) checkInvalid(t, itr) itr.Close() } func TestPrefixDBReverseIterator1(t *testing.T) { store := mockStoreWithStuff() pstore := store.Prefix(bz("key")) itr := pstore.ReverseIterator(nil, nil) checkDomain(t, itr, nil, nil) checkItem(t, itr, bz("3"), bz("value3")) checkNext(t, itr, true) checkItem(t, itr, bz("2"), bz("value2")) checkNext(t, itr, true) checkItem(t, itr, bz("1"), bz("value1")) checkNext(t, itr, true) checkItem(t, itr, bz(""), bz("value")) checkNext(t, itr, false) checkInvalid(t, itr) itr.Close() } func TestPrefixDBReverseIterator2(t *testing.T) { store := mockStoreWithStuff() pstore := store.Prefix(bz("key")) itr := pstore.ReverseIterator(nil, bz("")) checkDomain(t, itr, nil, bz("")) checkItem(t, itr, bz("3"), bz("value3")) checkNext(t, itr, true) checkItem(t, itr, bz("2"), bz("value2")) checkNext(t, itr, true) checkItem(t, itr, bz("1"), bz("value1")) checkNext(t, itr, false) checkInvalid(t, itr) itr.Close() } func TestPrefixDBReverseIterator3(t *testing.T) { store := mockStoreWithStuff() pstore := store.Prefix(bz("key")) itr := pstore.ReverseIterator(bz(""), nil) checkDomain(t, itr, bz(""), nil) checkItem(t, itr, bz(""), bz("value")) checkNext(t, itr, false) checkInvalid(t, itr) itr.Close() } func TestPrefixDBReverseIterator4(t *testing.T) { store := mockStoreWithStuff() pstore := store.Prefix(bz("key")) itr := pstore.ReverseIterator(bz(""), bz("")) checkInvalid(t, itr) itr.Close() }