github.com/theQRL/go-zond@v0.1.1/tests/fuzzers/stacktrie/trie_fuzzer.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 stacktrie 18 19 import ( 20 "bytes" 21 "encoding/binary" 22 "errors" 23 "fmt" 24 "hash" 25 "io" 26 27 "github.com/theQRL/go-zond/common" 28 "github.com/theQRL/go-zond/core/rawdb" 29 "github.com/theQRL/go-zond/core/types" 30 "github.com/theQRL/go-zond/crypto" 31 "github.com/theQRL/go-zond/trie" 32 "github.com/theQRL/go-zond/trie/trienode" 33 "github.com/theQRL/go-zond/zonddb" 34 "golang.org/x/crypto/sha3" 35 "golang.org/x/exp/slices" 36 ) 37 38 type fuzzer struct { 39 input io.Reader 40 exhausted bool 41 debugging bool 42 } 43 44 func (f *fuzzer) read(size int) []byte { 45 out := make([]byte, size) 46 if _, err := f.input.Read(out); err != nil { 47 f.exhausted = true 48 } 49 return out 50 } 51 52 func (f *fuzzer) readSlice(min, max int) []byte { 53 var a uint16 54 binary.Read(f.input, binary.LittleEndian, &a) 55 size := min + int(a)%(max-min) 56 out := make([]byte, size) 57 if _, err := f.input.Read(out); err != nil { 58 f.exhausted = true 59 } 60 return out 61 } 62 63 // spongeDb is a dummy db backend which accumulates writes in a sponge 64 type spongeDb struct { 65 sponge hash.Hash 66 debug bool 67 } 68 69 func (s *spongeDb) Has(key []byte) (bool, error) { panic("implement me") } 70 func (s *spongeDb) Get(key []byte) ([]byte, error) { return nil, errors.New("no such elem") } 71 func (s *spongeDb) Delete(key []byte) error { panic("implement me") } 72 func (s *spongeDb) NewBatch() zonddb.Batch { return &spongeBatch{s} } 73 func (s *spongeDb) NewBatchWithSize(size int) zonddb.Batch { return &spongeBatch{s} } 74 func (s *spongeDb) NewSnapshot() (zonddb.Snapshot, error) { panic("implement me") } 75 func (s *spongeDb) Stat(property string) (string, error) { panic("implement me") } 76 func (s *spongeDb) Compact(start []byte, limit []byte) error { panic("implement me") } 77 func (s *spongeDb) Close() error { return nil } 78 79 func (s *spongeDb) Put(key []byte, value []byte) error { 80 if s.debug { 81 fmt.Printf("db.Put %x : %x\n", key, value) 82 } 83 s.sponge.Write(key) 84 s.sponge.Write(value) 85 return nil 86 } 87 func (s *spongeDb) NewIterator(prefix []byte, start []byte) zonddb.Iterator { panic("implement me") } 88 89 // spongeBatch is a dummy batch which immediately writes to the underlying spongedb 90 type spongeBatch struct { 91 db *spongeDb 92 } 93 94 func (b *spongeBatch) Put(key, value []byte) error { 95 b.db.Put(key, value) 96 return nil 97 } 98 func (b *spongeBatch) Delete(key []byte) error { panic("implement me") } 99 func (b *spongeBatch) ValueSize() int { return 100 } 100 func (b *spongeBatch) Write() error { return nil } 101 func (b *spongeBatch) Reset() {} 102 func (b *spongeBatch) Replay(w zonddb.KeyValueWriter) error { return nil } 103 104 type kv struct { 105 k, v []byte 106 } 107 108 // Fuzz is the fuzzing entry-point. 109 // The function must return 110 // 111 // - 1 if the fuzzer should increase priority of the 112 // given input during subsequent fuzzing (for example, the input is lexically 113 // correct and was parsed successfully); 114 // - -1 if the input must not be added to corpus even if gives new coverage; and 115 // - 0 otherwise 116 // 117 // other values are reserved for future use. 118 func Fuzz(data []byte) int { 119 f := fuzzer{ 120 input: bytes.NewReader(data), 121 exhausted: false, 122 } 123 return f.fuzz() 124 } 125 126 func Debug(data []byte) int { 127 f := fuzzer{ 128 input: bytes.NewReader(data), 129 exhausted: false, 130 debugging: true, 131 } 132 return f.fuzz() 133 } 134 135 func (f *fuzzer) fuzz() int { 136 // This spongeDb is used to check the sequence of disk-db-writes 137 var ( 138 spongeA = &spongeDb{sponge: sha3.NewLegacyKeccak256()} 139 dbA = trie.NewDatabase(rawdb.NewDatabase(spongeA), nil) 140 trieA = trie.NewEmpty(dbA) 141 spongeB = &spongeDb{sponge: sha3.NewLegacyKeccak256()} 142 dbB = trie.NewDatabase(rawdb.NewDatabase(spongeB), nil) 143 trieB = trie.NewStackTrie(func(owner common.Hash, path []byte, hash common.Hash, blob []byte) { 144 rawdb.WriteTrieNode(spongeB, owner, path, hash, blob, dbB.Scheme()) 145 }) 146 vals []kv 147 useful bool 148 maxElements = 10000 149 // operate on unique keys only 150 keys = make(map[string]struct{}) 151 ) 152 // Fill the trie with elements 153 for i := 0; !f.exhausted && i < maxElements; i++ { 154 k := f.read(32) 155 v := f.readSlice(1, 500) 156 if f.exhausted { 157 // If it was exhausted while reading, the value may be all zeroes, 158 // thus 'deletion' which is not supported on stacktrie 159 break 160 } 161 if _, present := keys[string(k)]; present { 162 // This key is a duplicate, ignore it 163 continue 164 } 165 keys[string(k)] = struct{}{} 166 vals = append(vals, kv{k: k, v: v}) 167 trieA.MustUpdate(k, v) 168 useful = true 169 } 170 if !useful { 171 return 0 172 } 173 // Flush trie -> database 174 rootA, nodes, err := trieA.Commit(false) 175 if err != nil { 176 panic(err) 177 } 178 if nodes != nil { 179 dbA.Update(rootA, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) 180 } 181 // Flush memdb -> disk (sponge) 182 dbA.Commit(rootA, false) 183 184 // Stacktrie requires sorted insertion 185 slices.SortFunc(vals, func(a, b kv) int { 186 return bytes.Compare(a.k, b.k) 187 }) 188 for _, kv := range vals { 189 if f.debugging { 190 fmt.Printf("{\"%#x\" , \"%#x\"} // stacktrie.Update\n", kv.k, kv.v) 191 } 192 trieB.MustUpdate(kv.k, kv.v) 193 } 194 rootB := trieB.Hash() 195 trieB.Commit() 196 if rootA != rootB { 197 panic(fmt.Sprintf("roots differ: (trie) %x != %x (stacktrie)", rootA, rootB)) 198 } 199 sumA := spongeA.sponge.Sum(nil) 200 sumB := spongeB.sponge.Sum(nil) 201 if !bytes.Equal(sumA, sumB) { 202 panic(fmt.Sprintf("sequence differ: (trie) %x != %x (stacktrie)", sumA, sumB)) 203 } 204 205 // Ensure all the nodes are persisted correctly 206 var ( 207 nodeset = make(map[string][]byte) // path -> blob 208 trieC = trie.NewStackTrie(func(owner common.Hash, path []byte, hash common.Hash, blob []byte) { 209 if crypto.Keccak256Hash(blob) != hash { 210 panic("invalid node blob") 211 } 212 if owner != (common.Hash{}) { 213 panic("invalid node owner") 214 } 215 nodeset[string(path)] = common.CopyBytes(blob) 216 }) 217 checked int 218 ) 219 for _, kv := range vals { 220 trieC.MustUpdate(kv.k, kv.v) 221 } 222 rootC, _ := trieC.Commit() 223 if rootA != rootC { 224 panic(fmt.Sprintf("roots differ: (trie) %x != %x (stacktrie)", rootA, rootC)) 225 } 226 trieA, _ = trie.New(trie.TrieID(rootA), dbA) 227 iterA := trieA.MustNodeIterator(nil) 228 for iterA.Next(true) { 229 if iterA.Hash() == (common.Hash{}) { 230 if _, present := nodeset[string(iterA.Path())]; present { 231 panic("unexpected tiny node") 232 } 233 continue 234 } 235 nodeBlob, present := nodeset[string(iterA.Path())] 236 if !present { 237 panic("missing node") 238 } 239 if !bytes.Equal(nodeBlob, iterA.NodeBlob()) { 240 panic("node blob is not matched") 241 } 242 checked += 1 243 } 244 if checked != len(nodeset) { 245 panic("node number is not matched") 246 } 247 return 1 248 }