github.com/ethereum/go-ethereum@v1.16.1/trie/stacktrie_fuzzer_test.go (about)

     1  // Copyright 2020 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  	"slices"
    24  	"testing"
    25  
    26  	"github.com/ethereum/go-ethereum/common"
    27  	"github.com/ethereum/go-ethereum/core/rawdb"
    28  	"github.com/ethereum/go-ethereum/core/types"
    29  	"github.com/ethereum/go-ethereum/crypto"
    30  	"github.com/ethereum/go-ethereum/trie/trienode"
    31  )
    32  
    33  func FuzzStackTrie(f *testing.F) {
    34  	f.Fuzz(func(t *testing.T, data []byte) {
    35  		fuzz(data, false)
    36  	})
    37  }
    38  
    39  func fuzz(data []byte, debugging bool) {
    40  	var (
    41  		input = bytes.NewReader(data)
    42  		dbA   = newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)
    43  		trieA = NewEmpty(dbA)
    44  		memDB = rawdb.NewMemoryDatabase()
    45  		trieB = NewStackTrie(func(path []byte, hash common.Hash, blob []byte) {
    46  			rawdb.WriteTrieNode(memDB, common.Hash{}, path, hash, blob, rawdb.HashScheme)
    47  		})
    48  		vals        []*kv
    49  		maxElements = 10000
    50  		// operate on unique keys only
    51  		keys = make(map[string]struct{})
    52  	)
    53  	// Fill the trie with elements
    54  	for i := 0; input.Len() > 0 && i < maxElements; i++ {
    55  		// Build the key
    56  		k := make([]byte, 32)
    57  		input.Read(k)
    58  
    59  		// Build the val
    60  		var a uint16
    61  		binary.Read(input, binary.LittleEndian, &a)
    62  		a = 1 + a%100
    63  		v := make([]byte, a)
    64  		input.Read(v)
    65  
    66  		if input.Len() == 0 {
    67  			// If it was exhausted while reading, the value may be all zeroes,
    68  			// thus 'deletion' which is not supported on stacktrie
    69  			break
    70  		}
    71  		if _, present := keys[string(k)]; present {
    72  			// This key is a duplicate, ignore it
    73  			continue
    74  		}
    75  		keys[string(k)] = struct{}{}
    76  		vals = append(vals, &kv{k: k, v: v})
    77  
    78  		trieA.MustUpdate(k, v)
    79  	}
    80  	if len(vals) == 0 {
    81  		return
    82  	}
    83  	// Flush trie -> database
    84  	rootA, nodes := trieA.Commit(false)
    85  	if nodes != nil {
    86  		dbA.Update(rootA, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
    87  	}
    88  	// Flush memdb -> disk (sponge)
    89  	dbA.Commit(rootA)
    90  
    91  	// Stacktrie requires sorted insertion
    92  	slices.SortFunc(vals, (*kv).cmp)
    93  
    94  	for _, kv := range vals {
    95  		if debugging {
    96  			fmt.Printf("{\"%#x\" , \"%#x\"} // stacktrie.Update\n", kv.k, kv.v)
    97  		}
    98  		trieB.Update(kv.k, kv.v)
    99  	}
   100  	rootB := trieB.Hash()
   101  	if rootA != rootB {
   102  		panic(fmt.Sprintf("roots differ: (trie) %x != %x (stacktrie)", rootA, rootB))
   103  	}
   104  
   105  	// Ensure all the nodes are persisted correctly
   106  	var (
   107  		nodeset = make(map[string][]byte) // path -> blob
   108  		trieC   = NewStackTrie(func(path []byte, hash common.Hash, blob []byte) {
   109  			if crypto.Keccak256Hash(blob) != hash {
   110  				panic("invalid node blob")
   111  			}
   112  			nodeset[string(path)] = common.CopyBytes(blob)
   113  		})
   114  		checked int
   115  	)
   116  	for _, kv := range vals {
   117  		trieC.Update(kv.k, kv.v)
   118  	}
   119  	rootC := trieC.Hash()
   120  	if rootA != rootC {
   121  		panic(fmt.Sprintf("roots differ: (trie) %x != %x (stacktrie)", rootA, rootC))
   122  	}
   123  	trieA, _ = New(TrieID(rootA), dbA)
   124  	iterA := trieA.MustNodeIterator(nil)
   125  	for iterA.Next(true) {
   126  		if iterA.Hash() == (common.Hash{}) {
   127  			if _, present := nodeset[string(iterA.Path())]; present {
   128  				panic("unexpected tiny node")
   129  			}
   130  			continue
   131  		}
   132  		nodeBlob, present := nodeset[string(iterA.Path())]
   133  		if !present {
   134  			panic("missing node")
   135  		}
   136  		if !bytes.Equal(nodeBlob, iterA.NodeBlob()) {
   137  			panic("node blob is not matched")
   138  		}
   139  		checked += 1
   140  	}
   141  	if checked != len(nodeset) {
   142  		panic("node number is not matched")
   143  	}
   144  }