diff --git a/tests/fuzzers/abi/abifuzzer_test.go b/accounts/abi/abifuzzer_test.go
similarity index 65%
rename from tests/fuzzers/abi/abifuzzer_test.go
rename to accounts/abi/abifuzzer_test.go
index a206beaf91..4b67947815 100644
--- a/tests/fuzzers/abi/abifuzzer_test.go
+++ b/accounts/abi/abifuzzer_test.go
@@ -22,33 +22,31 @@ import (
"strings"
"testing"
- "github.com/ethereum/go-ethereum/accounts/abi"
fuzz "github.com/google/gofuzz"
)
// TestReplicate can be used to replicate crashers from the fuzzing tests.
// Just replace testString with the data in .quoted
func TestReplicate(t *testing.T) {
- testString := "\x20\x20\x20\x20\x20\x20\x20\x20\x80\x00\x00\x00\x20\x20\x20\x20\x00"
- data := []byte(testString)
- fuzzAbi(data)
+ //t.Skip("Test only useful for reproducing issues")
+ fuzzAbi([]byte("\x20\x20\x20\x20\x20\x20\x20\x20\x80\x00\x00\x00\x20\x20\x20\x20\x00"))
+ //fuzzAbi([]byte("asdfasdfkadsf;lasdf;lasd;lfk"))
}
-func Fuzz(f *testing.F) {
+// FuzzABI is the main entrypoint for fuzzing
+func FuzzABI(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
fuzzAbi(data)
})
}
var (
- names = []string{"_name", "name", "NAME", "name_", "__", "_name_", "n"}
- stateMut = []string{"", "pure", "view", "payable"}
- stateMutabilites = []*string{&stateMut[0], &stateMut[1], &stateMut[2], &stateMut[3]}
- pays = []string{"", "true", "false"}
- payables = []*string{&pays[0], &pays[1]}
- vNames = []string{"a", "b", "c", "d", "e", "f", "g"}
- varNames = append(vNames, names...)
- varTypes = []string{"bool", "address", "bytes", "string",
+ names = []string{"_name", "name", "NAME", "name_", "__", "_name_", "n"}
+ stateMut = []string{"pure", "view", "payable"}
+ pays = []string{"true", "false"}
+ vNames = []string{"a", "b", "c", "d", "e", "f", "g"}
+ varNames = append(vNames, names...)
+ varTypes = []string{"bool", "address", "bytes", "string",
"uint8", "int8", "uint8", "int8", "uint16", "int16",
"uint24", "int24", "uint32", "int32", "uint40", "int40", "uint48", "int48", "uint56", "int56",
"uint64", "int64", "uint72", "int72", "uint80", "int80", "uint88", "int88", "uint96", "int96",
@@ -62,7 +60,7 @@ var (
"bytes32", "bytes"}
)
-func unpackPack(abi abi.ABI, method string, input []byte) ([]interface{}, bool) {
+func unpackPack(abi ABI, method string, input []byte) ([]interface{}, bool) {
if out, err := abi.Unpack(method, input); err == nil {
_, err := abi.Pack(method, out...)
if err != nil {
@@ -78,7 +76,7 @@ func unpackPack(abi abi.ABI, method string, input []byte) ([]interface{}, bool)
return nil, false
}
-func packUnpack(abi abi.ABI, method string, input *[]interface{}) bool {
+func packUnpack(abi ABI, method string, input *[]interface{}) bool {
if packed, err := abi.Pack(method, input); err == nil {
outptr := reflect.New(reflect.TypeOf(input))
err := abi.UnpackIntoInterface(outptr.Interface(), method, packed)
@@ -94,12 +92,12 @@ func packUnpack(abi abi.ABI, method string, input *[]interface{}) bool {
return false
}
-type args struct {
+type arg struct {
name string
typ string
}
-func createABI(name string, stateMutability, payable *string, inputs []args) (abi.ABI, error) {
+func createABI(name string, stateMutability, payable *string, inputs []arg) (ABI, error) {
sig := fmt.Sprintf(`[{ "type" : "function", "name" : "%v" `, name)
if stateMutability != nil {
sig += fmt.Sprintf(`, "stateMutability": "%v" `, *stateMutability)
@@ -126,56 +124,55 @@ func createABI(name string, stateMutability, payable *string, inputs []args) (ab
sig += "} ]"
}
sig += `}]`
-
- return abi.JSON(strings.NewReader(sig))
+ //fmt.Printf("sig: %s\n", sig)
+ return JSON(strings.NewReader(sig))
}
-func fuzzAbi(input []byte) int {
- good := false
- fuzzer := fuzz.NewFromGoFuzz(input)
-
- name := names[getUInt(fuzzer)%len(names)]
- stateM := stateMutabilites[getUInt(fuzzer)%len(stateMutabilites)]
- payable := payables[getUInt(fuzzer)%len(payables)]
- maxLen := 5
- for k := 1; k < maxLen; k++ {
- var arg []args
- for i := k; i > 0; i-- {
- argName := varNames[i]
- argTyp := varTypes[getUInt(fuzzer)%len(varTypes)]
- if getUInt(fuzzer)%10 == 0 {
- argTyp += "[]"
- } else if getUInt(fuzzer)%10 == 0 {
- arrayArgs := getUInt(fuzzer)%30 + 1
- argTyp += fmt.Sprintf("[%d]", arrayArgs)
- }
- arg = append(arg, args{
- name: argName,
- typ: argTyp,
- })
+func fuzzAbi(input []byte) {
+ var (
+ fuzzer = fuzz.NewFromGoFuzz(input)
+ name = oneOf(fuzzer, names)
+ stateM = oneOfOrNil(fuzzer, stateMut)
+ payable = oneOfOrNil(fuzzer, pays)
+ arguments []arg
+ )
+ for i := 0; i < upTo(fuzzer, 10); i++ {
+ argName := oneOf(fuzzer, varNames)
+ argTyp := oneOf(fuzzer, varTypes)
+ switch upTo(fuzzer, 10) {
+ case 0: // 10% chance to make it a slice
+ argTyp += "[]"
+ case 1: // 10% chance to make it an array
+ argTyp += fmt.Sprintf("[%d]", 1+upTo(fuzzer, 30))
+ default:
}
- abi, err := createABI(name, stateM, payable, arg)
- if err != nil {
- continue
- }
- structs, b := unpackPack(abi, name, input)
- c := packUnpack(abi, name, &structs)
- good = good || b || c
+ arguments = append(arguments, arg{name: argName, typ: argTyp})
}
- if good {
- return 1
+ abi, err := createABI(name, stateM, payable, arguments)
+ if err != nil {
+ //fmt.Printf("err: %v\n", err)
+ panic(err)
}
- return 0
+ structs, _ := unpackPack(abi, name, input)
+ _ = packUnpack(abi, name, &structs)
}
-func getUInt(fuzzer *fuzz.Fuzzer) int {
+func upTo(fuzzer *fuzz.Fuzzer, max int) int {
var i int
fuzzer.Fuzz(&i)
if i < 0 {
- i = -i
- if i < 0 {
- return 0
- }
+ return (-1 - i) % max
}
- return i
+ return i % max
+}
+
+func oneOf(fuzzer *fuzz.Fuzzer, options []string) string {
+ return options[upTo(fuzzer, len(options))]
+}
+
+func oneOfOrNil(fuzzer *fuzz.Fuzzer, options []string) *string {
+ if i := upTo(fuzzer, len(options)+1); i < len(options) {
+ return &options[i]
+ }
+ return nil
}
diff --git a/tests/fuzzers/keystore/keystore_test.go b/accounts/keystore/keystore_fuzzing_test.go
similarity index 72%
rename from tests/fuzzers/keystore/keystore_test.go
rename to accounts/keystore/keystore_fuzzing_test.go
index 167ff6c471..793b46336a 100644
--- a/tests/fuzzers/keystore/keystore_test.go
+++ b/accounts/keystore/keystore_fuzzing_test.go
@@ -16,10 +16,19 @@
package keystore
-import "testing"
+import (
+ "testing"
+)
-func Fuzz(f *testing.F) {
- f.Fuzz(func(t *testing.T, data []byte) {
- fuzz(data)
+func FuzzPassword(f *testing.F) {
+ f.Fuzz(func(t *testing.T, password string) {
+ ks := NewKeyStore(t.TempDir(), LightScryptN, LightScryptP)
+ a, err := ks.NewAccount(password)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := ks.Unlock(a, password); err != nil {
+ t.Fatal(err)
+ }
})
}
diff --git a/common/bitutil/compress_test.go b/common/bitutil/compress_test.go
index 13a13011dc..c6f6fe8bcf 100644
--- a/common/bitutil/compress_test.go
+++ b/common/bitutil/compress_test.go
@@ -18,6 +18,7 @@ package bitutil
import (
"bytes"
+ "fmt"
"math/rand"
"testing"
@@ -48,19 +49,23 @@ func TestEncodingCycle(t *testing.T) {
"0xdf7070533534333636313639343638373532313536346c1bc333393438373130707063363430353639343638373532313536346c1bc333393438336336346c65fe",
}
for i, tt := range tests {
- data := hexutil.MustDecode(tt)
-
- proc, err := bitsetDecodeBytes(bitsetEncodeBytes(data), len(data))
- if err != nil {
- t.Errorf("test %d: failed to decompress compressed data: %v", i, err)
- continue
- }
- if !bytes.Equal(data, proc) {
- t.Errorf("test %d: compress/decompress mismatch: have %x, want %x", i, proc, data)
+ if err := testEncodingCycle(hexutil.MustDecode(tt)); err != nil {
+ t.Errorf("test %d: %v", i, err)
}
}
}
+func testEncodingCycle(data []byte) error {
+ proc, err := bitsetDecodeBytes(bitsetEncodeBytes(data), len(data))
+ if err != nil {
+ return fmt.Errorf("failed to decompress compressed data: %v", err)
+ }
+ if !bytes.Equal(data, proc) {
+ return fmt.Errorf("compress/decompress mismatch: have %x, want %x", proc, data)
+ }
+ return nil
+}
+
// Tests that data bitset decoding and rencoding works and is bijective.
func TestDecodingCycle(t *testing.T) {
tests := []struct {
@@ -179,3 +184,40 @@ func benchmarkEncoding(b *testing.B, bytes int, fill float64) {
bitsetDecodeBytes(bitsetEncodeBytes(data), len(data))
}
}
+
+func FuzzEncoder(f *testing.F) {
+ f.Fuzz(func(t *testing.T, data []byte) {
+ if err := testEncodingCycle(data); err != nil {
+ t.Fatal(err)
+ }
+ })
+}
+func FuzzDecoder(f *testing.F) {
+ f.Fuzz(func(t *testing.T, data []byte) {
+ fuzzDecode(data)
+ })
+}
+
+// fuzzDecode implements a go-fuzz fuzzer method to test the bit decoding and
+// reencoding algorithm.
+func fuzzDecode(data []byte) {
+ blob, err := DecompressBytes(data, 1024)
+ if err != nil {
+ return
+ }
+ // re-compress it (it's OK if the re-compressed differs from the
+ // original - the first input may not have been compressed at all)
+ comp := CompressBytes(blob)
+ if len(comp) > len(blob) {
+ // After compression, it must be smaller or equal
+ panic("bad compression")
+ }
+ // But decompressing it once again should work
+ decomp, err := DecompressBytes(data, 1024)
+ if err != nil {
+ panic(err)
+ }
+ if !bytes.Equal(decomp, blob) {
+ panic("content mismatch")
+ }
+}
diff --git a/core/types/rlp_fuzzer_test.go b/core/types/rlp_fuzzer_test.go
new file mode 100644
index 0000000000..a3b9f72436
--- /dev/null
+++ b/core/types/rlp_fuzzer_test.go
@@ -0,0 +1,147 @@
+// Copyright 2019 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package types
+
+import (
+ "bytes"
+ "fmt"
+ "math/big"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/rlp"
+ "github.com/holiman/uint256"
+)
+
+func decodeEncode(input []byte, val interface{}) error {
+ if err := rlp.DecodeBytes(input, val); err != nil {
+ // not valid rlp, nothing to do
+ return nil
+ }
+ // If it _were_ valid rlp, we can encode it again
+ output, err := rlp.EncodeToBytes(val)
+ if err != nil {
+ return err
+ }
+ if !bytes.Equal(input, output) {
+ return fmt.Errorf("encode-decode is not equal, \ninput : %x\noutput: %x", input, output)
+ }
+ return nil
+}
+
+func FuzzRLP(f *testing.F) {
+ f.Fuzz(fuzzRlp)
+}
+
+func fuzzRlp(t *testing.T, input []byte) {
+ if len(input) == 0 || len(input) > 500*1024 {
+ return
+ }
+ rlp.Split(input)
+ if elems, _, err := rlp.SplitList(input); err == nil {
+ rlp.CountValues(elems)
+ }
+ rlp.NewStream(bytes.NewReader(input), 0).Decode(new(interface{}))
+ if err := decodeEncode(input, new(interface{})); err != nil {
+ t.Fatal(err)
+ }
+ {
+ var v struct {
+ Int uint
+ String string
+ Bytes []byte
+ }
+ if err := decodeEncode(input, &v); err != nil {
+ t.Fatal(err)
+ }
+ }
+ {
+ type Types struct {
+ Bool bool
+ Raw rlp.RawValue
+ Slice []*Types
+ Iface []interface{}
+ }
+ var v Types
+ if err := decodeEncode(input, &v); err != nil {
+ t.Fatal(err)
+ }
+ }
+ {
+ type AllTypes struct {
+ Int uint
+ String string
+ Bytes []byte
+ Bool bool
+ Raw rlp.RawValue
+ Slice []*AllTypes
+ Array [3]*AllTypes
+ Iface []interface{}
+ }
+ var v AllTypes
+ if err := decodeEncode(input, &v); err != nil {
+ t.Fatal(err)
+ }
+ }
+ {
+ if err := decodeEncode(input, [10]byte{}); err != nil {
+ t.Fatal(err)
+ }
+ }
+ {
+ var v struct {
+ Byte [10]byte
+ Rool [10]bool
+ }
+ if err := decodeEncode(input, &v); err != nil {
+ t.Fatal(err)
+ }
+ }
+ {
+ var h Header
+ if err := decodeEncode(input, &h); err != nil {
+ t.Fatal(err)
+ }
+ var b Block
+ if err := decodeEncode(input, &b); err != nil {
+ t.Fatal(err)
+ }
+ var tx Transaction
+ if err := decodeEncode(input, &tx); err != nil {
+ t.Fatal(err)
+ }
+ var txs Transactions
+ if err := decodeEncode(input, &txs); err != nil {
+ t.Fatal(err)
+ }
+ var rs Receipts
+ if err := decodeEncode(input, &rs); err != nil {
+ t.Fatal(err)
+ }
+ }
+ {
+ var v struct {
+ AnIntPtr *big.Int
+ AnInt big.Int
+ AnU256Ptr *uint256.Int
+ AnU256 uint256.Int
+ NotAnU256 [4]uint64
+ }
+ if err := decodeEncode(input, &v); err != nil {
+ t.Fatal(err)
+ }
+ }
+}
diff --git a/tests/fuzzers/rlp/rlp_test.go b/core/vm/contracts_fuzz_test.go
similarity index 57%
rename from tests/fuzzers/rlp/rlp_test.go
rename to core/vm/contracts_fuzz_test.go
index 377b3961bf..87c1fff7cc 100644
--- a/tests/fuzzers/rlp/rlp_test.go
+++ b/core/vm/contracts_fuzz_test.go
@@ -14,12 +14,31 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package rlp
+package vm
-import "testing"
+import (
+ "testing"
-func Fuzz(f *testing.F) {
- f.Fuzz(func(t *testing.T, data []byte) {
- fuzz(data)
+ "github.com/ethereum/go-ethereum/common"
+)
+
+func FuzzPrecompiledContracts(f *testing.F) {
+ // Create list of addresses
+ var addrs []common.Address
+ for k := range allPrecompiles {
+ addrs = append(addrs, k)
+ }
+ f.Fuzz(func(t *testing.T, addr uint8, input []byte) {
+ a := addrs[int(addr)%len(addrs)]
+ p := allPrecompiles[a]
+ gas := p.RequiredGas(input)
+ if gas > 10_000_000 {
+ return
+ }
+ inWant := string(input)
+ RunPrecompiledContract(p, input, gas)
+ if inHave := string(input); inWant != inHave {
+ t.Errorf("Precompiled %v modified input data", a)
+ }
})
}
diff --git a/tests/fuzzers/runtime/runtime_test.go b/core/vm/runtime/runtime_fuzz_test.go
similarity index 87%
rename from tests/fuzzers/runtime/runtime_test.go
rename to core/vm/runtime/runtime_fuzz_test.go
index 2d73a56ca1..8a4d31d819 100644
--- a/tests/fuzzers/runtime/runtime_test.go
+++ b/core/vm/runtime/runtime_fuzz_test.go
@@ -18,13 +18,11 @@ package runtime
import (
"testing"
-
- "github.com/ethereum/go-ethereum/core/vm/runtime"
)
-func Fuzz(f *testing.F) {
+func FuzzVmRuntime(f *testing.F) {
f.Fuzz(func(t *testing.T, code, input []byte) {
- runtime.Execute(code, input, &runtime.Config{
+ Execute(code, input, &Config{
GasLimit: 12000000,
})
})
diff --git a/tests/fuzzers/snap/fuzz_handler.go b/eth/protocols/snap/handler_fuzzing_test.go
similarity index 77%
rename from tests/fuzzers/snap/fuzz_handler.go
rename to eth/protocols/snap/handler_fuzzing_test.go
index 20521bb92a..daed7ed44a 100644
--- a/tests/fuzzers/snap/fuzz_handler.go
+++ b/eth/protocols/snap/handler_fuzzing_test.go
@@ -21,6 +21,7 @@ import (
"encoding/binary"
"fmt"
"math/big"
+ "testing"
"time"
"github.com/ethereum/go-ethereum/common"
@@ -28,7 +29,6 @@ import (
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/eth/protocols/snap"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/params"
@@ -36,6 +36,56 @@ import (
fuzz "github.com/google/gofuzz"
)
+func FuzzARange(f *testing.F) {
+ f.Fuzz(func(t *testing.T, data []byte) {
+ doFuzz(data, &GetAccountRangePacket{}, GetAccountRangeMsg)
+ })
+}
+
+func FuzzSRange(f *testing.F) {
+ f.Fuzz(func(t *testing.T, data []byte) {
+ doFuzz(data, &GetStorageRangesPacket{}, GetStorageRangesMsg)
+ })
+}
+
+func FuzzByteCodes(f *testing.F) {
+ f.Fuzz(func(t *testing.T, data []byte) {
+ doFuzz(data, &GetByteCodesPacket{}, GetByteCodesMsg)
+ })
+}
+
+func FuzzTrieNodes(f *testing.F) {
+ f.Fuzz(func(t *testing.T, data []byte) {
+ doFuzz(data, &GetTrieNodesPacket{}, GetTrieNodesMsg)
+ })
+}
+
+func doFuzz(input []byte, obj interface{}, code int) {
+ bc := getChain()
+ defer bc.Stop()
+ fuzz.NewFromGoFuzz(input).Fuzz(obj)
+ var data []byte
+ switch p := obj.(type) {
+ case *GetTrieNodesPacket:
+ p.Root = trieRoot
+ data, _ = rlp.EncodeToBytes(obj)
+ default:
+ data, _ = rlp.EncodeToBytes(obj)
+ }
+ cli := &dummyRW{
+ code: uint64(code),
+ data: data,
+ }
+ peer := NewFakePeer(65, "gazonk01", cli)
+ err := HandleMessage(&dummyBackend{bc}, peer)
+ switch {
+ case err == nil && cli.writeCount != 1:
+ panic(fmt.Sprintf("Expected 1 response, got %d", cli.writeCount))
+ case err != nil && cli.writeCount != 0:
+ panic(fmt.Sprintf("Expected 0 response, got %d", cli.writeCount))
+ }
+}
+
var trieRoot common.Hash
func getChain() *core.BlockChain {
@@ -86,10 +136,10 @@ type dummyBackend struct {
chain *core.BlockChain
}
-func (d *dummyBackend) Chain() *core.BlockChain { return d.chain }
-func (d *dummyBackend) RunPeer(*snap.Peer, snap.Handler) error { return nil }
-func (d *dummyBackend) PeerInfo(enode.ID) interface{} { return "Foo" }
-func (d *dummyBackend) Handle(*snap.Peer, snap.Packet) error { return nil }
+func (d *dummyBackend) Chain() *core.BlockChain { return d.chain }
+func (d *dummyBackend) RunPeer(*Peer, Handler) error { return nil }
+func (d *dummyBackend) PeerInfo(enode.ID) interface{} { return "Foo" }
+func (d *dummyBackend) Handle(*Peer, Packet) error { return nil }
type dummyRW struct {
code uint64
@@ -110,34 +160,3 @@ func (d *dummyRW) WriteMsg(msg p2p.Msg) error {
d.writeCount++
return nil
}
-
-func doFuzz(input []byte, obj interface{}, code int) int {
- if len(input) > 1024*4 {
- return -1
- }
- bc := getChain()
- defer bc.Stop()
- backend := &dummyBackend{bc}
- fuzz.NewFromGoFuzz(input).Fuzz(obj)
- var data []byte
- switch p := obj.(type) {
- case *snap.GetTrieNodesPacket:
- p.Root = trieRoot
- data, _ = rlp.EncodeToBytes(obj)
- default:
- data, _ = rlp.EncodeToBytes(obj)
- }
- cli := &dummyRW{
- code: uint64(code),
- data: data,
- }
- peer := snap.NewFakePeer(65, "gazonk01", cli)
- err := snap.HandleMessage(backend, peer)
- switch {
- case err == nil && cli.writeCount != 1:
- panic(fmt.Sprintf("Expected 1 response, got %d", cli.writeCount))
- case err != nil && cli.writeCount != 0:
- panic(fmt.Sprintf("Expected 0 response, got %d", cli.writeCount))
- }
- return 1
-}
diff --git a/go.mod b/go.mod
index 4d7ddcfc73..32cfe26b14 100644
--- a/go.mod
+++ b/go.mod
@@ -33,7 +33,7 @@ require (
github.com/golang-jwt/jwt/v4 v4.5.0
github.com/golang/protobuf v1.5.3
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb
- github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa
+ github.com/google/gofuzz v1.2.0
github.com/google/uuid v1.3.0
github.com/gorilla/websocket v1.4.2
github.com/graph-gophers/graphql-go v1.3.0
diff --git a/go.sum b/go.sum
index 765a9da86e..e62d7d36ab 100644
--- a/go.sum
+++ b/go.sum
@@ -305,8 +305,8 @@ github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa h1:Q75Upo5UN4JbPFURXZ8nLKYUvF85dyFRop/vQ0Rv+64=
-github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
+github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
diff --git a/oss-fuzz.sh b/oss-fuzz.sh
index 55660d08e3..8978de70dd 100644
--- a/oss-fuzz.sh
+++ b/oss-fuzz.sh
@@ -48,39 +48,27 @@ DOG
cd -
}
-function build_native_go_fuzzer() {
- fuzzer=$1
- function=$2
- path=$3
- tags="-tags gofuzz"
-
- if [[ $SANITIZER == *coverage* ]]; then
- coverbuild $path $function $fuzzer $coverpkg
- else
- go-118-fuzz-build $tags -o $fuzzer.a -func $function $path
- $CXX $CXXFLAGS $LIB_FUZZING_ENGINE $fuzzer.a -o $OUT/$fuzzer
- fi
-}
-
function compile_fuzzer() {
- path=$GOPATH/src/github.com/ethereum/go-ethereum/$1
+ package=$1
function=$2
fuzzer=$3
+ file=$4
+
+ path=$GOPATH/src/$package
echo "Building $fuzzer"
cd $path
# Install build dependencies
- go install github.com/AdamKorcz/go-118-fuzz-build@latest
- go get github.com/AdamKorcz/go-118-fuzz-build/testing
+ go mod tidy
+ go get github.com/holiman/gofuzz-shim/testing
- # Test if file contains a line with "func $function(" and "testing.F".
- if [ $(grep -r "func $function(" $path | grep "testing.F" | wc -l) -eq 1 ]
- then
- build_native_go_fuzzer $fuzzer $function $path
- else
- echo "Could not find the function: func ${function}(f *testing.F)"
- fi
+ if [[ $SANITIZER == *coverage* ]]; then
+ coverbuild $path $function $fuzzer $coverpkg
+ else
+ gofuzz-shim --func $function --package $package -f $file -o $fuzzer.a
+ $CXX $CXXFLAGS $LIB_FUZZING_ENGINE $fuzzer.a -o $OUT/$fuzzer
+ fi
## Check if there exists a seed corpus file
corpusfile="${path}/testdata/${fuzzer}_seed_corpus.zip"
@@ -92,42 +80,140 @@ function compile_fuzzer() {
cd -
}
-compile_fuzzer tests/fuzzers/bitutil FuzzEncoder fuzzBitutilEncoder
-compile_fuzzer tests/fuzzers/bitutil FuzzDecoder fuzzBitutilDecoder
-compile_fuzzer tests/fuzzers/bn256 FuzzAdd fuzzBn256Add
-compile_fuzzer tests/fuzzers/bn256 FuzzMul fuzzBn256Mul
-compile_fuzzer tests/fuzzers/bn256 FuzzPair fuzzBn256Pair
-compile_fuzzer tests/fuzzers/runtime Fuzz fuzzVmRuntime
-compile_fuzzer tests/fuzzers/keystore Fuzz fuzzKeystore
-compile_fuzzer tests/fuzzers/txfetcher Fuzz fuzzTxfetcher
-compile_fuzzer tests/fuzzers/rlp Fuzz fuzzRlp
-compile_fuzzer tests/fuzzers/trie Fuzz fuzzTrie
-compile_fuzzer tests/fuzzers/stacktrie Fuzz fuzzStackTrie
-compile_fuzzer tests/fuzzers/difficulty Fuzz fuzzDifficulty
-compile_fuzzer tests/fuzzers/abi Fuzz fuzzAbi
-compile_fuzzer tests/fuzzers/les Fuzz fuzzLes
-compile_fuzzer tests/fuzzers/secp256k1 Fuzz fuzzSecp256k1
-compile_fuzzer tests/fuzzers/vflux FuzzClientPool fuzzClientPool
+go install github.com/holiman/gofuzz-shim@latest
+repo=$GOPATH/src/github.com/ethereum/go-ethereum
+compile_fuzzer github.com/ethereum/go-ethereum/accounts/abi \
+ FuzzABI fuzzAbi \
+ $repo/accounts/abi/abifuzzer_test.go
-compile_fuzzer tests/fuzzers/bls12381 FuzzG1Add fuzz_g1_add
-compile_fuzzer tests/fuzzers/bls12381 FuzzG1Mul fuzz_g1_mul
-compile_fuzzer tests/fuzzers/bls12381 FuzzG1MultiExp fuzz_g1_multiexp
-compile_fuzzer tests/fuzzers/bls12381 FuzzG2Add fuzz_g2_add
-compile_fuzzer tests/fuzzers/bls12381 FuzzG2Mul fuzz_g2_mul
-compile_fuzzer tests/fuzzers/bls12381 FuzzG2MultiExp fuzz_g2_multiexp
-compile_fuzzer tests/fuzzers/bls12381 FuzzPairing fuzz_pairing
-compile_fuzzer tests/fuzzers/bls12381 FuzzMapG1 fuzz_map_g1
-compile_fuzzer tests/fuzzers/bls12381 FuzzMapG2 fuzz_map_g2
+compile_fuzzer github.com/ethereum/go-ethereum/common/bitutil \
+ FuzzEncoder fuzzBitutilEncoder \
+ $repo/common/bitutil/compress_test.go
-compile_fuzzer tests/fuzzers/bls12381 FuzzCrossG1Add fuzz_cross_g1_add
-compile_fuzzer tests/fuzzers/bls12381 FuzzCrossG1MultiExp fuzz_cross_g1_multiexp
-compile_fuzzer tests/fuzzers/bls12381 FuzzCrossG2Add fuzz_cross_g2_add
-compile_fuzzer tests/fuzzers/bls12381 FuzzCrossPairing fuzz_cross_pairing
+compile_fuzzer github.com/ethereum/go-ethereum/common/bitutil \
+ FuzzDecoder fuzzBitutilDecoder \
+ $repo/common/bitutil/compress_test.go
-compile_fuzzer tests/fuzzers/snap FuzzARange fuzz_account_range
-compile_fuzzer tests/fuzzers/snap FuzzSRange fuzz_storage_range
-compile_fuzzer tests/fuzzers/snap FuzzByteCodes fuzz_byte_codes
-compile_fuzzer tests/fuzzers/snap FuzzTrieNodes fuzz_trie_nodes
+compile_fuzzer github.com/ethereum/go-ethereum/core/vm/runtime \
+ FuzzVmRuntime fuzzVmRuntime\
+ $repo/core/vm/runtime/runtime_fuzz_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/core/vm \
+ FuzzPrecompiledContracts fuzzPrecompiledContracts\
+ $repo/core/vm/contracts_fuzz_test.go,$repo/core/vm/contracts_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/core/types \
+ FuzzRLP fuzzRlp \
+ $repo/core/types/rlp_fuzzer_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/crypto/blake2b \
+ Fuzz fuzzBlake2b \
+ $repo/crypto/blake2b/blake2b_f_fuzz_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/accounts/keystore \
+ FuzzPassword fuzzKeystore \
+ $repo/accounts/keystore/keystore_fuzzing_test.go
+
+pkg=$repo/trie/
+compile_fuzzer github.com/ethereum/go-ethereum/trie \
+ FuzzTrie fuzzTrie \
+ $pkg/trie_test.go,$pkg/database_test.go,$pkg/tracer_test.go,$pkg/proof_test.go,$pkg/iterator_test.go,$pkg/sync_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/trie \
+ FuzzStackTrie fuzzStackTrie \
+ $pkg/stacktrie_fuzzer_test.go,$pkg/iterator_test.go,$pkg/trie_test.go,$pkg/database_test.go,$pkg/tracer_test.go,$pkg/proof_test.go,$pkg/sync_test.go
+
+#compile_fuzzer tests/fuzzers/snap FuzzARange fuzz_account_range
+compile_fuzzer github.com/ethereum/go-ethereum/eth/protocols/snap \
+ FuzzARange fuzz_account_range \
+ $repo/eth/protocols/snap/handler_fuzzing_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/eth/protocols/snap \
+ FuzzSRange fuzz_storage_range \
+ $repo/eth/protocols/snap/handler_fuzzing_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/eth/protocols/snap \
+ FuzzByteCodes fuzz_byte_codes \
+ $repo/eth/protocols/snap/handler_fuzzing_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/eth/protocols/snap \
+ FuzzTrieNodes fuzz_trie_nodes\
+ $repo/eth/protocols/snap/handler_fuzzing_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bn256 \
+ FuzzAdd fuzzBn256Add\
+ $repo/tests/fuzzers/bn256/bn256_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bn256 \
+ FuzzMul fuzzBn256Mul \
+ $repo/tests/fuzzers/bn256/bn256_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bn256 \
+ FuzzPair fuzzBn256Pair \
+ $repo/tests/fuzzers/bn256/bn256_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/txfetcher \
+ Fuzz fuzzTxfetcher \
+ $repo/tests/fuzzers/txfetcher/txfetcher_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
+ FuzzG1Add fuzz_g1_add\
+ $repo/tests/fuzzers/bls12381/bls12381_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
+ FuzzG1Mul fuzz_g1_mul\
+ $repo/tests/fuzzers/bls12381/bls12381_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
+ FuzzG1MultiExp fuzz_g1_multiexp \
+ $repo/tests/fuzzers/bls12381/bls12381_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
+ FuzzG2Add fuzz_g2_add \
+ $repo/tests/fuzzers/bls12381/bls12381_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
+ FuzzG2Mul fuzz_g2_mul\
+ $repo/tests/fuzzers/bls12381/bls12381_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
+ FuzzG2MultiExp fuzz_g2_multiexp \
+ $repo/tests/fuzzers/bls12381/bls12381_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
+ FuzzPairing fuzz_pairing \
+ $repo/tests/fuzzers/bls12381/bls12381_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
+ FuzzMapG1 fuzz_map_g1\
+ $repo/tests/fuzzers/bls12381/bls12381_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
+ FuzzMapG2 fuzz_map_g2 \
+ $repo/tests/fuzzers/bls12381/bls12381_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
+ FuzzCrossG1Add fuzz_cross_g1_add \
+ $repo/tests/fuzzers/bls12381/bls12381_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
+ FuzzCrossG1MultiExp fuzz_cross_g1_multiexp \
+ $repo/tests/fuzzers/bls12381/bls12381_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
+ FuzzCrossG2Add fuzz_cross_g2_add \
+ $repo/tests/fuzzers/bls12381/bls12381_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
+ FuzzCrossPairing fuzz_cross_pairing\
+ $repo/tests/fuzzers/bls12381/bls12381_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/secp256k1 \
+ Fuzz fuzzSecp256k1\
+ $repo/tests/fuzzers/secp256k1/secp_test.go
+
+
+#compile_fuzzer tests/fuzzers/vflux FuzzClientPool fuzzClientPool
+#compile_fuzzer tests/fuzzers/difficulty Fuzz fuzzDifficulty
+#compile_fuzzer tests/fuzzers/les Fuzz fuzzLes
-#TODO: move this to tests/fuzzers, if possible
-compile_fuzzer crypto/blake2b Fuzz fuzzBlake2b
diff --git a/tests/fuzzers/bitutil/compress_test.go b/tests/fuzzers/bitutil/compress_test.go
deleted file mode 100644
index ed9d27eb30..0000000000
--- a/tests/fuzzers/bitutil/compress_test.go
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2023 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package bitutil
-
-import (
- "bytes"
- "testing"
-
- "github.com/ethereum/go-ethereum/common/bitutil"
-)
-
-func FuzzEncoder(f *testing.F) {
- f.Fuzz(func(t *testing.T, data []byte) {
- fuzzEncode(data)
- })
-}
-func FuzzDecoder(f *testing.F) {
- f.Fuzz(func(t *testing.T, data []byte) {
- fuzzDecode(data)
- })
-}
-
-// fuzzEncode implements a go-fuzz fuzzer method to test the bitset encoding and
-// decoding algorithm.
-func fuzzEncode(data []byte) {
- proc, _ := bitutil.DecompressBytes(bitutil.CompressBytes(data), len(data))
- if !bytes.Equal(data, proc) {
- panic("content mismatch")
- }
-}
-
-// fuzzDecode implements a go-fuzz fuzzer method to test the bit decoding and
-// reencoding algorithm.
-func fuzzDecode(data []byte) {
- blob, err := bitutil.DecompressBytes(data, 1024)
- if err != nil {
- return
- }
- // re-compress it (it's OK if the re-compressed differs from the
- // original - the first input may not have been compressed at all)
- comp := bitutil.CompressBytes(blob)
- if len(comp) > len(blob) {
- // After compression, it must be smaller or equal
- panic("bad compression")
- }
- // But decompressing it once again should work
- decomp, err := bitutil.DecompressBytes(data, 1024)
- if err != nil {
- panic(err)
- }
- if !bytes.Equal(decomp, blob) {
- panic("content mismatch")
- }
-}
diff --git a/tests/fuzzers/keystore/keystore-fuzzer.go b/tests/fuzzers/keystore/keystore-fuzzer.go
deleted file mode 100644
index 07a85d77b5..0000000000
--- a/tests/fuzzers/keystore/keystore-fuzzer.go
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2019 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package keystore
-
-import (
- "os"
-
- "github.com/ethereum/go-ethereum/accounts/keystore"
-)
-
-func fuzz(input []byte) int {
- ks := keystore.NewKeyStore("/tmp/ks", keystore.LightScryptN, keystore.LightScryptP)
-
- a, err := ks.NewAccount(string(input))
- if err != nil {
- panic(err)
- }
- if err := ks.Unlock(a, string(input)); err != nil {
- panic(err)
- }
- os.Remove(a.URL.Path)
- return 1
-}
diff --git a/tests/fuzzers/rlp/corpus/block_with_uncle.rlp b/tests/fuzzers/rlp/corpus/block_with_uncle.rlp
deleted file mode 100644
index 1b49fe6a09..0000000000
Binary files a/tests/fuzzers/rlp/corpus/block_with_uncle.rlp and /dev/null differ
diff --git a/tests/fuzzers/rlp/corpus/r.bin b/tests/fuzzers/rlp/corpus/r.bin
deleted file mode 100644
index cb98a76a8a..0000000000
--- a/tests/fuzzers/rlp/corpus/r.bin
+++ /dev/null
@@ -1 +0,0 @@
-ˀ
\ No newline at end of file
diff --git a/tests/fuzzers/rlp/corpus/transaction.rlp b/tests/fuzzers/rlp/corpus/transaction.rlp
deleted file mode 100644
index 80eea1aec6..0000000000
--- a/tests/fuzzers/rlp/corpus/transaction.rlp
+++ /dev/null
@@ -1,2 +0,0 @@
-N
-aP?-'{ЋDYfj\E~읕F?1(ij6@vLڑ
\ No newline at end of file
diff --git a/tests/fuzzers/rlp/rlp_fuzzer.go b/tests/fuzzers/rlp/rlp_fuzzer.go
deleted file mode 100644
index 0da8ccdd77..0000000000
--- a/tests/fuzzers/rlp/rlp_fuzzer.go
+++ /dev/null
@@ -1,143 +0,0 @@
-// Copyright 2019 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package rlp
-
-import (
- "bytes"
- "fmt"
- "math/big"
-
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/rlp"
- "github.com/holiman/uint256"
-)
-
-func decodeEncode(input []byte, val interface{}, i int) {
- if err := rlp.DecodeBytes(input, val); err == nil {
- output, err := rlp.EncodeToBytes(val)
- if err != nil {
- panic(err)
- }
- if !bytes.Equal(input, output) {
- panic(fmt.Sprintf("case %d: encode-decode is not equal, \ninput : %x\noutput: %x", i, input, output))
- }
- }
-}
-
-func fuzz(input []byte) int {
- if len(input) == 0 {
- return 0
- }
- if len(input) > 500*1024 {
- return 0
- }
-
- var i int
- {
- rlp.Split(input)
- }
- {
- if elems, _, err := rlp.SplitList(input); err == nil {
- rlp.CountValues(elems)
- }
- }
-
- {
- rlp.NewStream(bytes.NewReader(input), 0).Decode(new(interface{}))
- }
-
- {
- decodeEncode(input, new(interface{}), i)
- i++
- }
- {
- var v struct {
- Int uint
- String string
- Bytes []byte
- }
- decodeEncode(input, &v, i)
- i++
- }
-
- {
- type Types struct {
- Bool bool
- Raw rlp.RawValue
- Slice []*Types
- Iface []interface{}
- }
- var v Types
- decodeEncode(input, &v, i)
- i++
- }
- {
- type AllTypes struct {
- Int uint
- String string
- Bytes []byte
- Bool bool
- Raw rlp.RawValue
- Slice []*AllTypes
- Array [3]*AllTypes
- Iface []interface{}
- }
- var v AllTypes
- decodeEncode(input, &v, i)
- i++
- }
- {
- decodeEncode(input, [10]byte{}, i)
- i++
- }
- {
- var v struct {
- Byte [10]byte
- Rool [10]bool
- }
- decodeEncode(input, &v, i)
- i++
- }
- {
- var h types.Header
- decodeEncode(input, &h, i)
- i++
- var b types.Block
- decodeEncode(input, &b, i)
- i++
- var t types.Transaction
- decodeEncode(input, &t, i)
- i++
- var txs types.Transactions
- decodeEncode(input, &txs, i)
- i++
- var rs types.Receipts
- decodeEncode(input, &rs, i)
- }
- {
- i++
- var v struct {
- AnIntPtr *big.Int
- AnInt big.Int
- AnU256Ptr *uint256.Int
- AnU256 uint256.Int
- NotAnU256 [4]uint64
- }
- decodeEncode(input, &v, i)
- }
- return 1
-}
diff --git a/tests/fuzzers/secp256k1/secp_test.go b/tests/fuzzers/secp256k1/secp_test.go
index fbdd8e6ac2..ca3039764b 100644
--- a/tests/fuzzers/secp256k1/secp_test.go
+++ b/tests/fuzzers/secp256k1/secp_test.go
@@ -35,7 +35,7 @@ func Fuzz(f *testing.F) {
})
}
-func fuzz(dataP1, dataP2 []byte) int {
+func fuzz(dataP1, dataP2 []byte) {
var (
curveA = secp256k1.S256()
curveB = btcec.S256()
@@ -50,5 +50,4 @@ func fuzz(dataP1, dataP2 []byte) int {
fmt.Printf("%s %s %s %s\n", x1, y1, x2, y2)
panic(fmt.Sprintf("Addition failed: geth: %s %s btcd: %s %s", resAX, resAY, resBX, resBY))
}
- return 0
}
diff --git a/tests/fuzzers/snap/fuzz_test.go b/tests/fuzzers/snap/fuzz_test.go
deleted file mode 100644
index 1c39f2bb50..0000000000
--- a/tests/fuzzers/snap/fuzz_test.go
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2023 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package snap
-
-import (
- "testing"
-
- "github.com/ethereum/go-ethereum/eth/protocols/snap"
-)
-
-func FuzzARange(f *testing.F) {
- f.Fuzz(func(t *testing.T, data []byte) {
- doFuzz(data, &snap.GetAccountRangePacket{}, snap.GetAccountRangeMsg)
- })
-}
-
-func FuzzSRange(f *testing.F) {
- f.Fuzz(func(t *testing.T, data []byte) {
- doFuzz(data, &snap.GetStorageRangesPacket{}, snap.GetStorageRangesMsg)
- })
-}
-
-func FuzzByteCodes(f *testing.F) {
- f.Fuzz(func(t *testing.T, data []byte) {
- doFuzz(data, &snap.GetByteCodesPacket{}, snap.GetByteCodesMsg)
- })
-}
-
-func FuzzTrieNodes(f *testing.F) {
- f.Fuzz(func(t *testing.T, data []byte) {
- doFuzz(data, &snap.GetTrieNodesPacket{}, snap.GetTrieNodesMsg)
- })
-}
diff --git a/tests/fuzzers/stacktrie/trie_fuzzer.go b/tests/fuzzers/stacktrie/trie_fuzzer.go
deleted file mode 100644
index 9e02176e3e..0000000000
--- a/tests/fuzzers/stacktrie/trie_fuzzer.go
+++ /dev/null
@@ -1,248 +0,0 @@
-// Copyright 2020 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package stacktrie
-
-import (
- "bytes"
- "encoding/binary"
- "errors"
- "fmt"
- "hash"
- "io"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/trie"
- "github.com/ethereum/go-ethereum/trie/trienode"
- "golang.org/x/crypto/sha3"
- "golang.org/x/exp/slices"
-)
-
-type fuzzer struct {
- input io.Reader
- exhausted bool
- debugging bool
-}
-
-func (f *fuzzer) read(size int) []byte {
- out := make([]byte, size)
- if _, err := f.input.Read(out); err != nil {
- f.exhausted = true
- }
- return out
-}
-
-func (f *fuzzer) readSlice(min, max int) []byte {
- var a uint16
- binary.Read(f.input, binary.LittleEndian, &a)
- size := min + int(a)%(max-min)
- out := make([]byte, size)
- if _, err := f.input.Read(out); err != nil {
- f.exhausted = true
- }
- return out
-}
-
-// spongeDb is a dummy db backend which accumulates writes in a sponge
-type spongeDb struct {
- sponge hash.Hash
- debug bool
-}
-
-func (s *spongeDb) Has(key []byte) (bool, error) { panic("implement me") }
-func (s *spongeDb) Get(key []byte) ([]byte, error) { return nil, errors.New("no such elem") }
-func (s *spongeDb) Delete(key []byte) error { panic("implement me") }
-func (s *spongeDb) NewBatch() ethdb.Batch { return &spongeBatch{s} }
-func (s *spongeDb) NewBatchWithSize(size int) ethdb.Batch { return &spongeBatch{s} }
-func (s *spongeDb) NewSnapshot() (ethdb.Snapshot, error) { panic("implement me") }
-func (s *spongeDb) Stat(property string) (string, error) { panic("implement me") }
-func (s *spongeDb) Compact(start []byte, limit []byte) error { panic("implement me") }
-func (s *spongeDb) Close() error { return nil }
-
-func (s *spongeDb) Put(key []byte, value []byte) error {
- if s.debug {
- fmt.Printf("db.Put %x : %x\n", key, value)
- }
- s.sponge.Write(key)
- s.sponge.Write(value)
- return nil
-}
-func (s *spongeDb) NewIterator(prefix []byte, start []byte) ethdb.Iterator { panic("implement me") }
-
-// spongeBatch is a dummy batch which immediately writes to the underlying spongedb
-type spongeBatch struct {
- db *spongeDb
-}
-
-func (b *spongeBatch) Put(key, value []byte) error {
- b.db.Put(key, value)
- return nil
-}
-func (b *spongeBatch) Delete(key []byte) error { panic("implement me") }
-func (b *spongeBatch) ValueSize() int { return 100 }
-func (b *spongeBatch) Write() error { return nil }
-func (b *spongeBatch) Reset() {}
-func (b *spongeBatch) Replay(w ethdb.KeyValueWriter) error { return nil }
-
-type kv struct {
- k, v []byte
-}
-
-// Fuzz is the fuzzing entry-point.
-// The function must return
-//
-// - 1 if the fuzzer should increase priority of the
-// given input during subsequent fuzzing (for example, the input is lexically
-// correct and was parsed successfully);
-// - -1 if the input must not be added to corpus even if gives new coverage; and
-// - 0 otherwise
-//
-// other values are reserved for future use.
-func fuzz(data []byte) int {
- f := fuzzer{
- input: bytes.NewReader(data),
- exhausted: false,
- }
- return f.fuzz()
-}
-
-func Debug(data []byte) int {
- f := fuzzer{
- input: bytes.NewReader(data),
- exhausted: false,
- debugging: true,
- }
- return f.fuzz()
-}
-
-func (f *fuzzer) fuzz() int {
- // This spongeDb is used to check the sequence of disk-db-writes
- var (
- spongeA = &spongeDb{sponge: sha3.NewLegacyKeccak256()}
- dbA = trie.NewDatabase(rawdb.NewDatabase(spongeA), nil)
- trieA = trie.NewEmpty(dbA)
- spongeB = &spongeDb{sponge: sha3.NewLegacyKeccak256()}
- dbB = trie.NewDatabase(rawdb.NewDatabase(spongeB), nil)
-
- options = trie.NewStackTrieOptions().WithWriter(func(path []byte, hash common.Hash, blob []byte) {
- rawdb.WriteTrieNode(spongeB, common.Hash{}, path, hash, blob, dbB.Scheme())
- })
- trieB = trie.NewStackTrie(options)
- vals []kv
- useful bool
- maxElements = 10000
- // operate on unique keys only
- keys = make(map[string]struct{})
- )
- // Fill the trie with elements
- for i := 0; !f.exhausted && i < maxElements; i++ {
- k := f.read(32)
- v := f.readSlice(1, 500)
- if f.exhausted {
- // If it was exhausted while reading, the value may be all zeroes,
- // thus 'deletion' which is not supported on stacktrie
- break
- }
- if _, present := keys[string(k)]; present {
- // This key is a duplicate, ignore it
- continue
- }
- keys[string(k)] = struct{}{}
- vals = append(vals, kv{k: k, v: v})
- trieA.MustUpdate(k, v)
- useful = true
- }
- if !useful {
- return 0
- }
- // Flush trie -> database
- rootA, nodes, err := trieA.Commit(false)
- if err != nil {
- panic(err)
- }
- if nodes != nil {
- dbA.Update(rootA, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil)
- }
- // Flush memdb -> disk (sponge)
- dbA.Commit(rootA, false)
-
- // Stacktrie requires sorted insertion
- slices.SortFunc(vals, func(a, b kv) int {
- return bytes.Compare(a.k, b.k)
- })
- for _, kv := range vals {
- if f.debugging {
- fmt.Printf("{\"%#x\" , \"%#x\"} // stacktrie.Update\n", kv.k, kv.v)
- }
- trieB.MustUpdate(kv.k, kv.v)
- }
- rootB := trieB.Hash()
- trieB.Commit()
- if rootA != rootB {
- panic(fmt.Sprintf("roots differ: (trie) %x != %x (stacktrie)", rootA, rootB))
- }
- sumA := spongeA.sponge.Sum(nil)
- sumB := spongeB.sponge.Sum(nil)
- if !bytes.Equal(sumA, sumB) {
- panic(fmt.Sprintf("sequence differ: (trie) %x != %x (stacktrie)", sumA, sumB))
- }
-
- // Ensure all the nodes are persisted correctly
- var (
- nodeset = make(map[string][]byte) // path -> blob
- optionsC = trie.NewStackTrieOptions().WithWriter(func(path []byte, hash common.Hash, blob []byte) {
- if crypto.Keccak256Hash(blob) != hash {
- panic("invalid node blob")
- }
- nodeset[string(path)] = common.CopyBytes(blob)
- })
- trieC = trie.NewStackTrie(optionsC)
- checked int
- )
- for _, kv := range vals {
- trieC.MustUpdate(kv.k, kv.v)
- }
- rootC := trieC.Commit()
- if rootA != rootC {
- panic(fmt.Sprintf("roots differ: (trie) %x != %x (stacktrie)", rootA, rootC))
- }
- trieA, _ = trie.New(trie.TrieID(rootA), dbA)
- iterA := trieA.MustNodeIterator(nil)
- for iterA.Next(true) {
- if iterA.Hash() == (common.Hash{}) {
- if _, present := nodeset[string(iterA.Path())]; present {
- panic("unexpected tiny node")
- }
- continue
- }
- nodeBlob, present := nodeset[string(iterA.Path())]
- if !present {
- panic("missing node")
- }
- if !bytes.Equal(nodeBlob, iterA.NodeBlob()) {
- panic("node blob is not matched")
- }
- checked += 1
- }
- if checked != len(nodeset) {
- panic("node number is not matched")
- }
- return 1
-}
diff --git a/tests/fuzzers/stacktrie/trie_test.go b/tests/fuzzers/stacktrie/trie_test.go
deleted file mode 100644
index f6f755f76a..0000000000
--- a/tests/fuzzers/stacktrie/trie_test.go
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2023 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package stacktrie
-
-import "testing"
-
-func Fuzz(f *testing.F) {
- f.Fuzz(func(t *testing.T, data []byte) {
- fuzz(data)
- })
-}
diff --git a/tests/fuzzers/trie/trie-fuzzer.go b/tests/fuzzers/trie/trie-fuzzer.go
deleted file mode 100644
index a505fa78a0..0000000000
--- a/tests/fuzzers/trie/trie-fuzzer.go
+++ /dev/null
@@ -1,201 +0,0 @@
-// Copyright 2019 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package trie
-
-import (
- "bytes"
- "encoding/binary"
- "errors"
- "fmt"
-
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/trie"
- "github.com/ethereum/go-ethereum/trie/trienode"
-)
-
-// randTest performs random trie operations.
-// Instances of this test are created by Generate.
-type randTest []randTestStep
-
-type randTestStep struct {
- op int
- key []byte // for opUpdate, opDelete, opGet
- value []byte // for opUpdate
- err error // for debugging
-}
-
-type proofDb struct{}
-
-func (proofDb) Put(key []byte, value []byte) error {
- return nil
-}
-
-func (proofDb) Delete(key []byte) error {
- return nil
-}
-
-const (
- opUpdate = iota
- opDelete
- opGet
- opHash
- opCommit
- opItercheckhash
- opProve
- opMax // boundary value, not an actual op
-)
-
-type dataSource struct {
- input []byte
- reader *bytes.Reader
-}
-
-func newDataSource(input []byte) *dataSource {
- return &dataSource{
- input, bytes.NewReader(input),
- }
-}
-func (ds *dataSource) readByte() byte {
- if b, err := ds.reader.ReadByte(); err != nil {
- return 0
- } else {
- return b
- }
-}
-func (ds *dataSource) Read(buf []byte) (int, error) {
- return ds.reader.Read(buf)
-}
-func (ds *dataSource) Ended() bool {
- return ds.reader.Len() == 0
-}
-
-func Generate(input []byte) randTest {
- var allKeys [][]byte
- r := newDataSource(input)
- genKey := func() []byte {
- if len(allKeys) < 2 || r.readByte() < 0x0f {
- // new key
- key := make([]byte, r.readByte()%50)
- r.Read(key)
- allKeys = append(allKeys, key)
- return key
- }
- // use existing key
- return allKeys[int(r.readByte())%len(allKeys)]
- }
-
- var steps randTest
-
- for i := 0; !r.Ended(); i++ {
- step := randTestStep{op: int(r.readByte()) % opMax}
- switch step.op {
- case opUpdate:
- step.key = genKey()
- step.value = make([]byte, 8)
- binary.BigEndian.PutUint64(step.value, uint64(i))
- case opGet, opDelete, opProve:
- step.key = genKey()
- }
- steps = append(steps, step)
- if len(steps) > 500 {
- break
- }
- }
-
- return steps
-}
-
-// Fuzz is the fuzzing entry-point.
-// The function must return
-//
-// - 1 if the fuzzer should increase priority of the
-// given input during subsequent fuzzing (for example, the input is lexically
-// correct and was parsed successfully);
-// - -1 if the input must not be added to corpus even if gives new coverage; and
-// - 0 otherwise
-//
-// other values are reserved for future use.
-func fuzz(input []byte) int {
- program := Generate(input)
- if len(program) == 0 {
- return 0
- }
- if err := runRandTest(program); err != nil {
- panic(err)
- }
- return 1
-}
-
-func runRandTest(rt randTest) error {
- var (
- triedb = trie.NewDatabase(rawdb.NewMemoryDatabase(), nil)
- tr = trie.NewEmpty(triedb)
- origin = types.EmptyRootHash
- values = make(map[string]string) // tracks content of the trie
- )
- for i, step := range rt {
- switch step.op {
- case opUpdate:
- tr.MustUpdate(step.key, step.value)
- values[string(step.key)] = string(step.value)
- case opDelete:
- tr.MustDelete(step.key)
- delete(values, string(step.key))
- case opGet:
- v := tr.MustGet(step.key)
- want := values[string(step.key)]
- if string(v) != want {
- rt[i].err = fmt.Errorf("mismatch for key %#x, got %#x want %#x", step.key, v, want)
- }
- case opHash:
- tr.Hash()
- case opCommit:
- hash, nodes, err := tr.Commit(false)
- if err != nil {
- return err
- }
- if nodes != nil {
- if err := triedb.Update(hash, origin, 0, trienode.NewWithNodeSet(nodes), nil); err != nil {
- return err
- }
- }
- newtr, err := trie.New(trie.TrieID(hash), triedb)
- if err != nil {
- return err
- }
- tr = newtr
- origin = hash
- case opItercheckhash:
- checktr := trie.NewEmpty(triedb)
- it := trie.NewIterator(tr.MustNodeIterator(nil))
- for it.Next() {
- checktr.MustUpdate(it.Key, it.Value)
- }
- if tr.Hash() != checktr.Hash() {
- return errors.New("hash mismatch in opItercheckhash")
- }
- case opProve:
- rt[i].err = tr.Prove(step.key, proofDb{})
- }
- // Abort the test on error.
- if rt[i].err != nil {
- return rt[i].err
- }
- }
- return nil
-}
diff --git a/tests/fuzzers/trie/trie_test.go b/tests/fuzzers/trie/trie_test.go
deleted file mode 100644
index a7d28a806e..0000000000
--- a/tests/fuzzers/trie/trie_test.go
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2023 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package trie
-
-import "testing"
-
-func Fuzz(f *testing.F) {
- f.Fuzz(func(t *testing.T, data []byte) {
- fuzz(data)
- })
-}
diff --git a/trie/stacktrie_fuzzer_test.go b/trie/stacktrie_fuzzer_test.go
new file mode 100644
index 0000000000..1b3f9dbe9c
--- /dev/null
+++ b/trie/stacktrie_fuzzer_test.go
@@ -0,0 +1,155 @@
+// Copyright 2020 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package trie
+
+import (
+ "bytes"
+ "encoding/binary"
+ "fmt"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/rawdb"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/trie/trienode"
+ "golang.org/x/crypto/sha3"
+ "golang.org/x/exp/slices"
+)
+
+func FuzzStackTrie(f *testing.F) {
+ f.Fuzz(func(t *testing.T, data []byte) {
+ fuzz(data, false)
+ })
+}
+
+func fuzz(data []byte, debugging bool) {
+ // This spongeDb is used to check the sequence of disk-db-writes
+ var (
+ input = bytes.NewReader(data)
+ spongeA = &spongeDb{sponge: sha3.NewLegacyKeccak256()}
+ dbA = NewDatabase(rawdb.NewDatabase(spongeA), nil)
+ trieA = NewEmpty(dbA)
+ spongeB = &spongeDb{sponge: sha3.NewLegacyKeccak256()}
+ dbB = NewDatabase(rawdb.NewDatabase(spongeB), nil)
+
+ options = NewStackTrieOptions().WithWriter(func(path []byte, hash common.Hash, blob []byte) {
+ rawdb.WriteTrieNode(spongeB, common.Hash{}, path, hash, blob, dbB.Scheme())
+ })
+ trieB = NewStackTrie(options)
+ vals []*kv
+ maxElements = 10000
+ // operate on unique keys only
+ keys = make(map[string]struct{})
+ )
+ // Fill the trie with elements
+ for i := 0; input.Len() > 0 && i < maxElements; i++ {
+ k := make([]byte, 32)
+ input.Read(k)
+ var a uint16
+ binary.Read(input, binary.LittleEndian, &a)
+ a = 1 + a%100
+ v := make([]byte, a)
+ input.Read(v)
+ if input.Len() == 0 {
+ // If it was exhausted while reading, the value may be all zeroes,
+ // thus 'deletion' which is not supported on stacktrie
+ break
+ }
+ if _, present := keys[string(k)]; present {
+ // This key is a duplicate, ignore it
+ continue
+ }
+ keys[string(k)] = struct{}{}
+ vals = append(vals, &kv{k: k, v: v})
+ trieA.MustUpdate(k, v)
+ }
+ if len(vals) == 0 {
+ return
+ }
+ // Flush trie -> database
+ rootA, nodes, err := trieA.Commit(false)
+ if err != nil {
+ panic(err)
+ }
+ if nodes != nil {
+ dbA.Update(rootA, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil)
+ }
+ // Flush memdb -> disk (sponge)
+ dbA.Commit(rootA, false)
+
+ // Stacktrie requires sorted insertion
+ slices.SortFunc(vals, (*kv).cmp)
+
+ for _, kv := range vals {
+ if debugging {
+ fmt.Printf("{\"%#x\" , \"%#x\"} // stacktrie.Update\n", kv.k, kv.v)
+ }
+ trieB.MustUpdate(kv.k, kv.v)
+ }
+ rootB := trieB.Hash()
+ trieB.Commit()
+ if rootA != rootB {
+ panic(fmt.Sprintf("roots differ: (trie) %x != %x (stacktrie)", rootA, rootB))
+ }
+ sumA := spongeA.sponge.Sum(nil)
+ sumB := spongeB.sponge.Sum(nil)
+ if !bytes.Equal(sumA, sumB) {
+ panic(fmt.Sprintf("sequence differ: (trie) %x != %x (stacktrie)", sumA, sumB))
+ }
+
+ // Ensure all the nodes are persisted correctly
+ var (
+ nodeset = make(map[string][]byte) // path -> blob
+ optionsC = NewStackTrieOptions().WithWriter(func(path []byte, hash common.Hash, blob []byte) {
+ if crypto.Keccak256Hash(blob) != hash {
+ panic("invalid node blob")
+ }
+ nodeset[string(path)] = common.CopyBytes(blob)
+ })
+ trieC = NewStackTrie(optionsC)
+ checked int
+ )
+ for _, kv := range vals {
+ trieC.MustUpdate(kv.k, kv.v)
+ }
+ rootC := trieC.Commit()
+ if rootA != rootC {
+ panic(fmt.Sprintf("roots differ: (trie) %x != %x (stacktrie)", rootA, rootC))
+ }
+ trieA, _ = New(TrieID(rootA), dbA)
+ iterA := trieA.MustNodeIterator(nil)
+ for iterA.Next(true) {
+ if iterA.Hash() == (common.Hash{}) {
+ if _, present := nodeset[string(iterA.Path())]; present {
+ panic("unexpected tiny node")
+ }
+ continue
+ }
+ nodeBlob, present := nodeset[string(iterA.Path())]
+ if !present {
+ panic("missing node")
+ }
+ if !bytes.Equal(nodeBlob, iterA.NodeBlob()) {
+ panic("node blob is not matched")
+ }
+ checked += 1
+ }
+ if checked != len(nodeset) {
+ panic("node number is not matched")
+ }
+}
diff --git a/trie/trie_test.go b/trie/trie_test.go
index 4315753548..6af0f67b9f 100644
--- a/trie/trie_test.go
+++ b/trie/trie_test.go
@@ -22,6 +22,7 @@ import (
"errors"
"fmt"
"hash"
+ "io"
"math/big"
"math/rand"
"reflect"
@@ -362,7 +363,9 @@ func TestRandomCases(t *testing.T) {
{op: 1, key: common.Hex2Bytes("980c393656413a15c8da01978ed9f89feb80b502f58f2d640e3a2f5f7a99a7018f1b573befd92053ac6f78fca4a87268"), value: common.Hex2Bytes("")}, // step 24
{op: 1, key: common.Hex2Bytes("fd"), value: common.Hex2Bytes("")}, // step 25
}
- runRandTest(rt)
+ if err := runRandTest(rt); err != nil {
+ t.Fatal(err)
+ }
}
// randTest performs random trie operations.
@@ -389,33 +392,45 @@ const (
)
func (randTest) Generate(r *rand.Rand, size int) reflect.Value {
+ var finishedFn = func() bool {
+ size--
+ return size > 0
+ }
+ return reflect.ValueOf(generateSteps(finishedFn, r))
+}
+
+func generateSteps(finished func() bool, r io.Reader) randTest {
var allKeys [][]byte
+ var one = []byte{0}
genKey := func() []byte {
- if len(allKeys) < 2 || r.Intn(100) < 10 {
+ r.Read(one)
+ if len(allKeys) < 2 || one[0]%100 > 90 {
// new key
- key := make([]byte, r.Intn(50))
+ size := one[0] % 50
+ key := make([]byte, size)
r.Read(key)
allKeys = append(allKeys, key)
return key
}
// use existing key
- return allKeys[r.Intn(len(allKeys))]
+ idx := int(one[0]) % len(allKeys)
+ return allKeys[idx]
}
-
var steps randTest
- for i := 0; i < size; i++ {
- step := randTestStep{op: r.Intn(opMax)}
+ for !finished() {
+ r.Read(one)
+ step := randTestStep{op: int(one[0]) % opMax}
switch step.op {
case opUpdate:
step.key = genKey()
step.value = make([]byte, 8)
- binary.BigEndian.PutUint64(step.value, uint64(i))
+ binary.BigEndian.PutUint64(step.value, uint64(len(steps)))
case opGet, opDelete, opProve:
step.key = genKey()
}
steps = append(steps, step)
}
- return reflect.ValueOf(steps)
+ return steps
}
func verifyAccessList(old *Trie, new *Trie, set *trienode.NodeSet) error {
@@ -460,7 +475,12 @@ func verifyAccessList(old *Trie, new *Trie, set *trienode.NodeSet) error {
return nil
}
-func runRandTest(rt randTest) bool {
+// runRandTestBool coerces error to boolean, for use in quick.Check
+func runRandTestBool(rt randTest) bool {
+ return runRandTest(rt) == nil
+}
+
+func runRandTest(rt randTest) error {
var scheme = rawdb.HashScheme
if rand.Intn(2) == 0 {
scheme = rawdb.PathScheme
@@ -513,12 +533,12 @@ func runRandTest(rt randTest) bool {
newtr, err := New(TrieID(root), triedb)
if err != nil {
rt[i].err = err
- return false
+ return err
}
if nodes != nil {
if err := verifyAccessList(origTrie, newtr, nodes); err != nil {
rt[i].err = err
- return false
+ return err
}
}
tr = newtr
@@ -587,14 +607,14 @@ func runRandTest(rt randTest) bool {
}
// Abort the test on error.
if rt[i].err != nil {
- return false
+ return rt[i].err
}
}
- return true
+ return nil
}
func TestRandom(t *testing.T) {
- if err := quick.Check(runRandTest, nil); err != nil {
+ if err := quick.Check(runRandTestBool, nil); err != nil {
if cerr, ok := err.(*quick.CheckError); ok {
t.Fatalf("random test iteration %d failed: %s", cerr.Count, spew.Sdump(cerr.In))
}
@@ -1185,3 +1205,17 @@ func TestDecodeNode(t *testing.T) {
decodeNode(hash, elems)
}
}
+
+func FuzzTrie(f *testing.F) {
+ f.Fuzz(func(t *testing.T, data []byte) {
+ var steps = 500
+ var input = bytes.NewReader(data)
+ var finishedFn = func() bool {
+ steps--
+ return steps < 0 || input.Len() == 0
+ }
+ if err := runRandTest(generateSteps(finishedFn, input)); err != nil {
+ t.Fatal(err)
+ }
+ })
+}