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