cosmos-sdk/store/iavlstore_test.go

467 lines
14 KiB
Go

package store
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
"github.com/tendermint/iavl"
abci "github.com/tendermint/tendermint/abci/types"
cmn "github.com/tendermint/tendermint/libs/common"
dbm "github.com/tendermint/tendermint/libs/db"
sdk "github.com/cosmos/cosmos-sdk/types"
)
var (
cacheSize = 100
numRecent int64 = 5
storeEvery int64 = 3
)
var (
treeData = map[string]string{
"hello": "goodbye",
"aloha": "shalom",
}
nMoreData = 0
)
// make a tree and save it
func newTree(t *testing.T, db dbm.DB) (*iavl.VersionedTree, CommitID) {
tree := iavl.NewVersionedTree(db, cacheSize)
for k, v := range treeData {
tree.Set([]byte(k), []byte(v))
}
for i := 0; i < nMoreData; i++ {
key := cmn.RandBytes(12)
value := cmn.RandBytes(50)
tree.Set(key, value)
}
hash, ver, err := tree.SaveVersion()
require.Nil(t, err)
return tree, CommitID{ver, hash}
}
func TestIAVLStoreGetSetHasDelete(t *testing.T) {
db := dbm.NewMemDB()
tree, _ := newTree(t, db)
iavlStore := newIAVLStore(tree, numRecent, storeEvery)
key := "hello"
exists := iavlStore.Has([]byte(key))
require.True(t, exists)
value := iavlStore.Get([]byte(key))
require.EqualValues(t, value, treeData[key])
value2 := "notgoodbye"
iavlStore.Set([]byte(key), []byte(value2))
value = iavlStore.Get([]byte(key))
require.EqualValues(t, value, value2)
iavlStore.Delete([]byte(key))
exists = iavlStore.Has([]byte(key))
require.False(t, exists)
}
func TestIAVLIterator(t *testing.T) {
db := dbm.NewMemDB()
tree, _ := newTree(t, db)
iavlStore := newIAVLStore(tree, numRecent, storeEvery)
iter := iavlStore.Iterator([]byte("aloha"), []byte("hellz"))
expected := []string{"aloha", "hello"}
var i int
for i = 0; iter.Valid(); iter.Next() {
expectedKey := expected[i]
key, value := iter.Key(), iter.Value()
require.EqualValues(t, key, expectedKey)
require.EqualValues(t, value, treeData[expectedKey])
i++
}
require.Equal(t, len(expected), i)
iter = iavlStore.Iterator([]byte("golang"), []byte("rocks"))
expected = []string{"hello"}
for i = 0; iter.Valid(); iter.Next() {
expectedKey := expected[i]
key, value := iter.Key(), iter.Value()
require.EqualValues(t, key, expectedKey)
require.EqualValues(t, value, treeData[expectedKey])
i++
}
require.Equal(t, len(expected), i)
iter = iavlStore.Iterator(nil, []byte("golang"))
expected = []string{"aloha"}
for i = 0; iter.Valid(); iter.Next() {
expectedKey := expected[i]
key, value := iter.Key(), iter.Value()
require.EqualValues(t, key, expectedKey)
require.EqualValues(t, value, treeData[expectedKey])
i++
}
require.Equal(t, len(expected), i)
iter = iavlStore.Iterator(nil, []byte("shalom"))
expected = []string{"aloha", "hello"}
for i = 0; iter.Valid(); iter.Next() {
expectedKey := expected[i]
key, value := iter.Key(), iter.Value()
require.EqualValues(t, key, expectedKey)
require.EqualValues(t, value, treeData[expectedKey])
i++
}
require.Equal(t, len(expected), i)
iter = iavlStore.Iterator(nil, nil)
expected = []string{"aloha", "hello"}
for i = 0; iter.Valid(); iter.Next() {
expectedKey := expected[i]
key, value := iter.Key(), iter.Value()
require.EqualValues(t, key, expectedKey)
require.EqualValues(t, value, treeData[expectedKey])
i++
}
require.Equal(t, len(expected), i)
iter = iavlStore.Iterator([]byte("golang"), nil)
expected = []string{"hello"}
for i = 0; iter.Valid(); iter.Next() {
expectedKey := expected[i]
key, value := iter.Key(), iter.Value()
require.EqualValues(t, key, expectedKey)
require.EqualValues(t, value, treeData[expectedKey])
i++
}
require.Equal(t, len(expected), i)
}
func TestIAVLSubspaceIterator(t *testing.T) {
db := dbm.NewMemDB()
tree, _ := newTree(t, db)
iavlStore := newIAVLStore(tree, numRecent, storeEvery)
iavlStore.Set([]byte("test1"), []byte("test1"))
iavlStore.Set([]byte("test2"), []byte("test2"))
iavlStore.Set([]byte("test3"), []byte("test3"))
iavlStore.Set([]byte{byte(55), byte(255), byte(255), byte(0)}, []byte("test4"))
iavlStore.Set([]byte{byte(55), byte(255), byte(255), byte(1)}, []byte("test4"))
iavlStore.Set([]byte{byte(55), byte(255), byte(255), byte(255)}, []byte("test4"))
iavlStore.Set([]byte{byte(255), byte(255), byte(0)}, []byte("test4"))
iavlStore.Set([]byte{byte(255), byte(255), byte(1)}, []byte("test4"))
iavlStore.Set([]byte{byte(255), byte(255), byte(255)}, []byte("test4"))
var i int
iter := sdk.KVStorePrefixIterator(iavlStore, []byte("test"))
expected := []string{"test1", "test2", "test3"}
for i = 0; iter.Valid(); iter.Next() {
expectedKey := expected[i]
key, value := iter.Key(), iter.Value()
require.EqualValues(t, key, expectedKey)
require.EqualValues(t, value, expectedKey)
i++
}
iter.Close()
require.Equal(t, len(expected), i)
iter = sdk.KVStorePrefixIterator(iavlStore, []byte{byte(55), byte(255), byte(255)})
expected2 := [][]byte{
{byte(55), byte(255), byte(255), byte(0)},
{byte(55), byte(255), byte(255), byte(1)},
{byte(55), byte(255), byte(255), byte(255)},
}
for i = 0; iter.Valid(); iter.Next() {
expectedKey := expected2[i]
key, value := iter.Key(), iter.Value()
require.EqualValues(t, key, expectedKey)
require.EqualValues(t, value, []byte("test4"))
i++
}
iter.Close()
require.Equal(t, len(expected), i)
iter = sdk.KVStorePrefixIterator(iavlStore, []byte{byte(255), byte(255)})
expected2 = [][]byte{
{byte(255), byte(255), byte(0)},
{byte(255), byte(255), byte(1)},
{byte(255), byte(255), byte(255)},
}
for i = 0; iter.Valid(); iter.Next() {
expectedKey := expected2[i]
key, value := iter.Key(), iter.Value()
require.EqualValues(t, key, expectedKey)
require.EqualValues(t, value, []byte("test4"))
i++
}
iter.Close()
require.Equal(t, len(expected), i)
}
func TestIAVLReverseSubspaceIterator(t *testing.T) {
db := dbm.NewMemDB()
tree, _ := newTree(t, db)
iavlStore := newIAVLStore(tree, numRecent, storeEvery)
iavlStore.Set([]byte("test1"), []byte("test1"))
iavlStore.Set([]byte("test2"), []byte("test2"))
iavlStore.Set([]byte("test3"), []byte("test3"))
iavlStore.Set([]byte{byte(55), byte(255), byte(255), byte(0)}, []byte("test4"))
iavlStore.Set([]byte{byte(55), byte(255), byte(255), byte(1)}, []byte("test4"))
iavlStore.Set([]byte{byte(55), byte(255), byte(255), byte(255)}, []byte("test4"))
iavlStore.Set([]byte{byte(255), byte(255), byte(0)}, []byte("test4"))
iavlStore.Set([]byte{byte(255), byte(255), byte(1)}, []byte("test4"))
iavlStore.Set([]byte{byte(255), byte(255), byte(255)}, []byte("test4"))
var i int
iter := sdk.KVStoreReversePrefixIterator(iavlStore, []byte("test"))
expected := []string{"test3", "test2", "test1"}
for i = 0; iter.Valid(); iter.Next() {
expectedKey := expected[i]
key, value := iter.Key(), iter.Value()
require.EqualValues(t, key, expectedKey)
require.EqualValues(t, value, expectedKey)
i++
}
require.Equal(t, len(expected), i)
iter = sdk.KVStoreReversePrefixIterator(iavlStore, []byte{byte(55), byte(255), byte(255)})
expected2 := [][]byte{
{byte(55), byte(255), byte(255), byte(255)},
{byte(55), byte(255), byte(255), byte(1)},
{byte(55), byte(255), byte(255), byte(0)},
}
for i = 0; iter.Valid(); iter.Next() {
expectedKey := expected2[i]
key, value := iter.Key(), iter.Value()
require.EqualValues(t, key, expectedKey)
require.EqualValues(t, value, []byte("test4"))
i++
}
require.Equal(t, len(expected), i)
iter = sdk.KVStoreReversePrefixIterator(iavlStore, []byte{byte(255), byte(255)})
expected2 = [][]byte{
{byte(255), byte(255), byte(255)},
{byte(255), byte(255), byte(1)},
{byte(255), byte(255), byte(0)},
}
for i = 0; iter.Valid(); iter.Next() {
expectedKey := expected2[i]
key, value := iter.Key(), iter.Value()
require.EqualValues(t, key, expectedKey)
require.EqualValues(t, value, []byte("test4"))
i++
}
require.Equal(t, len(expected), i)
}
func nextVersion(iavl *iavlStore) {
key := []byte(fmt.Sprintf("Key for tree: %d", iavl.LastCommitID().Version))
value := []byte(fmt.Sprintf("Value for tree: %d", iavl.LastCommitID().Version))
iavl.Set(key, value)
iavl.Commit()
}
func TestIAVLDefaultPruning(t *testing.T) {
//Expected stored / deleted version numbers for:
//numRecent = 5, storeEvery = 3
var states = []pruneState{
{[]int64{}, []int64{}},
{[]int64{1}, []int64{}},
{[]int64{1, 2}, []int64{}},
{[]int64{1, 2, 3}, []int64{}},
{[]int64{1, 2, 3, 4}, []int64{}},
{[]int64{1, 2, 3, 4, 5}, []int64{}},
{[]int64{1, 2, 3, 4, 5, 6}, []int64{}},
{[]int64{2, 3, 4, 5, 6, 7}, []int64{1}},
{[]int64{3, 4, 5, 6, 7, 8}, []int64{1, 2}},
{[]int64{3, 4, 5, 6, 7, 8, 9}, []int64{1, 2}},
{[]int64{3, 5, 6, 7, 8, 9, 10}, []int64{1, 2, 4}},
{[]int64{3, 6, 7, 8, 9, 10, 11}, []int64{1, 2, 4, 5}},
{[]int64{3, 6, 7, 8, 9, 10, 11, 12}, []int64{1, 2, 4, 5}},
{[]int64{3, 6, 8, 9, 10, 11, 12, 13}, []int64{1, 2, 4, 5, 7}},
{[]int64{3, 6, 9, 10, 11, 12, 13, 14}, []int64{1, 2, 4, 5, 7, 8}},
{[]int64{3, 6, 9, 10, 11, 12, 13, 14, 15}, []int64{1, 2, 4, 5, 7, 8}},
}
testPruning(t, int64(5), int64(3), states)
}
func TestIAVLAlternativePruning(t *testing.T) {
//Expected stored / deleted version numbers for:
//numRecent = 3, storeEvery = 5
var states = []pruneState{
{[]int64{}, []int64{}},
{[]int64{1}, []int64{}},
{[]int64{1, 2}, []int64{}},
{[]int64{1, 2, 3}, []int64{}},
{[]int64{1, 2, 3, 4}, []int64{}},
{[]int64{2, 3, 4, 5}, []int64{1}},
{[]int64{3, 4, 5, 6}, []int64{1, 2}},
{[]int64{4, 5, 6, 7}, []int64{1, 2, 3}},
{[]int64{5, 6, 7, 8}, []int64{1, 2, 3, 4}},
{[]int64{5, 6, 7, 8, 9}, []int64{1, 2, 3, 4}},
{[]int64{5, 7, 8, 9, 10}, []int64{1, 2, 3, 4, 6}},
{[]int64{5, 8, 9, 10, 11}, []int64{1, 2, 3, 4, 6, 7}},
{[]int64{5, 9, 10, 11, 12}, []int64{1, 2, 3, 4, 6, 7, 8}},
{[]int64{5, 10, 11, 12, 13}, []int64{1, 2, 3, 4, 6, 7, 8, 9}},
{[]int64{5, 10, 11, 12, 13, 14}, []int64{1, 2, 3, 4, 6, 7, 8, 9}},
{[]int64{5, 10, 12, 13, 14, 15}, []int64{1, 2, 3, 4, 6, 7, 8, 9, 11}},
}
testPruning(t, int64(3), int64(5), states)
}
type pruneState struct {
stored []int64
deleted []int64
}
func testPruning(t *testing.T, numRecent int64, storeEvery int64, states []pruneState) {
db := dbm.NewMemDB()
tree := iavl.NewVersionedTree(db, cacheSize)
iavlStore := newIAVLStore(tree, numRecent, storeEvery)
for step, state := range states {
for _, ver := range state.stored {
require.True(t, iavlStore.VersionExists(ver),
"Missing version %d with latest version %d. Should save last %d and every %d",
ver, step, numRecent, storeEvery)
}
for _, ver := range state.deleted {
require.False(t, iavlStore.VersionExists(ver),
"Unpruned version %d with latest version %d. Should prune all but last %d and every %d",
ver, step, numRecent, storeEvery)
}
nextVersion(iavlStore)
}
}
func TestIAVLNoPrune(t *testing.T) {
db := dbm.NewMemDB()
tree := iavl.NewVersionedTree(db, cacheSize)
iavlStore := newIAVLStore(tree, numRecent, int64(1))
nextVersion(iavlStore)
for i := 1; i < 100; i++ {
for j := 1; j <= i; j++ {
require.True(t, iavlStore.VersionExists(int64(j)),
"Missing version %d with latest version %d. Should be storing all versions",
j, i)
}
nextVersion(iavlStore)
}
}
func TestIAVLPruneEverything(t *testing.T) {
db := dbm.NewMemDB()
tree := iavl.NewVersionedTree(db, cacheSize)
iavlStore := newIAVLStore(tree, int64(0), int64(0))
nextVersion(iavlStore)
for i := 1; i < 100; i++ {
for j := 1; j < i; j++ {
require.False(t, iavlStore.VersionExists(int64(j)),
"Unpruned version %d with latest version %d. Should prune all old versions",
j, i)
}
require.True(t, iavlStore.VersionExists(int64(i)),
"Missing current version on step %d, should not prune current state tree",
i)
nextVersion(iavlStore)
}
}
func TestIAVLStoreQuery(t *testing.T) {
db := dbm.NewMemDB()
tree := iavl.NewVersionedTree(db, cacheSize)
iavlStore := newIAVLStore(tree, numRecent, storeEvery)
k1, v1 := []byte("key1"), []byte("val1")
k2, v2 := []byte("key2"), []byte("val2")
v3 := []byte("val3")
ksub := []byte("key")
KVs0 := []KVPair{}
KVs1 := []KVPair{
{k1, v1},
{k2, v2},
}
KVs2 := []KVPair{
{k1, v3},
{k2, v2},
}
valExpSubEmpty := cdc.MustMarshalBinary(KVs0)
valExpSub1 := cdc.MustMarshalBinary(KVs1)
valExpSub2 := cdc.MustMarshalBinary(KVs2)
cid := iavlStore.Commit()
ver := cid.Version
query := abci.RequestQuery{Path: "/key", Data: k1, Height: ver}
querySub := abci.RequestQuery{Path: "/subspace", Data: ksub, Height: ver}
// query subspace before anything set
qres := iavlStore.Query(querySub)
require.Equal(t, uint32(sdk.CodeOK), qres.Code)
require.Equal(t, valExpSubEmpty, qres.Value)
// set data
iavlStore.Set(k1, v1)
iavlStore.Set(k2, v2)
// set data without commit, doesn't show up
qres = iavlStore.Query(query)
require.Equal(t, uint32(sdk.CodeOK), qres.Code)
require.Nil(t, qres.Value)
// commit it, but still don't see on old version
cid = iavlStore.Commit()
qres = iavlStore.Query(query)
require.Equal(t, uint32(sdk.CodeOK), qres.Code)
require.Nil(t, qres.Value)
// but yes on the new version
query.Height = cid.Version
qres = iavlStore.Query(query)
require.Equal(t, uint32(sdk.CodeOK), qres.Code)
require.Equal(t, v1, qres.Value)
// and for the subspace
qres = iavlStore.Query(querySub)
require.Equal(t, uint32(sdk.CodeOK), qres.Code)
require.Equal(t, valExpSub1, qres.Value)
// modify
iavlStore.Set(k1, v3)
cid = iavlStore.Commit()
// query will return old values, as height is fixed
qres = iavlStore.Query(query)
require.Equal(t, uint32(sdk.CodeOK), qres.Code)
require.Equal(t, v1, qres.Value)
// update to latest in the query and we are happy
query.Height = cid.Version
qres = iavlStore.Query(query)
require.Equal(t, uint32(sdk.CodeOK), qres.Code)
require.Equal(t, v3, qres.Value)
query2 := abci.RequestQuery{Path: "/key", Data: k2, Height: cid.Version}
qres = iavlStore.Query(query2)
require.Equal(t, uint32(sdk.CodeOK), qres.Code)
require.Equal(t, v2, qres.Value)
// and for the subspace
qres = iavlStore.Query(querySub)
require.Equal(t, uint32(sdk.CodeOK), qres.Code)
require.Equal(t, valExpSub2, qres.Value)
// default (height 0) will show latest -1
query0 := abci.RequestQuery{Path: "/store", Data: k1}
qres = iavlStore.Query(query0)
require.Equal(t, uint32(sdk.CodeOK), qres.Code)
require.Equal(t, v1, qres.Value)
}