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