github.com/ethereum/go-ethereum@v1.14.3/tests/fuzzers/rangeproof/rangeproof-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 rangeproof 18 19 import ( 20 "bytes" 21 "encoding/binary" 22 "fmt" 23 "io" 24 "slices" 25 26 "github.com/ethereum/go-ethereum/common" 27 "github.com/ethereum/go-ethereum/core/rawdb" 28 "github.com/ethereum/go-ethereum/ethdb/memorydb" 29 "github.com/ethereum/go-ethereum/trie" 30 "github.com/ethereum/go-ethereum/triedb" 31 ) 32 33 type kv struct { 34 k, v []byte 35 t bool 36 } 37 38 type fuzzer struct { 39 input io.Reader 40 exhausted bool 41 } 42 43 func (f *fuzzer) randBytes(n int) []byte { 44 r := make([]byte, n) 45 if _, err := f.input.Read(r); err != nil { 46 f.exhausted = true 47 } 48 return r 49 } 50 51 func (f *fuzzer) readInt() uint64 { 52 var x uint64 53 if err := binary.Read(f.input, binary.LittleEndian, &x); err != nil { 54 f.exhausted = true 55 } 56 return x 57 } 58 59 func (f *fuzzer) randomTrie(n int) (*trie.Trie, map[string]*kv) { 60 trie := trie.NewEmpty(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil)) 61 vals := make(map[string]*kv) 62 size := f.readInt() 63 // Fill it with some fluff 64 for i := byte(0); i < byte(size); i++ { 65 value := &kv{common.LeftPadBytes([]byte{i}, 32), []byte{i}, false} 66 value2 := &kv{common.LeftPadBytes([]byte{i + 10}, 32), []byte{i}, false} 67 trie.MustUpdate(value.k, value.v) 68 trie.MustUpdate(value2.k, value2.v) 69 vals[string(value.k)] = value 70 vals[string(value2.k)] = value2 71 } 72 if f.exhausted { 73 return nil, nil 74 } 75 // And now fill with some random 76 for i := 0; i < n; i++ { 77 k := f.randBytes(32) 78 v := f.randBytes(20) 79 value := &kv{k, v, false} 80 trie.MustUpdate(k, v) 81 vals[string(k)] = value 82 if f.exhausted { 83 return nil, nil 84 } 85 } 86 return trie, vals 87 } 88 89 func (f *fuzzer) fuzz() int { 90 maxSize := 200 91 tr, vals := f.randomTrie(1 + int(f.readInt())%maxSize) 92 if f.exhausted { 93 return 0 // input too short 94 } 95 var entries []*kv 96 for _, kv := range vals { 97 entries = append(entries, kv) 98 } 99 if len(entries) <= 1 { 100 return 0 101 } 102 slices.SortFunc(entries, func(a, b *kv) int { 103 return bytes.Compare(a.k, b.k) 104 }) 105 106 var ok = 0 107 for { 108 start := int(f.readInt() % uint64(len(entries))) 109 end := 1 + int(f.readInt()%uint64(len(entries)-1)) 110 testcase := int(f.readInt() % uint64(6)) 111 index := int(f.readInt() & 0xFFFFFFFF) 112 index2 := int(f.readInt() & 0xFFFFFFFF) 113 if f.exhausted { 114 break 115 } 116 proof := memorydb.New() 117 if err := tr.Prove(entries[start].k, proof); err != nil { 118 panic(fmt.Sprintf("Failed to prove the first node %v", err)) 119 } 120 if err := tr.Prove(entries[end-1].k, proof); err != nil { 121 panic(fmt.Sprintf("Failed to prove the last node %v", err)) 122 } 123 var keys [][]byte 124 var vals [][]byte 125 for i := start; i < end; i++ { 126 keys = append(keys, entries[i].k) 127 vals = append(vals, entries[i].v) 128 } 129 if len(keys) == 0 { 130 return 0 131 } 132 var first = keys[0] 133 testcase %= 6 134 switch testcase { 135 case 0: 136 // Modified key 137 keys[index%len(keys)] = f.randBytes(32) // In theory it can't be same 138 case 1: 139 // Modified val 140 vals[index%len(vals)] = f.randBytes(20) // In theory it can't be same 141 case 2: 142 // Gapped entry slice 143 index = index % len(keys) 144 keys = append(keys[:index], keys[index+1:]...) 145 vals = append(vals[:index], vals[index+1:]...) 146 case 3: 147 // Out of order 148 index1 := index % len(keys) 149 index2 := index2 % len(keys) 150 keys[index1], keys[index2] = keys[index2], keys[index1] 151 vals[index1], vals[index2] = vals[index2], vals[index1] 152 case 4: 153 // Set random key to nil, do nothing 154 keys[index%len(keys)] = nil 155 case 5: 156 // Set random value to nil, deletion 157 vals[index%len(vals)] = nil 158 159 // Other cases: 160 // Modify something in the proof db 161 // add stuff to proof db 162 // drop stuff from proof db 163 } 164 if f.exhausted { 165 break 166 } 167 ok = 1 168 //nodes, subtrie 169 hasMore, err := trie.VerifyRangeProof(tr.Hash(), first, keys, vals, proof) 170 if err != nil { 171 if hasMore { 172 panic("err != nil && hasMore == true") 173 } 174 } 175 } 176 return ok 177 } 178 179 // Fuzz is the fuzzing entry-point. 180 // The function must return 181 // 182 // - 1 if the fuzzer should increase priority of the 183 // given input during subsequent fuzzing (for example, the input is lexically 184 // correct and was parsed successfully); 185 // - -1 if the input must not be added to corpus even if gives new coverage; and 186 // - 0 otherwise 187 // 188 // other values are reserved for future use. 189 func fuzz(input []byte) int { 190 if len(input) < 100 { 191 return 0 192 } 193 r := bytes.NewReader(input) 194 f := fuzzer{ 195 input: r, 196 exhausted: false, 197 } 198 return f.fuzz() 199 }