From 7cf20ef70aef9c80b00e3e2c80d9e025054baae2 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 3 Aug 2017 16:58:57 +0200 Subject: [PATCH] Add test to enforce deteministic application of cache order --- state/merkle_test.go | 100 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 state/merkle_test.go diff --git a/state/merkle_test.go b/state/merkle_test.go new file mode 100644 index 000000000..d6c9f577b --- /dev/null +++ b/state/merkle_test.go @@ -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 + } + } + +}