github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/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 // This spongeDb is used to check the sequence of disk-db-writes 41 var ( 42 input = bytes.NewReader(data) 43 spongeA = &spongeDb{sponge: crypto.NewKeccakState()} 44 dbA = newTestDatabase(rawdb.NewDatabase(spongeA), rawdb.HashScheme) 45 trieA = NewEmpty(dbA) 46 spongeB = &spongeDb{sponge: crypto.NewKeccakState()} 47 dbB = newTestDatabase(rawdb.NewDatabase(spongeB), rawdb.HashScheme) 48 trieB = NewStackTrie(func(path []byte, hash common.Hash, blob []byte) { 49 rawdb.WriteTrieNode(spongeB, common.Hash{}, path, hash, blob, dbB.Scheme()) 50 }) 51 vals []*kv 52 maxElements = 10000 53 // operate on unique keys only 54 keys = make(map[string]struct{}) 55 ) 56 // Fill the trie with elements 57 for i := 0; input.Len() > 0 && i < maxElements; i++ { 58 k := make([]byte, 32) 59 input.Read(k) 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 if input.Len() == 0 { 66 // If it was exhausted while reading, the value may be all zeroes, 67 // thus 'deletion' which is not supported on stacktrie 68 break 69 } 70 if _, present := keys[string(k)]; present { 71 // This key is a duplicate, ignore it 72 continue 73 } 74 keys[string(k)] = struct{}{} 75 vals = append(vals, &kv{k: k, v: v}) 76 trieA.MustUpdate(k, v) 77 } 78 if len(vals) == 0 { 79 return 80 } 81 // Flush trie -> database 82 rootA, nodes, err := trieA.Commit(false) 83 if err != nil { 84 panic(err) 85 } 86 if nodes != nil { 87 dbA.Update(rootA, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) 88 } 89 // Flush memdb -> disk (sponge) 90 dbA.Commit(rootA) 91 92 // Stacktrie requires sorted insertion 93 slices.SortFunc(vals, (*kv).cmp) 94 95 for _, kv := range vals { 96 if debugging { 97 fmt.Printf("{\"%#x\" , \"%#x\"} // stacktrie.Update\n", kv.k, kv.v) 98 } 99 trieB.Update(kv.k, kv.v) 100 } 101 rootB := trieB.Hash() 102 if rootA != rootB { 103 panic(fmt.Sprintf("roots differ: (trie) %x != %x (stacktrie)", rootA, rootB)) 104 } 105 sumA := spongeA.sponge.Sum(nil) 106 sumB := spongeB.sponge.Sum(nil) 107 if !bytes.Equal(sumA, sumB) { 108 panic(fmt.Sprintf("sequence differ: (trie) %x != %x (stacktrie)", sumA, sumB)) 109 } 110 111 // Ensure all the nodes are persisted correctly 112 var ( 113 nodeset = make(map[string][]byte) // path -> blob 114 trieC = NewStackTrie(func(path []byte, hash common.Hash, blob []byte) { 115 if crypto.Keccak256Hash(blob) != hash { 116 panic("invalid node blob") 117 } 118 nodeset[string(path)] = common.CopyBytes(blob) 119 }) 120 checked int 121 ) 122 for _, kv := range vals { 123 trieC.Update(kv.k, kv.v) 124 } 125 rootC := trieC.Hash() 126 if rootA != rootC { 127 panic(fmt.Sprintf("roots differ: (trie) %x != %x (stacktrie)", rootA, rootC)) 128 } 129 trieA, _ = New(TrieID(rootA), dbA) 130 iterA := trieA.MustNodeIterator(nil) 131 for iterA.Next(true) { 132 if iterA.Hash() == (common.Hash{}) { 133 if _, present := nodeset[string(iterA.Path())]; present { 134 panic("unexpected tiny node") 135 } 136 continue 137 } 138 nodeBlob, present := nodeset[string(iterA.Path())] 139 if !present { 140 panic("missing node") 141 } 142 if !bytes.Equal(nodeBlob, iterA.NodeBlob()) { 143 panic("node blob is not matched") 144 } 145 checked += 1 146 } 147 if checked != len(nodeset) { 148 panic("node number is not matched") 149 } 150 }