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  }