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 }