Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix-up store #206

Merged
merged 3 commits into from
Aug 3, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func (s *Store) Info() abci.ResponseInfo {
"height", s.height,
"hash", fmt.Sprintf("%X", s.hash))
return abci.ResponseInfo{
Data: cmn.Fmt("size:%v", s.State.Committed().Size()),
Data: cmn.Fmt("size:%v", s.State.Size()),
LastBlockHeight: s.height - 1,
LastBlockAppHash: s.hash,
}
Expand Down Expand Up @@ -144,7 +144,7 @@ func (s *Store) Commit() abci.Result {
return abci.NewError(abci.CodeType_InternalError, "AppHash is incorrect")
}

if s.State.Committed().Size() == 0 {
if s.State.Size() == 0 {
return abci.NewResultOK(nil, "Empty hash for empty tree")
}
return abci.NewResultOK(s.hash, "")
Expand Down
13 changes: 11 additions & 2 deletions state/bonsai.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ type nonce int64

// Bonsai is a deformed tree forced to fit in a small pot
type Bonsai struct {
id nonce
merkle.Tree
id nonce
Tree merkle.Tree
}

var _ SimpleDB = &Bonsai{}
Expand All @@ -31,6 +31,11 @@ func (b *Bonsai) Get(key []byte) []byte {
return value
}

// Get matches the signature of KVStore
func (b *Bonsai) Has(key []byte) bool {
return b.Tree.Has(key)
}

// Set matches the signature of KVStore
func (b *Bonsai) Set(key, value []byte) {
b.Tree.Set(key, value)
Expand All @@ -41,6 +46,10 @@ func (b *Bonsai) Remove(key []byte) (value []byte) {
return
}

func (b *Bonsai) Proof(key []byte) (value []byte, proof []byte, exists bool) {
return b.Tree.Proof(key)
}

func (b *Bonsai) List(start, end []byte, limit int) []Model {
res := []Model{}
stopAtCount := func(key []byte, value []byte) (stop bool) {
Expand Down
10 changes: 8 additions & 2 deletions state/kvcache.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,12 @@ func (c *MemKVCache) Commit(sub SimpleDB) error {
if !ok {
return ErrNotASubTransaction()
}
// TODO: see if it points to us

// see if it points to us
ref, ok := cache.store.(*MemKVCache)
if !ok || ref != c {
return ErrNotASubTransaction()
}

// apply the cached data to us
cache.applyCache()
Expand All @@ -126,7 +131,8 @@ func (c *MemKVCache) Commit(sub SimpleDB) error {

// applyCache will apply all the cache methods to the underlying store
func (c *MemKVCache) applyCache() {
for k, v := range c.cache.m {
for _, k := range c.cache.keysInRange(nil, nil) {
v := c.cache.m[k]
if v == nil {
c.store.Remove([]byte(k))
} else {
Expand Down
7 changes: 6 additions & 1 deletion state/kvstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,12 @@ func (m *MemKVStore) Commit(sub SimpleDB) error {
if !ok {
return ErrNotASubTransaction()
}
// TODO: see if it points to us

// see if it points to us
ref, ok := cache.store.(*MemKVStore)
if !ok || ref != m {
return ErrNotASubTransaction()
}

// apply the cached data to us
cache.applyCache()
Expand Down
4 changes: 4 additions & 0 deletions state/merkle.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ func NewState(tree merkle.Tree, persistent bool) State {
}
}

func (s State) Size() int {
return s.committed.Tree.Size()
}

func (s State) Committed() *Bonsai {
return s.committed
}
Expand Down
100 changes: 100 additions & 0 deletions state/merkle_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package state

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/tendermint/merkleeyes/iavl"
)

type keyVal struct {
key string
val string
}

func (kv keyVal) getKV() ([]byte, []byte) {
return []byte(kv.key), []byte(kv.val)
}

type round []keyVal

// make sure the commits are deterministic
func TestStateCommitHash(t *testing.T) {
assert, require := assert.New(t), require.New(t)

cases := [...]struct {
rounds []round
}{
// simple, two rounds, no overlap
0: {
[]round{
[]keyVal{{"abc", "123"}, {"def", "456"}},
[]keyVal{{"more", "news"}, {"good", "news"}},
},
},
// more complex, order should change if applyCache is not deterministic
1: {
[]round{
[]keyVal{{"abc", "123"}, {"def", "456"}, {"foo", "789"}, {"dings", "646"}},
[]keyVal{{"hf", "123"}, {"giug", "456"}, {"kgiuvgi", "789"}, {"kjguvgk", "646"}},
[]keyVal{{"one", "more"}, {"two", "things"}, {"uh", "oh"}, {"a", "2"}},
},
},
// make sure ordering works with overwriting as well
2: {
[]round{
[]keyVal{{"abc", "123"}, {"def", "456"}, {"foo", "789"}, {"dings", "646"}},
[]keyVal{{"hf", "123"}, {"giug", "456"}, {"kgiuvgi", "789"}, {"kjguvgk", "646"}},
[]keyVal{{"abc", "qqq"}, {"def", "www"}, {"foo", "ee"}, {"dings", "ff"}},
[]keyVal{{"one", "more"}, {"uh", "oh"}, {"a", "2"}},
[]keyVal{{"hf", "dd"}, {"giug", "gg"}, {"kgiuvgi", "jj"}, {"kjguvgk", "uu"}},
},
},
}

for i, tc := range cases {
// let's run all rounds... they must each be different,
// and they must have the same results each run
var hashes [][]byte

// try each 5 times for deterministic check
for j := 0; j < 5; j++ {
result := make([][]byte, len(tc.rounds))

// make the store...
tree := iavl.NewIAVLTree(0, nil)
store := NewState(tree, false)

for n, r := range tc.rounds {
// start the cache
deliver := store.Append()
for _, kv := range r {
// add the value to cache
k, v := kv.getKV()
deliver.Set(k, v)
}
// commit and add hash to result
hash, err := store.Commit()
require.Nil(err, "tc:%d / rnd:%d - %+v", i, n, err)
result[n] = hash
}

// make sure result is all unique
for n := 0; n < len(result)-1; n++ {
assert.NotEqual(result[n], result[n+1], "tc:%d / rnd:%d", i, n)
}

// if hashes != nil, make sure same as last trial
if hashes != nil {
for n := 0; n < len(result); n++ {
assert.Equal(hashes[n], result[n], "tc:%d / rnd:%d", i, n)
}
}
// store to check against next trial
hashes = result
}
}

}