github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/trie/tracer_test.go (about)

     1  // Copyright 2022 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  	"testing"
    22  
    23  	"github.com/ethereum/go-ethereum/common"
    24  	"github.com/ethereum/go-ethereum/core/rawdb"
    25  	"github.com/ethereum/go-ethereum/core/types"
    26  	"github.com/ethereum/go-ethereum/trie/trienode"
    27  )
    28  
    29  var (
    30  	tiny = []struct{ k, v string }{
    31  		{"k1", "v1"},
    32  		{"k2", "v2"},
    33  		{"k3", "v3"},
    34  	}
    35  	nonAligned = []struct{ k, v string }{
    36  		{"do", "verb"},
    37  		{"ether", "wookiedoo"},
    38  		{"horse", "stallion"},
    39  		{"shaman", "horse"},
    40  		{"doge", "coin"},
    41  		{"dog", "puppy"},
    42  		{"somethingveryoddindeedthis is", "myothernodedata"},
    43  	}
    44  	standard = []struct{ k, v string }{
    45  		{string(randBytes(32)), "verb"},
    46  		{string(randBytes(32)), "wookiedoo"},
    47  		{string(randBytes(32)), "stallion"},
    48  		{string(randBytes(32)), "horse"},
    49  		{string(randBytes(32)), "coin"},
    50  		{string(randBytes(32)), "puppy"},
    51  		{string(randBytes(32)), "myothernodedata"},
    52  	}
    53  )
    54  
    55  func TestTrieTracer(t *testing.T) {
    56  	testTrieTracer(t, tiny)
    57  	testTrieTracer(t, nonAligned)
    58  	testTrieTracer(t, standard)
    59  }
    60  
    61  // Tests if the trie diffs are tracked correctly. Tracer should capture
    62  // all non-leaf dirty nodes, no matter the node is embedded or not.
    63  func testTrieTracer(t *testing.T, vals []struct{ k, v string }) {
    64  	db := newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)
    65  	trie := NewEmpty(db)
    66  
    67  	// Determine all new nodes are tracked
    68  	for _, val := range vals {
    69  		trie.MustUpdate([]byte(val.k), []byte(val.v))
    70  	}
    71  	insertSet := copySet(trie.tracer.inserts) // copy before commit
    72  	deleteSet := copySet(trie.tracer.deletes) // copy before commit
    73  	root, nodes, _ := trie.Commit(false)
    74  	db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
    75  
    76  	seen := setKeys(iterNodes(db, root))
    77  	if !compareSet(insertSet, seen) {
    78  		t.Fatal("Unexpected insertion set")
    79  	}
    80  	if !compareSet(deleteSet, nil) {
    81  		t.Fatal("Unexpected deletion set")
    82  	}
    83  
    84  	// Determine all deletions are tracked
    85  	trie, _ = New(TrieID(root), db)
    86  	for _, val := range vals {
    87  		trie.MustDelete([]byte(val.k))
    88  	}
    89  	insertSet, deleteSet = copySet(trie.tracer.inserts), copySet(trie.tracer.deletes)
    90  	if !compareSet(insertSet, nil) {
    91  		t.Fatal("Unexpected insertion set")
    92  	}
    93  	if !compareSet(deleteSet, seen) {
    94  		t.Fatal("Unexpected deletion set")
    95  	}
    96  }
    97  
    98  // Test that after inserting a new batch of nodes and deleting them immediately,
    99  // the trie tracer should be cleared normally as no operation happened.
   100  func TestTrieTracerNoop(t *testing.T) {
   101  	testTrieTracerNoop(t, tiny)
   102  	testTrieTracerNoop(t, nonAligned)
   103  	testTrieTracerNoop(t, standard)
   104  }
   105  
   106  func testTrieTracerNoop(t *testing.T, vals []struct{ k, v string }) {
   107  	db := newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)
   108  	trie := NewEmpty(db)
   109  	for _, val := range vals {
   110  		trie.MustUpdate([]byte(val.k), []byte(val.v))
   111  	}
   112  	for _, val := range vals {
   113  		trie.MustDelete([]byte(val.k))
   114  	}
   115  	if len(trie.tracer.inserts) != 0 {
   116  		t.Fatal("Unexpected insertion set")
   117  	}
   118  	if len(trie.tracer.deletes) != 0 {
   119  		t.Fatal("Unexpected deletion set")
   120  	}
   121  }
   122  
   123  // Tests if the accessList is correctly tracked.
   124  func TestAccessList(t *testing.T) {
   125  	testAccessList(t, tiny)
   126  	testAccessList(t, nonAligned)
   127  	testAccessList(t, standard)
   128  }
   129  
   130  func testAccessList(t *testing.T, vals []struct{ k, v string }) {
   131  	var (
   132  		db   = newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)
   133  		trie = NewEmpty(db)
   134  		orig = trie.Copy()
   135  	)
   136  	// Create trie from scratch
   137  	for _, val := range vals {
   138  		trie.MustUpdate([]byte(val.k), []byte(val.v))
   139  	}
   140  	root, nodes, _ := trie.Commit(false)
   141  	db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
   142  
   143  	trie, _ = New(TrieID(root), db)
   144  	if err := verifyAccessList(orig, trie, nodes); err != nil {
   145  		t.Fatalf("Invalid accessList %v", err)
   146  	}
   147  
   148  	// Update trie
   149  	parent := root
   150  	trie, _ = New(TrieID(root), db)
   151  	orig = trie.Copy()
   152  	for _, val := range vals {
   153  		trie.MustUpdate([]byte(val.k), randBytes(32))
   154  	}
   155  	root, nodes, _ = trie.Commit(false)
   156  	db.Update(root, parent, trienode.NewWithNodeSet(nodes))
   157  
   158  	trie, _ = New(TrieID(root), db)
   159  	if err := verifyAccessList(orig, trie, nodes); err != nil {
   160  		t.Fatalf("Invalid accessList %v", err)
   161  	}
   162  
   163  	// Add more new nodes
   164  	parent = root
   165  	trie, _ = New(TrieID(root), db)
   166  	orig = trie.Copy()
   167  	var keys []string
   168  	for i := 0; i < 30; i++ {
   169  		key := randBytes(32)
   170  		keys = append(keys, string(key))
   171  		trie.MustUpdate(key, randBytes(32))
   172  	}
   173  	root, nodes, _ = trie.Commit(false)
   174  	db.Update(root, parent, trienode.NewWithNodeSet(nodes))
   175  
   176  	trie, _ = New(TrieID(root), db)
   177  	if err := verifyAccessList(orig, trie, nodes); err != nil {
   178  		t.Fatalf("Invalid accessList %v", err)
   179  	}
   180  
   181  	// Partial deletions
   182  	parent = root
   183  	trie, _ = New(TrieID(root), db)
   184  	orig = trie.Copy()
   185  	for _, key := range keys {
   186  		trie.MustUpdate([]byte(key), nil)
   187  	}
   188  	root, nodes, _ = trie.Commit(false)
   189  	db.Update(root, parent, trienode.NewWithNodeSet(nodes))
   190  
   191  	trie, _ = New(TrieID(root), db)
   192  	if err := verifyAccessList(orig, trie, nodes); err != nil {
   193  		t.Fatalf("Invalid accessList %v", err)
   194  	}
   195  
   196  	// Delete all
   197  	parent = root
   198  	trie, _ = New(TrieID(root), db)
   199  	orig = trie.Copy()
   200  	for _, val := range vals {
   201  		trie.MustUpdate([]byte(val.k), nil)
   202  	}
   203  	root, nodes, _ = trie.Commit(false)
   204  	db.Update(root, parent, trienode.NewWithNodeSet(nodes))
   205  
   206  	trie, _ = New(TrieID(root), db)
   207  	if err := verifyAccessList(orig, trie, nodes); err != nil {
   208  		t.Fatalf("Invalid accessList %v", err)
   209  	}
   210  }
   211  
   212  // Tests origin values won't be tracked in Iterator or Prover
   213  func TestAccessListLeak(t *testing.T) {
   214  	var (
   215  		db   = newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)
   216  		trie = NewEmpty(db)
   217  	)
   218  	// Create trie from scratch
   219  	for _, val := range standard {
   220  		trie.MustUpdate([]byte(val.k), []byte(val.v))
   221  	}
   222  	root, nodes, _ := trie.Commit(false)
   223  	db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
   224  
   225  	var cases = []struct {
   226  		op func(tr *Trie)
   227  	}{
   228  		{
   229  			func(tr *Trie) {
   230  				it := tr.MustNodeIterator(nil)
   231  				for it.Next(true) {
   232  				}
   233  			},
   234  		},
   235  		{
   236  			func(tr *Trie) {
   237  				it := NewIterator(tr.MustNodeIterator(nil))
   238  				for it.Next() {
   239  				}
   240  			},
   241  		},
   242  		{
   243  			func(tr *Trie) {
   244  				for _, val := range standard {
   245  					tr.Prove([]byte(val.k), rawdb.NewMemoryDatabase())
   246  				}
   247  			},
   248  		},
   249  	}
   250  	for _, c := range cases {
   251  		trie, _ = New(TrieID(root), db)
   252  		n1 := len(trie.tracer.accessList)
   253  		c.op(trie)
   254  		n2 := len(trie.tracer.accessList)
   255  
   256  		if n1 != n2 {
   257  			t.Fatalf("AccessList is leaked, prev %d after %d", n1, n2)
   258  		}
   259  	}
   260  }
   261  
   262  // Tests whether the original tree node is correctly deleted after being embedded
   263  // in its parent due to the smaller size of the original tree node.
   264  func TestTinyTree(t *testing.T) {
   265  	var (
   266  		db   = newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)
   267  		trie = NewEmpty(db)
   268  	)
   269  	for _, val := range tiny {
   270  		trie.MustUpdate([]byte(val.k), randBytes(32))
   271  	}
   272  	root, set, _ := trie.Commit(false)
   273  	db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(set))
   274  
   275  	parent := root
   276  	trie, _ = New(TrieID(root), db)
   277  	orig := trie.Copy()
   278  	for _, val := range tiny {
   279  		trie.MustUpdate([]byte(val.k), []byte(val.v))
   280  	}
   281  	root, set, _ = trie.Commit(false)
   282  	db.Update(root, parent, trienode.NewWithNodeSet(set))
   283  
   284  	trie, _ = New(TrieID(root), db)
   285  	if err := verifyAccessList(orig, trie, set); err != nil {
   286  		t.Fatalf("Invalid accessList %v", err)
   287  	}
   288  }
   289  
   290  func compareSet(setA, setB map[string]struct{}) bool {
   291  	if len(setA) != len(setB) {
   292  		return false
   293  	}
   294  	for key := range setA {
   295  		if _, ok := setB[key]; !ok {
   296  			return false
   297  		}
   298  	}
   299  	return true
   300  }
   301  
   302  func forNodes(tr *Trie) map[string][]byte {
   303  	var (
   304  		it    = tr.MustNodeIterator(nil)
   305  		nodes = make(map[string][]byte)
   306  	)
   307  	for it.Next(true) {
   308  		if it.Leaf() {
   309  			continue
   310  		}
   311  		nodes[string(it.Path())] = common.CopyBytes(it.NodeBlob())
   312  	}
   313  	return nodes
   314  }
   315  
   316  func iterNodes(db *testDb, root common.Hash) map[string][]byte {
   317  	tr, _ := New(TrieID(root), db)
   318  	return forNodes(tr)
   319  }
   320  
   321  func forHashedNodes(tr *Trie) map[string][]byte {
   322  	var (
   323  		it    = tr.MustNodeIterator(nil)
   324  		nodes = make(map[string][]byte)
   325  	)
   326  	for it.Next(true) {
   327  		if it.Hash() == (common.Hash{}) {
   328  			continue
   329  		}
   330  		nodes[string(it.Path())] = common.CopyBytes(it.NodeBlob())
   331  	}
   332  	return nodes
   333  }
   334  
   335  func diffTries(trieA, trieB *Trie) (map[string][]byte, map[string][]byte, map[string][]byte) {
   336  	var (
   337  		nodesA = forHashedNodes(trieA)
   338  		nodesB = forHashedNodes(trieB)
   339  		inA    = make(map[string][]byte) // hashed nodes in trie a but not b
   340  		inB    = make(map[string][]byte) // hashed nodes in trie b but not a
   341  		both   = make(map[string][]byte) // hashed nodes in both tries but different value
   342  	)
   343  	for path, blobA := range nodesA {
   344  		if blobB, ok := nodesB[path]; ok {
   345  			if bytes.Equal(blobA, blobB) {
   346  				continue
   347  			}
   348  			both[path] = blobA
   349  			continue
   350  		}
   351  		inA[path] = blobA
   352  	}
   353  	for path, blobB := range nodesB {
   354  		if _, ok := nodesA[path]; ok {
   355  			continue
   356  		}
   357  		inB[path] = blobB
   358  	}
   359  	return inA, inB, both
   360  }
   361  
   362  func setKeys(set map[string][]byte) map[string]struct{} {
   363  	keys := make(map[string]struct{})
   364  	for k := range set {
   365  		keys[k] = struct{}{}
   366  	}
   367  	return keys
   368  }
   369  
   370  func copySet(set map[string]struct{}) map[string]struct{} {
   371  	copied := make(map[string]struct{})
   372  	for k := range set {
   373  		copied[k] = struct{}{}
   374  	}
   375  	return copied
   376  }