github.com/intfoundation/intchain@v0.0.0-20220727031208-4316ad31ca73/trie/trie_test.go (about)

     1  // Copyright 2014 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package trie
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/binary"
    22  	"fmt"
    23  	"io/ioutil"
    24  	"math/big"
    25  	"math/rand"
    26  	"os"
    27  	"reflect"
    28  	"testing"
    29  	"testing/quick"
    30  
    31  	"github.com/davecgh/go-spew/spew"
    32  	"github.com/intfoundation/intchain/common"
    33  	"github.com/intfoundation/intchain/crypto"
    34  	"github.com/intfoundation/intchain/intdb"
    35  	"github.com/intfoundation/intchain/intdb/leveldb"
    36  	"github.com/intfoundation/intchain/intdb/memorydb"
    37  	"github.com/intfoundation/intchain/rlp"
    38  )
    39  
    40  func init() {
    41  	spew.Config.Indent = "    "
    42  	spew.Config.DisableMethods = false
    43  }
    44  
    45  // Used for testing
    46  func newEmpty() *Trie {
    47  	trie, _ := New(common.Hash{}, NewDatabase(memorydb.New()))
    48  	return trie
    49  }
    50  
    51  func TestEmptyTrie(t *testing.T) {
    52  	var trie Trie
    53  	res := trie.Hash()
    54  	exp := emptyRoot
    55  	if res != common.Hash(exp) {
    56  		t.Errorf("expected %x got %x", exp, res)
    57  	}
    58  }
    59  
    60  func TestNull(t *testing.T) {
    61  	var trie Trie
    62  	key := make([]byte, 32)
    63  	value := []byte("test")
    64  	trie.Update(key, value)
    65  	if !bytes.Equal(trie.Get(key), value) {
    66  		t.Fatal("wrong value")
    67  	}
    68  }
    69  
    70  func TestMissingRoot(t *testing.T) {
    71  	trie, err := New(common.HexToHash("0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"), NewDatabase(memorydb.New()))
    72  	if trie != nil {
    73  		t.Error("New returned non-nil trie for invalid root")
    74  	}
    75  	if _, ok := err.(*MissingNodeError); !ok {
    76  		t.Errorf("New returned wrong error: %v", err)
    77  	}
    78  }
    79  
    80  func TestMissingNodeDisk(t *testing.T)    { testMissingNode(t, false) }
    81  func TestMissingNodeMemonly(t *testing.T) { testMissingNode(t, true) }
    82  
    83  func testMissingNode(t *testing.T, memonly bool) {
    84  	diskdb := memorydb.New()
    85  	triedb := NewDatabase(diskdb)
    86  
    87  	trie, _ := New(common.Hash{}, triedb)
    88  	updateString(trie, "120000", "qwerqwerqwerqwerqwerqwerqwerqwer")
    89  	updateString(trie, "123456", "asdfasdfasdfasdfasdfasdfasdfasdf")
    90  	root, _ := trie.Commit(nil)
    91  	if !memonly {
    92  		triedb.Commit(root, true)
    93  	}
    94  
    95  	trie, _ = New(root, triedb)
    96  	_, err := trie.TryGet([]byte("120000"))
    97  	if err != nil {
    98  		t.Errorf("Unexpected error: %v", err)
    99  	}
   100  	trie, _ = New(root, triedb)
   101  	_, err = trie.TryGet([]byte("120099"))
   102  	if err != nil {
   103  		t.Errorf("Unexpected error: %v", err)
   104  	}
   105  	trie, _ = New(root, triedb)
   106  	_, err = trie.TryGet([]byte("123456"))
   107  	if err != nil {
   108  		t.Errorf("Unexpected error: %v", err)
   109  	}
   110  	trie, _ = New(root, triedb)
   111  	err = trie.TryUpdate([]byte("120099"), []byte("zxcvzxcvzxcvzxcvzxcvzxcvzxcvzxcv"))
   112  	if err != nil {
   113  		t.Errorf("Unexpected error: %v", err)
   114  	}
   115  	trie, _ = New(root, triedb)
   116  	err = trie.TryDelete([]byte("123456"))
   117  	if err != nil {
   118  		t.Errorf("Unexpected error: %v", err)
   119  	}
   120  
   121  	hash := common.HexToHash("0xe1d943cc8f061a0c0b98162830b970395ac9315654824bf21b73b891365262f9")
   122  	if memonly {
   123  		delete(triedb.dirties, hash)
   124  	} else {
   125  		diskdb.Delete(hash[:])
   126  	}
   127  
   128  	trie, _ = New(root, triedb)
   129  	_, err = trie.TryGet([]byte("120000"))
   130  	if _, ok := err.(*MissingNodeError); !ok {
   131  		t.Errorf("Wrong error: %v", err)
   132  	}
   133  	trie, _ = New(root, triedb)
   134  	_, err = trie.TryGet([]byte("120099"))
   135  	if _, ok := err.(*MissingNodeError); !ok {
   136  		t.Errorf("Wrong error: %v", err)
   137  	}
   138  	trie, _ = New(root, triedb)
   139  	_, err = trie.TryGet([]byte("123456"))
   140  	if err != nil {
   141  		t.Errorf("Unexpected error: %v", err)
   142  	}
   143  	trie, _ = New(root, triedb)
   144  	err = trie.TryUpdate([]byte("120099"), []byte("zxcv"))
   145  	if _, ok := err.(*MissingNodeError); !ok {
   146  		t.Errorf("Wrong error: %v", err)
   147  	}
   148  	trie, _ = New(root, triedb)
   149  	err = trie.TryDelete([]byte("123456"))
   150  	if _, ok := err.(*MissingNodeError); !ok {
   151  		t.Errorf("Wrong error: %v", err)
   152  	}
   153  }
   154  
   155  func TestInsert(t *testing.T) {
   156  	trie := newEmpty()
   157  
   158  	updateString(trie, "doe", "reindeer")
   159  	updateString(trie, "dog", "puppy")
   160  	updateString(trie, "dogglesworth", "cat")
   161  
   162  	exp := common.HexToHash("8aad789dff2f538bca5d8ea56e8abe10f4c7ba3a5dea95fea4cd6e7c3a1168d3")
   163  	root := trie.Hash()
   164  	if root != exp {
   165  		t.Errorf("exp %x got %x", exp, root)
   166  	}
   167  
   168  	trie = newEmpty()
   169  	updateString(trie, "A", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
   170  
   171  	exp = common.HexToHash("d23786fb4a010da3ce639d66d5e904a11dbc02746d1ce25029e53290cabf28ab")
   172  	root, err := trie.Commit(nil)
   173  	if err != nil {
   174  		t.Fatalf("commit error: %v", err)
   175  	}
   176  	if root != exp {
   177  		t.Errorf("exp %x got %x", exp, root)
   178  	}
   179  }
   180  
   181  func TestGet(t *testing.T) {
   182  	trie := newEmpty()
   183  	updateString(trie, "doe", "reindeer")
   184  	updateString(trie, "dog", "puppy")
   185  	updateString(trie, "dogglesworth", "cat")
   186  
   187  	for i := 0; i < 2; i++ {
   188  		res := getString(trie, "dog")
   189  		if !bytes.Equal(res, []byte("puppy")) {
   190  			t.Errorf("expected puppy got %x", res)
   191  		}
   192  
   193  		unknown := getString(trie, "unknown")
   194  		if unknown != nil {
   195  			t.Errorf("expected nil got %x", unknown)
   196  		}
   197  
   198  		if i == 1 {
   199  			return
   200  		}
   201  		trie.Commit(nil)
   202  	}
   203  }
   204  
   205  func TestDelete(t *testing.T) {
   206  	trie := newEmpty()
   207  	vals := []struct{ k, v string }{
   208  		{"do", "verb"},
   209  		{"ether", "wookiedoo"},
   210  		{"horse", "stallion"},
   211  		{"shaman", "horse"},
   212  		{"doge", "coin"},
   213  		{"ether", ""},
   214  		{"dog", "puppy"},
   215  		{"shaman", ""},
   216  	}
   217  	for _, val := range vals {
   218  		if val.v != "" {
   219  			updateString(trie, val.k, val.v)
   220  		} else {
   221  			deleteString(trie, val.k)
   222  		}
   223  	}
   224  
   225  	hash := trie.Hash()
   226  	exp := common.HexToHash("5991bb8c6514148a29db676a14ac506cd2cd5775ace63c30a4fe457715e9ac84")
   227  	if hash != exp {
   228  		t.Errorf("expected %x got %x", exp, hash)
   229  	}
   230  }
   231  
   232  func TestEmptyValues(t *testing.T) {
   233  	trie := newEmpty()
   234  
   235  	vals := []struct{ k, v string }{
   236  		{"do", "verb"},
   237  		{"ether", "wookiedoo"},
   238  		{"horse", "stallion"},
   239  		{"shaman", "horse"},
   240  		{"doge", "coin"},
   241  		{"ether", ""},
   242  		{"dog", "puppy"},
   243  		{"shaman", ""},
   244  	}
   245  	for _, val := range vals {
   246  		updateString(trie, val.k, val.v)
   247  	}
   248  
   249  	hash := trie.Hash()
   250  	exp := common.HexToHash("5991bb8c6514148a29db676a14ac506cd2cd5775ace63c30a4fe457715e9ac84")
   251  	if hash != exp {
   252  		t.Errorf("expected %x got %x", exp, hash)
   253  	}
   254  }
   255  
   256  func TestReplication(t *testing.T) {
   257  	trie := newEmpty()
   258  	vals := []struct{ k, v string }{
   259  		{"do", "verb"},
   260  		{"ether", "wookiedoo"},
   261  		{"horse", "stallion"},
   262  		{"shaman", "horse"},
   263  		{"doge", "coin"},
   264  		{"dog", "puppy"},
   265  		{"somethingveryoddindeedthis is", "myothernodedata"},
   266  	}
   267  	for _, val := range vals {
   268  		updateString(trie, val.k, val.v)
   269  	}
   270  	exp, err := trie.Commit(nil)
   271  	if err != nil {
   272  		t.Fatalf("commit error: %v", err)
   273  	}
   274  
   275  	// create a new trie on top of the database and check that lookups work.
   276  	trie2, err := New(exp, trie.db)
   277  	if err != nil {
   278  		t.Fatalf("can't recreate trie at %x: %v", exp, err)
   279  	}
   280  	for _, kv := range vals {
   281  		if string(getString(trie2, kv.k)) != kv.v {
   282  			t.Errorf("trie2 doesn't have %q => %q", kv.k, kv.v)
   283  		}
   284  	}
   285  	hash, err := trie2.Commit(nil)
   286  	if err != nil {
   287  		t.Fatalf("commit error: %v", err)
   288  	}
   289  	if hash != exp {
   290  		t.Errorf("root failure. expected %x got %x", exp, hash)
   291  	}
   292  
   293  	// perform some insertions on the new trie.
   294  	vals2 := []struct{ k, v string }{
   295  		{"do", "verb"},
   296  		{"ether", "wookiedoo"},
   297  		{"horse", "stallion"},
   298  		// {"shaman", "horse"},
   299  		// {"doge", "coin"},
   300  		// {"ether", ""},
   301  		// {"dog", "puppy"},
   302  		// {"somethingveryoddindeedthis is", "myothernodedata"},
   303  		// {"shaman", ""},
   304  	}
   305  	for _, val := range vals2 {
   306  		updateString(trie2, val.k, val.v)
   307  	}
   308  	if hash := trie2.Hash(); hash != exp {
   309  		t.Errorf("root failure. expected %x got %x", exp, hash)
   310  	}
   311  }
   312  
   313  func TestLargeValue(t *testing.T) {
   314  	trie := newEmpty()
   315  	trie.Update([]byte("key1"), []byte{99, 99, 99, 99})
   316  	trie.Update([]byte("key2"), bytes.Repeat([]byte{1}, 32))
   317  	trie.Hash()
   318  }
   319  
   320  type countingDB struct {
   321  	intdb.KeyValueStore
   322  	gets map[string]int
   323  }
   324  
   325  func (db *countingDB) Get(key []byte) ([]byte, error) {
   326  	db.gets[string(key)]++
   327  	return db.KeyValueStore.Get(key)
   328  }
   329  
   330  // randTest performs random trie operations.
   331  // Instances of this test are created by Generate.
   332  type randTest []randTestStep
   333  
   334  type randTestStep struct {
   335  	op    int
   336  	key   []byte // for opUpdate, opDelete, opGet
   337  	value []byte // for opUpdate
   338  	err   error  // for debugging
   339  }
   340  
   341  const (
   342  	opUpdate = iota
   343  	opDelete
   344  	opGet
   345  	opCommit
   346  	opHash
   347  	opReset
   348  	opItercheckhash
   349  	opMax // boundary value, not an actual op
   350  )
   351  
   352  func (randTest) Generate(r *rand.Rand, size int) reflect.Value {
   353  	var allKeys [][]byte
   354  	genKey := func() []byte {
   355  		if len(allKeys) < 2 || r.Intn(100) < 10 {
   356  			// new key
   357  			key := make([]byte, r.Intn(50))
   358  			r.Read(key)
   359  			allKeys = append(allKeys, key)
   360  			return key
   361  		}
   362  		// use existing key
   363  		return allKeys[r.Intn(len(allKeys))]
   364  	}
   365  
   366  	var steps randTest
   367  	for i := 0; i < size; i++ {
   368  		step := randTestStep{op: r.Intn(opMax)}
   369  		switch step.op {
   370  		case opUpdate:
   371  			step.key = genKey()
   372  			step.value = make([]byte, 8)
   373  			binary.BigEndian.PutUint64(step.value, uint64(i))
   374  		case opGet, opDelete:
   375  			step.key = genKey()
   376  		}
   377  		steps = append(steps, step)
   378  	}
   379  	return reflect.ValueOf(steps)
   380  }
   381  
   382  func runRandTest(rt randTest) bool {
   383  	triedb := NewDatabase(memorydb.New())
   384  
   385  	tr, _ := New(common.Hash{}, triedb)
   386  	values := make(map[string]string) // tracks content of the trie
   387  
   388  	for i, step := range rt {
   389  		switch step.op {
   390  		case opUpdate:
   391  			tr.Update(step.key, step.value)
   392  			values[string(step.key)] = string(step.value)
   393  		case opDelete:
   394  			tr.Delete(step.key)
   395  			delete(values, string(step.key))
   396  		case opGet:
   397  			v := tr.Get(step.key)
   398  			want := values[string(step.key)]
   399  			if string(v) != want {
   400  				rt[i].err = fmt.Errorf("mismatch for key 0x%x, got 0x%x want 0x%x", step.key, v, want)
   401  			}
   402  		case opCommit:
   403  			_, rt[i].err = tr.Commit(nil)
   404  		case opHash:
   405  			tr.Hash()
   406  		case opReset:
   407  			hash, err := tr.Commit(nil)
   408  			if err != nil {
   409  				rt[i].err = err
   410  				return false
   411  			}
   412  			newtr, err := New(hash, triedb)
   413  			if err != nil {
   414  				rt[i].err = err
   415  				return false
   416  			}
   417  			tr = newtr
   418  		case opItercheckhash:
   419  			checktr, _ := New(common.Hash{}, triedb)
   420  			it := NewIterator(tr.NodeIterator(nil))
   421  			for it.Next() {
   422  				checktr.Update(it.Key, it.Value)
   423  			}
   424  			if tr.Hash() != checktr.Hash() {
   425  				rt[i].err = fmt.Errorf("hash mismatch in opItercheckhash")
   426  			}
   427  		}
   428  		// Abort the test on error.
   429  		if rt[i].err != nil {
   430  			return false
   431  		}
   432  	}
   433  	return true
   434  }
   435  
   436  func TestRandom(t *testing.T) {
   437  	if err := quick.Check(runRandTest, nil); err != nil {
   438  		if cerr, ok := err.(*quick.CheckError); ok {
   439  			t.Fatalf("random test iteration %d failed: %s", cerr.Count, spew.Sdump(cerr.In))
   440  		}
   441  		t.Fatal(err)
   442  	}
   443  }
   444  
   445  func BenchmarkGet(b *testing.B)      { benchGet(b, false) }
   446  func BenchmarkGetDB(b *testing.B)    { benchGet(b, true) }
   447  func BenchmarkUpdateBE(b *testing.B) { benchUpdate(b, binary.BigEndian) }
   448  func BenchmarkUpdateLE(b *testing.B) { benchUpdate(b, binary.LittleEndian) }
   449  
   450  const benchElemCount = 20000
   451  
   452  func benchGet(b *testing.B, commit bool) {
   453  	trie := new(Trie)
   454  	if commit {
   455  		_, tmpdb := tempDB()
   456  		trie, _ = New(common.Hash{}, tmpdb)
   457  	}
   458  	k := make([]byte, 32)
   459  	for i := 0; i < benchElemCount; i++ {
   460  		binary.LittleEndian.PutUint64(k, uint64(i))
   461  		trie.Update(k, k)
   462  	}
   463  	binary.LittleEndian.PutUint64(k, benchElemCount/2)
   464  	if commit {
   465  		trie.Commit(nil)
   466  	}
   467  
   468  	b.ResetTimer()
   469  	for i := 0; i < b.N; i++ {
   470  		trie.Get(k)
   471  	}
   472  	b.StopTimer()
   473  
   474  	if commit {
   475  		ldb := trie.db.diskdb.(*leveldb.Database)
   476  		ldb.Close()
   477  		os.RemoveAll(ldb.Path())
   478  	}
   479  }
   480  
   481  func benchUpdate(b *testing.B, e binary.ByteOrder) *Trie {
   482  	trie := newEmpty()
   483  	k := make([]byte, 32)
   484  	for i := 0; i < b.N; i++ {
   485  		e.PutUint64(k, uint64(i))
   486  		trie.Update(k, k)
   487  	}
   488  	return trie
   489  }
   490  
   491  // Benchmarks the trie hashing. Since the trie caches the result of any operation,
   492  // we cannot use b.N as the number of hashing rouns, since all rounds apart from
   493  // the first one will be NOOP. As such, we'll use b.N as the number of account to
   494  // insert into the trie before measuring the hashing.
   495  func BenchmarkHash(b *testing.B) {
   496  	// Make the random benchmark deterministic
   497  	random := rand.New(rand.NewSource(0))
   498  
   499  	// Create a realistic account trie to hash
   500  	addresses := make([][20]byte, b.N)
   501  	for i := 0; i < len(addresses); i++ {
   502  		for j := 0; j < len(addresses[i]); j++ {
   503  			addresses[i][j] = byte(random.Intn(256))
   504  		}
   505  	}
   506  	accounts := make([][]byte, len(addresses))
   507  	for i := 0; i < len(accounts); i++ {
   508  		var (
   509  			nonce   = uint64(random.Int63())
   510  			balance = new(big.Int).Rand(random, new(big.Int).Exp(common.Big2, common.Big256, nil))
   511  			root    = emptyRoot
   512  			code    = crypto.Keccak256(nil)
   513  		)
   514  		accounts[i], _ = rlp.EncodeToBytes([]interface{}{nonce, balance, root, code})
   515  	}
   516  	// Insert the accounts into the trie and hash it
   517  	trie := newEmpty()
   518  	for i := 0; i < len(addresses); i++ {
   519  		trie.Update(crypto.Keccak256(addresses[i][:]), accounts[i])
   520  	}
   521  	b.ResetTimer()
   522  	b.ReportAllocs()
   523  	trie.Hash()
   524  }
   525  
   526  func tempDB() (string, *Database) {
   527  	dir, err := ioutil.TempDir("", "trie-bench")
   528  	if err != nil {
   529  		panic(fmt.Sprintf("can't create temporary directory: %v", err))
   530  	}
   531  	diskdb, err := leveldb.New(dir, 256, 0, "")
   532  	if err != nil {
   533  		panic(fmt.Sprintf("can't create temporary database: %v", err))
   534  	}
   535  	return dir, NewDatabase(diskdb)
   536  }
   537  
   538  func getString(trie *Trie, k string) []byte {
   539  	return trie.Get([]byte(k))
   540  }
   541  
   542  func updateString(trie *Trie, k, v string) {
   543  	trie.Update([]byte(k), []byte(v))
   544  }
   545  
   546  func deleteString(trie *Trie, k string) {
   547  	trie.Delete([]byte(k))
   548  }
   549  
   550  func TestDecodeNode(t *testing.T) {
   551  	t.Parallel()
   552  	var (
   553  		hash  = make([]byte, 20)
   554  		elems = make([]byte, 20)
   555  	)
   556  	for i := 0; i < 5000000; i++ {
   557  		rand.Read(hash)
   558  		rand.Read(elems)
   559  		decodeNode(hash, elems)
   560  	}
   561  }