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