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