|
|
@@ -18,6 +18,8 @@ package trie
|
|
|
|
|
|
import (
|
|
|
"bytes"
|
|
|
+ "runtime"
|
|
|
+ "sync"
|
|
|
"testing"
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
|
@@ -31,6 +33,37 @@ func newEmptySecure() *SecureTrie {
|
|
|
return trie
|
|
|
}
|
|
|
|
|
|
+// makeTestSecureTrie creates a large enough secure trie for testing.
|
|
|
+func makeTestSecureTrie() (ethdb.Database, *SecureTrie, map[string][]byte) {
|
|
|
+ // Create an empty trie
|
|
|
+ db, _ := ethdb.NewMemDatabase()
|
|
|
+ trie, _ := NewSecure(common.Hash{}, db)
|
|
|
+
|
|
|
+ // Fill it with some arbitrary data
|
|
|
+ content := make(map[string][]byte)
|
|
|
+ for i := byte(0); i < 255; i++ {
|
|
|
+ // Map the same data under multiple keys
|
|
|
+ key, val := common.LeftPadBytes([]byte{1, i}, 32), []byte{i}
|
|
|
+ content[string(key)] = val
|
|
|
+ trie.Update(key, val)
|
|
|
+
|
|
|
+ key, val = common.LeftPadBytes([]byte{2, i}, 32), []byte{i}
|
|
|
+ content[string(key)] = val
|
|
|
+ trie.Update(key, val)
|
|
|
+
|
|
|
+ // Add some other data to inflate th trie
|
|
|
+ for j := byte(3); j < 13; j++ {
|
|
|
+ key, val = common.LeftPadBytes([]byte{j, i}, 32), []byte{j, i}
|
|
|
+ content[string(key)] = val
|
|
|
+ trie.Update(key, val)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ trie.Commit()
|
|
|
+
|
|
|
+ // Return the generated trie
|
|
|
+ return db, trie, content
|
|
|
+}
|
|
|
+
|
|
|
func TestSecureDelete(t *testing.T) {
|
|
|
trie := newEmptySecure()
|
|
|
vals := []struct{ k, v string }{
|
|
|
@@ -72,3 +105,41 @@ func TestSecureGetKey(t *testing.T) {
|
|
|
t.Errorf("GetKey returned %q, want %q", k, key)
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+func TestSecureTrieConcurrency(t *testing.T) {
|
|
|
+ // Create an initial trie and copy if for concurrent access
|
|
|
+ _, trie, _ := makeTestSecureTrie()
|
|
|
+
|
|
|
+ threads := runtime.NumCPU()
|
|
|
+ tries := make([]*SecureTrie, threads)
|
|
|
+ for i := 0; i < threads; i++ {
|
|
|
+ cpy := *trie
|
|
|
+ tries[i] = &cpy
|
|
|
+ }
|
|
|
+ // Start a batch of goroutines interactng with the trie
|
|
|
+ pend := new(sync.WaitGroup)
|
|
|
+ pend.Add(threads)
|
|
|
+ for i := 0; i < threads; i++ {
|
|
|
+ go func(index int) {
|
|
|
+ defer pend.Done()
|
|
|
+
|
|
|
+ for j := byte(0); j < 255; j++ {
|
|
|
+ // Map the same data under multiple keys
|
|
|
+ key, val := common.LeftPadBytes([]byte{byte(index), 1, j}, 32), []byte{j}
|
|
|
+ tries[index].Update(key, val)
|
|
|
+
|
|
|
+ key, val = common.LeftPadBytes([]byte{byte(index), 2, j}, 32), []byte{j}
|
|
|
+ tries[index].Update(key, val)
|
|
|
+
|
|
|
+ // Add some other data to inflate the trie
|
|
|
+ for k := byte(3); k < 13; k++ {
|
|
|
+ key, val = common.LeftPadBytes([]byte{byte(index), k, j}, 32), []byte{k, j}
|
|
|
+ tries[index].Update(key, val)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ tries[index].Commit()
|
|
|
+ }(i)
|
|
|
+ }
|
|
|
+ // Wait for all threads to finish
|
|
|
+ pend.Wait()
|
|
|
+}
|