|
|
/** * @file * @copyright defined in aergo/LICENSE.txt */
package trie
import ( "bytes" "runtime"
//"io/ioutil"
"os" "path" "time"
//"encoding/hex"
"fmt" "math/rand" "sort" "testing"
"github.com/p4u/asmt/db" )
func TestTrieEmpty(t *testing.T) { smt := NewTrie(nil, Hasher, nil) if len(smt.Root) != 0 { t.Fatal("empty trie root hash not correct") } }
func TestTrieUpdateAndGet(t *testing.T) { smt := NewTrie(nil, Hasher, nil) smt.atomicUpdate = false
// Add data to empty trie
keys := getFreshData(10, 32) values := getFreshData(10, 32) ch := make(chan mresult, 1) smt.update(smt.Root, keys, values, nil, 0, smt.TrieHeight, ch) res := <-ch root := res.update
// Check all keys have been stored
for i, key := range keys { value, _ := smt.get(root, key, nil, 0, smt.TrieHeight) if !bytes.Equal(values[i], value) { t.Fatal("value not updated") } }
// Append to the trie
newKeys := getFreshData(5, 32) newValues := getFreshData(5, 32) ch = make(chan mresult, 1) smt.update(root, newKeys, newValues, nil, 0, smt.TrieHeight, ch) res = <-ch newRoot := res.update if bytes.Equal(root, newRoot) { t.Fatal("trie not updated") } for i, newKey := range newKeys { newValue, _ := smt.get(newRoot, newKey, nil, 0, smt.TrieHeight) if !bytes.Equal(newValues[i], newValue) { t.Fatal("failed to get value") } } }
func TestTrieAtomicUpdate(t *testing.T) { smt := NewTrie(nil, Hasher, nil) smt.CacheHeightLimit = 0 keys := getFreshData(1, 32) values := getFreshData(1, 32) root, _ := smt.AtomicUpdate(keys, values) updatedNb := len(smt.db.updatedNodes) cacheNb := len(smt.db.liveCache) newvalues := getFreshData(1, 32) if _, err := smt.AtomicUpdate(keys, newvalues); err != nil { t.Fatal(err) } if len(smt.db.updatedNodes) != 2*updatedNb { t.Fatal("Atomic update doesnt store all tries") } if len(smt.db.liveCache) != cacheNb { t.Fatal("Cache size should remain the same") }
// check keys of previous atomic update are accessible in
// updated nodes with root.
smt.atomicUpdate = false for i, key := range keys { value, _ := smt.get(root, key, nil, 0, smt.TrieHeight) if !bytes.Equal(values[i], value) { t.Fatal("failed to get value") } } }
func TestTriePublicUpdateAndGet(t *testing.T) { smt := NewTrie(nil, Hasher, nil) smt.CacheHeightLimit = 0 // Add data to empty trie
keys := getFreshData(20, 32) values := getFreshData(20, 32) root, _ := smt.Update(keys, values) updatedNb := len(smt.db.updatedNodes) cacheNb := len(smt.db.liveCache)
// Check all keys have been stored
for i, key := range keys { value, _ := smt.Get(key) if !bytes.Equal(values[i], value) { t.Fatal("trie not updated") } } if !bytes.Equal(root, smt.Root) { t.Fatal("Root not stored") }
newValues := getFreshData(20, 32) if _, err := smt.Update(keys, newValues); err != nil { t.Fatal(err) }
if len(smt.db.updatedNodes) != updatedNb { t.Fatal("multiple updates don't actualise updated nodes") } if len(smt.db.liveCache) != cacheNb { t.Fatal("multiple updates don't actualise liveCache") } // Check all keys have been modified
for i, key := range keys { value, _ := smt.Get(key) if !bytes.Equal(newValues[i], value) { t.Fatal("trie not updated") } } }
func TestGetWithRoot(t *testing.T) { dbPath := t.TempDir() st := db.NewDB(db.LevelImpl, dbPath) smt := NewTrie(nil, Hasher, st) smt.CacheHeightLimit = 0
// Add data to empty trie
keys := getFreshData(20, 32) values := getFreshData(20, 32) root, _ := smt.Update(keys, values)
// Check all keys have been stored
for i, key := range keys { value, _ := smt.Get(key) if !bytes.Equal(values[i], value) { t.Fatal("trie not updated") } } if !bytes.Equal(root, smt.Root) { t.Fatal("Root not stored") } if err := smt.Commit(); err != nil { t.Fatal(err) } // Delete two values (0 and 1)
if _, err := smt.Update([][]byte{keys[0], keys[1]}, [][]byte{DefaultLeaf, DefaultLeaf}); err != nil { t.Fatal(err) }
// Change one value
oldValue3 := make([]byte, 32) copy(oldValue3, values[3]) values[3] = getFreshData(1, 32)[0] if _, err := smt.Update([][]byte{keys[3]}, [][]byte{values[3]}); err != nil { t.Fatal(err) }
// Check root has been actually updated
if bytes.Equal(smt.Root, root) { t.Fatal("root not updated") }
// Get the third value with the new root
v3, err := smt.GetWithRoot(keys[3], smt.Root) if err != nil { t.Fatal(err) } if !bytes.Equal(v3, values[3]) { t.Fatalf("GetWithRoot did not keep the value: %x != %x", v3, values[3]) }
// Get the third value with the old root
v3, err = smt.GetWithRoot(keys[3], root) if err != nil { t.Fatal(err) } if !bytes.Equal(v3, oldValue3) { t.Fatalf("GetWithRoot did not keep the value: %x != %x", v3, oldValue3) } st.Close() }
func TestTrieWalk(t *testing.T) { dbPath := t.TempDir() st := db.NewDB(db.LevelImpl, dbPath)
smt := NewTrie(nil, Hasher, st) smt.CacheHeightLimit = 0 // Add data to empty trie
keys := getFreshData(20, 32) values := getFreshData(20, 32) root, _ := smt.Update(keys, values)
// Check all keys have been stored
for i, key := range keys { value, _ := smt.Get(key) if !bytes.Equal(values[i], value) { t.Fatal("trie not updated") } } if !bytes.Equal(root, smt.Root) { t.Fatal("Root not stored") }
// Walk over the whole tree and compare the values
i := 0 if err := smt.Walk(nil, func(v *WalkResult) int32 { if !bytes.Equal(v.Value, values[i]) { t.Fatalf("walk value does not match %x != %x", v.Value, values[i]) } if !bytes.Equal(v.Key, keys[i]) { t.Fatalf("walk key does not match %x != %x", v.Key, keys[i]) } i++ return 0 }); err != nil { t.Fatal(err) }
// Delete two values (0 and 3)
if _, err := smt.Update([][]byte{keys[0], keys[3]}, [][]byte{DefaultLeaf, DefaultLeaf}); err != nil { t.Fatal(err) }
// Delete two elements and walk again
i = 1 if err := smt.Walk(nil, func(v *WalkResult) int32 { if i == 3 { i++ } if !bytes.Equal(v.Value, values[i]) { t.Fatalf("walk value does not match %x == %x\n", v.Value, values[i]) } if !bytes.Equal(v.Key, keys[i]) { t.Fatalf("walk key does not match %x == %x\n", v.Key, keys[i]) } i++ return 0 }); err != nil { t.Fatal(err) }
// Add one new value to preivous deleted key
values[3] = getFreshData(1, 32)[0] if _, err := smt.Update([][]byte{keys[3]}, [][]byte{values[3]}); err != nil { t.Fatal(err) }
// Walk and check again
i = 1 if err := smt.Walk(nil, func(v *WalkResult) int32 { if !bytes.Equal(v.Value, values[i]) { t.Fatalf("walk value does not match %x != %x\n", v.Value, values[i]) } if !bytes.Equal(v.Key, keys[i]) { t.Fatalf("walk key does not match %x != %x\n", v.Key, keys[i]) } i++ return 0 }); err != nil { t.Fatal(err) }
// Find a specific value and test stop
i = 0 if err := smt.Walk(nil, func(v *WalkResult) int32 { if bytes.Equal(v.Value, values[5]) { return 1 } i++ return 0 }); err != nil { t.Fatal(err) } if i != 4 { t.Fatalf("Needed more iterations on walk than expected: %d != 4", i) }
st.Close() }
func TestTrieDelete(t *testing.T) { smt := NewTrie(nil, Hasher, nil) // Add data to empty trie
keys := getFreshData(20, 32) values := getFreshData(20, 32) ch := make(chan mresult, 1) smt.update(smt.Root, keys, values, nil, 0, smt.TrieHeight, ch) result := <-ch root := result.update value, _ := smt.get(root, keys[0], nil, 0, smt.TrieHeight) if !bytes.Equal(values[0], value) { t.Fatal("trie not updated") }
// Delete from trie
// To delete a key, just set it's value to Default leaf hash.
ch = make(chan mresult, 1) smt.update(root, keys[0:1], [][]byte{DefaultLeaf}, nil, 0, smt.TrieHeight, ch) result = <-ch updatedNb := len(smt.db.updatedNodes) newRoot := result.update newValue, _ := smt.get(newRoot, keys[0], nil, 0, smt.TrieHeight) if len(newValue) != 0 { t.Fatal("Failed to delete from trie") } // Remove deleted key from keys and check root with a clean trie.
smt2 := NewTrie(nil, Hasher, nil) ch = make(chan mresult, 1) smt2.update(smt.Root, keys[1:], values[1:], nil, 0, smt.TrieHeight, ch) result = <-ch cleanRoot := result.update if !bytes.Equal(newRoot, cleanRoot) { t.Fatal("roots mismatch") }
if len(smt2.db.updatedNodes) != updatedNb { t.Fatal("deleting doesn't actualise updated nodes") }
//Empty the trie
var newValues [][]byte for i := 0; i < 20; i++ { newValues = append(newValues, DefaultLeaf) } ch = make(chan mresult, 1) smt.update(root, keys, newValues, nil, 0, smt.TrieHeight, ch) result = <-ch root = result.update //if !bytes.Equal(smt.DefaultHash(256), root) {
if len(root) != 0 { t.Fatal("empty trie root hash not correct") }
// Test deleting an already empty key
smt = NewTrie(nil, Hasher, nil) keys = getFreshData(2, 32) values = getFreshData(2, 32) root, _ = smt.Update(keys, values) key0 := make([]byte, 32) key1 := make([]byte, 32) if _, err := smt.Update([][]byte{key0, key1}, [][]byte{DefaultLeaf, DefaultLeaf}); err != nil { t.Fatal(err) } if !bytes.Equal(root, smt.Root) { t.Fatal("deleting a default key shouldnt' modify the tree") } }
// test updating and deleting at the same time
func TestTrieUpdateAndDelete(t *testing.T) { smt := NewTrie(nil, Hasher, nil) smt.CacheHeightLimit = 0 key0 := make([]byte, 32) values := getFreshData(1, 32) root, _ := smt.Update([][]byte{key0}, values) cacheNb := len(smt.db.liveCache) updatedNb := len(smt.db.updatedNodes) smt.atomicUpdate = false _, _, k, v, isShortcut, _ := smt.loadChildren(root, smt.TrieHeight, 0, nil) if !isShortcut || !bytes.Equal(k[:HashLength], key0) || !bytes.Equal(v[:HashLength], values[0]) { t.Fatal("leaf shortcut didn't move up to root") }
key1 := make([]byte, 32) // set the last bit
bitSet(key1, 255) keys := [][]byte{key0, key1} values = [][]byte{DefaultLeaf, getFreshData(1, 32)[0]} root, _ = smt.Update(keys, values)
if len(smt.db.liveCache) != cacheNb { t.Fatal("number of cache nodes not correct after delete") } if len(smt.db.updatedNodes) != updatedNb { t.Fatal("number of cache nodes not correct after delete") }
smt.atomicUpdate = false _, _, k, v, isShortcut, _ = smt.loadChildren(root, smt.TrieHeight, 0, nil) if !isShortcut || !bytes.Equal(k[:HashLength], key1) || !bytes.Equal(v[:HashLength], values[1]) { t.Fatal("leaf shortcut didn't move up to root") } }
func TestTrieMerkleProof(t *testing.T) { smt := NewTrie(nil, Hasher, nil) // Add data to empty trie
keys := getFreshData(10, 32) values := getFreshData(10, 32) if _, err := smt.Update(keys, values); err != nil { t.Fatal(err) }
for i, key := range keys { ap, _, k, v, _ := smt.MerkleProof(key) if !smt.VerifyInclusion(ap, key, values[i]) { t.Fatalf("failed to verify inclusion proof") } if !bytes.Equal(key, k) && !bytes.Equal(values[i], v) { t.Fatalf("merkle proof didnt return the correct key-value pair") } } emptyKey := Hasher([]byte("non-member")) ap, included, proofKey, proofValue, _ := smt.MerkleProof(emptyKey) if included { t.Fatalf("failed to verify non inclusion proof") } if !smt.VerifyNonInclusion(ap, emptyKey, proofValue, proofKey) { t.Fatalf("failed to verify non inclusion proof") } }
func TestTrieMerkleProofCompressed(t *testing.T) { smt := NewTrie(nil, Hasher, nil) // Add data to empty trie
keys := getFreshData(10, 32) values := getFreshData(10, 32) if _, err := smt.Update(keys, values); err != nil { t.Fatal(err) }
for i, key := range keys { bitmap, ap, length, _, k, v, _ := smt.MerkleProofCompressed(key) if !smt.VerifyInclusionC(bitmap, key, values[i], ap, length) { t.Fatalf("failed to verify inclusion proof") } if !bytes.Equal(key, k) && !bytes.Equal(values[i], v) { t.Fatalf("merkle proof didnt return the correct key-value pair") } } emptyKey := Hasher([]byte("non-member")) bitmap, ap, length, included, proofKey, proofValue, _ := smt.MerkleProofCompressed(emptyKey) if included { t.Fatalf("failed to verify non inclusion proof") } if !smt.VerifyNonInclusionC(ap, length, bitmap, emptyKey, proofValue, proofKey) { t.Fatalf("failed to verify non inclusion proof") } }
func TestTrieCommit(t *testing.T) { dbPath := path.Join(".aergo", "db") if _, err := os.Stat(dbPath); os.IsNotExist(err) { _ = os.MkdirAll(dbPath, 0711) } st := db.NewDB(db.LevelImpl, dbPath)
smt := NewTrie(nil, Hasher, st) keys := getFreshData(10, 32) values := getFreshData(10, 32) if _, err := smt.Update(keys, values); err != nil { t.Fatal(err) } if err := smt.Commit(); err != nil { t.Fatal(err) } // liveCache is deleted so the key is fetched in badger db
smt.db.liveCache = make(map[Hash][][]byte) for i, key := range keys { value, _ := smt.Get(key) if !bytes.Equal(value, values[i]) { t.Fatal("failed to get value in committed db") } } st.Close() os.RemoveAll(".aergo") }
func TestTrieStageUpdates(t *testing.T) { dbPath := path.Join(".aergo", "db") if _, err := os.Stat(dbPath); os.IsNotExist(err) { _ = os.MkdirAll(dbPath, 0711) } st := db.NewDB(db.LevelImpl, dbPath)
smt := NewTrie(nil, Hasher, st) keys := getFreshData(10, 32) values := getFreshData(10, 32) if _, err := smt.Update(keys, values); err != nil { t.Fatal(err) } txn := st.NewTx() smt.StageUpdates(txn.(DbTx)) txn.Commit() // liveCache is deleted so the key is fetched in badger db
smt.db.liveCache = make(map[Hash][][]byte) for i, key := range keys { value, _ := smt.Get(key) if !bytes.Equal(value, values[i]) { t.Fatal("failed to get value in committed db") } } st.Close() os.RemoveAll(".aergo") }
func TestTrieRevert(t *testing.T) { dbPath := path.Join(".aergo", "db") if _, err := os.Stat(dbPath); os.IsNotExist(err) { _ = os.MkdirAll(dbPath, 0711) } st := db.NewDB(db.LevelImpl, dbPath)
smt := NewTrie(nil, Hasher, st)
// Edge case : Test that revert doesnt delete shortcut nodes
// when moved to a different position in tree
key0 := make([]byte, 32) key1 := make([]byte, 32) // setting the bit at 251 creates 2 shortcut batches at height 252
bitSet(key1, 251) values := getFreshData(2, 32) root, _ := smt.Update([][]byte{key0}, [][]byte{values[0]}) if err := smt.Commit(); err != nil { t.Fatal(err) } root2, _ := smt.Update([][]byte{key1}, [][]byte{values[1]}) if err := smt.Commit(); err != nil { t.Fatal(err) } if err := smt.Revert(root); err != nil { t.Fatal(err) } if len(smt.db.Store.Get(root)) == 0 { t.Fatal("shortcut node shouldnt be deleted by revert") } if len(smt.db.Store.Get(root2)) != 0 { t.Fatal("reverted root should have been deleted") } key1 = make([]byte, 32) // setting the bit at 255 stores the keys as the tip
bitSet(key1, 255) if _, err := smt.Update([][]byte{key1}, [][]byte{values[1]}); err != nil { t.Fatal(err) } if err := smt.Commit(); err != nil { t.Fatal(err) } if err := smt.Revert(root); err != nil { t.Fatal(err) } if len(smt.db.Store.Get(root)) == 0 { t.Fatal("shortcut node shouldnt be deleted by revert") }
// Test all nodes are reverted in the usual case
// Add data to empty trie
keys := getFreshData(10, 32) values = getFreshData(10, 32) root, _ = smt.Update(keys, values) if err := smt.Commit(); err != nil { t.Fatal(err) }
// Update the values
newValues := getFreshData(10, 32) if _, err := smt.Update(keys, newValues); err != nil { t.Fatal(err) } updatedNodes1 := smt.db.updatedNodes if err := smt.Commit(); err != nil { t.Fatal(err) } newKeys := getFreshData(10, 32) newValues = getFreshData(10, 32) if _, err := smt.Update(newKeys, newValues); err != nil { t.Fatal(err) } updatedNodes2 := smt.db.updatedNodes if err := smt.Commit(); err != nil { t.Fatal(err) }
if err := smt.Revert(root); err != nil { t.Fatal(err) }
if !bytes.Equal(smt.Root, root) { t.Fatal("revert failed") } if len(smt.pastTries) != 2 { // contains empty trie + reverted trie
t.Fatal("past tries not updated after revert") } // Check all keys have been reverted
for i, key := range keys { value, _ := smt.Get(key) if !bytes.Equal(values[i], value) { t.Fatal("revert failed, values not updated") } } if len(smt.db.liveCache) != 0 { t.Fatal("live cache not reset after revert") } // Check all reverted nodes have been deleted
for node := range updatedNodes2 { if len(smt.db.Store.Get(node[:])) != 0 { t.Fatal("nodes not deleted from database", node) } } for node := range updatedNodes1 { if len(smt.db.Store.Get(node[:])) != 0 { t.Fatal("nodes not deleted from database", node) } } st.Close() os.RemoveAll(".aergo") }
func TestTrieRaisesError(t *testing.T) { dbPath := path.Join(".aergo", "db") if _, err := os.Stat(dbPath); os.IsNotExist(err) { _ = os.MkdirAll(dbPath, 0711) } st := db.NewDB(db.LevelImpl, dbPath)
smt := NewTrie(nil, Hasher, st) // Add data to empty trie
keys := getFreshData(10, 32) values := getFreshData(10, 32) if _, err := smt.Update(keys, values); err != nil { t.Fatal(err) } smt.db.liveCache = make(map[Hash][][]byte) smt.db.updatedNodes = make(map[Hash][][]byte)
// Check errors are raised is a keys is not in cache nore db
for _, key := range keys { _, err := smt.Get(key) if err == nil { t.Fatal("Error not created if database doesnt have a node") } } _, _, _, _, _, _, err := smt.MerkleProofCompressed(keys[0]) if err == nil { t.Fatal("Error not created if database doesnt have a node") } _, err = smt.Update(keys, values) if err == nil { t.Fatal("Error not created if database doesnt have a node") } st.Close() os.RemoveAll(".aergo")
smt = NewTrie(nil, Hasher, nil) err = smt.Commit() if err == nil { t.Fatal("Error not created if database not connected") } smt.db.liveCache = make(map[Hash][][]byte) smt.atomicUpdate = false _, _, _, _, _, err = smt.loadChildren(make([]byte, 32), smt.TrieHeight, 0, nil) if err == nil { t.Fatal("Error not created if database not connected") } err = smt.LoadCache(make([]byte, 32)) if err == nil { t.Fatal("Error not created if database not connected") } }
func TestTrieLoadCache(t *testing.T) { dbPath := path.Join(".aergo", "db") if _, err := os.Stat(dbPath); os.IsNotExist(err) { _ = os.MkdirAll(dbPath, 0711) } st := db.NewDB(db.LevelImpl, dbPath)
smt := NewTrie(nil, Hasher, st) // Test size of cache
smt.CacheHeightLimit = 0 key0 := make([]byte, 32) key1 := make([]byte, 32) bitSet(key1, 255) values := getFreshData(2, 32) if _, err := smt.Update([][]byte{key0, key1}, values); err != nil { t.Fatal(err) } if len(smt.db.liveCache) != 66 { // the nodes are at the tip, so 64 + 2 = 66
t.Fatal("cache size incorrect") }
// Add data to empty trie
keys := getFreshData(10, 32) values = getFreshData(10, 32) if _, err := smt.Update(keys, values); err != nil { t.Fatal(err) } if err := smt.Commit(); err != nil { t.Fatal(err) }
// Simulate node restart by deleting and loading cache
cacheSize := len(smt.db.liveCache) smt.db.liveCache = make(map[Hash][][]byte)
err := smt.LoadCache(smt.Root)
if err != nil { t.Fatal(err) } if cacheSize != len(smt.db.liveCache) { t.Fatal("Cache loading from db incorrect") } st.Close() os.RemoveAll(".aergo") }
func TestHeight0LeafShortcut(t *testing.T) { keySize := 32 smt := NewTrie(nil, Hasher, nil) // Add 2 sibling keys that will be stored at height 0
key0 := make([]byte, keySize) key1 := make([]byte, keySize) bitSet(key1, keySize*8-1) keys := [][]byte{key0, key1} values := getFreshData(2, 32) if _, err := smt.Update(keys, values); err != nil { t.Fatal(err) } updatedNb := len(smt.db.updatedNodes)
// Check all keys have been stored
for i, key := range keys { value, _ := smt.Get(key) if !bytes.Equal(values[i], value) { t.Fatal("trie not updated") } } bitmap, ap, length, _, k, v, err := smt.MerkleProofCompressed(key1) if err != nil { t.Fatal(err) } if !bytes.Equal(key1, k) && !bytes.Equal(values[1], v) { t.Fatalf("merkle proof didnt return the correct key-value pair") } if length != smt.TrieHeight { t.Fatal("proof should have length equal to trie height for a leaf shortcut") } if !smt.VerifyInclusionC(bitmap, key1, values[1], ap, length) { t.Fatal("failed to verify inclusion proof") }
// Delete one key and check that the remaining one moved up to the root of the tree
newRoot, _ := smt.AtomicUpdate(keys[0:1], [][]byte{DefaultLeaf})
// Nb of updated nodes remains same because the new shortcut root was already stored at height 0.
if len(smt.db.updatedNodes) != updatedNb { fmt.Println(len(smt.db.updatedNodes), updatedNb) t.Fatal("number of cache nodes not correct after delete") } smt.atomicUpdate = false _, _, k, v, isShortcut, err := smt.loadChildren(newRoot, smt.TrieHeight, 0, nil) if err != nil { t.Fatal(err) } if !isShortcut || !bytes.Equal(k[:HashLength], key1) || !bytes.Equal(v[:HashLength], values[1]) { t.Fatal("leaf shortcut didn't move up to root") }
_, _, length, _, k, v, _ = smt.MerkleProofCompressed(key1) if length != 0 { t.Fatal("proof should have length equal to trie height for a leaf shortcut") } if !bytes.Equal(key1, k) && !bytes.Equal(values[1], v) { t.Fatalf("merkle proof didnt return the correct key-value pair") } }
func TestStash(t *testing.T) { dbPath := path.Join(".aergo", "db") if _, err := os.Stat(dbPath); os.IsNotExist(err) { _ = os.MkdirAll(dbPath, 0711) } st := db.NewDB(db.LevelImpl, dbPath) smt := NewTrie(nil, Hasher, st) // Add data to empty trie
keys := getFreshData(20, 32) values := getFreshData(20, 32) root, _ := smt.Update(keys, values) cacheSize := len(smt.db.liveCache) if err := smt.Commit(); err != nil { t.Fatal(err) } if len(smt.pastTries) != 1 { t.Fatal("Past tries not updated after commit") } values = getFreshData(20, 32) if _, err := smt.Update(keys, values); err != nil { t.Fatal(err) } if err := smt.Stash(true); err != nil { t.Fatal(err) } if len(smt.pastTries) != 1 { t.Fatal("Past tries not updated after commit") } if !bytes.Equal(smt.Root, root) { t.Fatal("Trie not rolled back") } if len(smt.db.updatedNodes) != 0 { t.Fatal("Trie not rolled back") } if len(smt.db.liveCache) != cacheSize { t.Fatal("Trie not rolled back") } keys = getFreshData(20, 32) values = getFreshData(20, 32) if _, err := smt.AtomicUpdate(keys, values); err != nil { t.Fatal(err) } values = getFreshData(20, 32) if _, err := smt.AtomicUpdate(keys, values); err != nil { t.Fatal(err) } if len(smt.pastTries) != 3 { t.Fatal("Past tries not updated after commit") } if err := smt.Stash(true); err != nil { t.Fatal(err) } if !bytes.Equal(smt.Root, root) { t.Fatal("Trie not rolled back") } if len(smt.db.updatedNodes) != 0 { t.Fatal("Trie not rolled back") } if len(smt.db.liveCache) != cacheSize { t.Fatal("Trie not rolled back") } if len(smt.pastTries) != 1 { t.Fatal("Past tries not updated after commit") } st.Close() os.RemoveAll(".aergo") }
func benchmark10MAccounts10Ktps(smt *Trie, b *testing.B) { //b.ReportAllocs()
keys := getFreshData(100, 32) values := getFreshData(100, 32) if _, err := smt.Update(keys, values); err != nil { b.Fatal(err) } fmt.Println("\nLoading b.N x 1000 accounts") for i := 0; i < b.N; i++ { newkeys := getFreshData(1000, 32) newvalues := getFreshData(1000, 32) start := time.Now() if _, err := smt.Update(newkeys, newvalues); err != nil { b.Fatal(err) } end := time.Now() if err := smt.Commit(); err != nil { b.Fatal(err) } end2 := time.Now() for j, key := range newkeys { val, _ := smt.Get(key) if !bytes.Equal(val, newvalues[j]) { b.Fatal("new key not included") } } end3 := time.Now() elapsed := end.Sub(start) elapsed2 := end2.Sub(end) elapsed3 := end3.Sub(end2) var m runtime.MemStats runtime.ReadMemStats(&m) fmt.Println(i, " : update time : ", elapsed, "commit time : ", elapsed2, "\n1000 Get time : ", elapsed3, "\ndb read : ", smt.LoadDbCounter, " cache read : ", smt.LoadCacheCounter, "\ncache size : ", len(smt.db.liveCache), "\nRAM : ", m.Sys/1024/1024, " MiB") } }
//go test -run=xxx -bench=. -benchmem -test.benchtime=20s
func BenchmarkCacheHeightLimit233(b *testing.B) { dbPath := path.Join(".aergo", "db") if _, err := os.Stat(dbPath); os.IsNotExist(err) { _ = os.MkdirAll(dbPath, 0711) } st := db.NewDB(db.LevelImpl, dbPath) smt := NewTrie(nil, Hasher, st) smt.CacheHeightLimit = 233 benchmark10MAccounts10Ktps(smt, b) st.Close() os.RemoveAll(".aergo") } func BenchmarkCacheHeightLimit238(b *testing.B) { dbPath := path.Join(".aergo", "db") if _, err := os.Stat(dbPath); os.IsNotExist(err) { _ = os.MkdirAll(dbPath, 0711) } st := db.NewDB(db.LevelImpl, dbPath) smt := NewTrie(nil, Hasher, st) smt.CacheHeightLimit = 238 benchmark10MAccounts10Ktps(smt, b) st.Close() os.RemoveAll(".aergo") } func BenchmarkCacheHeightLimit245(b *testing.B) { dbPath := path.Join(".aergo", "db") if _, err := os.Stat(dbPath); os.IsNotExist(err) { _ = os.MkdirAll(dbPath, 0711) } st := db.NewDB(db.LevelImpl, dbPath) smt := NewTrie(nil, Hasher, st) smt.CacheHeightLimit = 245 benchmark10MAccounts10Ktps(smt, b) st.Close() os.RemoveAll(".aergo") }
func getFreshData(size, length int) [][]byte { var data [][]byte for i := 0; i < size; i++ { key := make([]byte, 32) _, err := rand.Read(key) if err != nil { panic(err) } data = append(data, Hasher(key)[:length]) } sort.Sort(DataArray(data)) return data }
|