forked from cerc-io/plugeth
tests/fuzzers: move fuzzers into native packages (#28467)
This PR moves our fuzzers from tests/fuzzers into whatever their respective 'native' package is. The historical reason why they were placed in an external location, is that when they were based on go-fuzz, they could not be "hidden" via the _test.go prefix. So in order to shove them away from the go-ethereum "production code", they were put aside. But now we've rewritten them to be based on golang testing, and thus can be brought back. I've left (in tests/) the ones that are not production (bls128381), require non-standard imports (secp requires btcec, bn256 requires gnark/google/cloudflare deps). This PR also adds a fuzzer for precompiled contracts, because why not. This PR utilizes a newly rewritten replacement for go-118-fuzz-build, namely gofuzz-shim, which utilises the inputs from the fuzzing engine better.
This commit is contained in:
@@ -1,181 +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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package abi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"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)
|
||||
}
|
||||
|
||||
func Fuzz(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",
|
||||
"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",
|
||||
"uint104", "int104", "uint112", "int112", "uint120", "int120", "uint128", "int128", "uint136", "int136",
|
||||
"uint144", "int144", "uint152", "int152", "uint160", "int160", "uint168", "int168", "uint176", "int176",
|
||||
"uint184", "int184", "uint192", "int192", "uint200", "int200", "uint208", "int208", "uint216", "int216",
|
||||
"uint224", "int224", "uint232", "int232", "uint240", "int240", "uint248", "int248", "uint256", "int256",
|
||||
"bytes1", "bytes2", "bytes3", "bytes4", "bytes5", "bytes6", "bytes7", "bytes8", "bytes9", "bytes10", "bytes11",
|
||||
"bytes12", "bytes13", "bytes14", "bytes15", "bytes16", "bytes17", "bytes18", "bytes19", "bytes20", "bytes21",
|
||||
"bytes22", "bytes23", "bytes24", "bytes25", "bytes26", "bytes27", "bytes28", "bytes29", "bytes30", "bytes31",
|
||||
"bytes32", "bytes"}
|
||||
)
|
||||
|
||||
func unpackPack(abi 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 {
|
||||
// We have some false positives as we can unpack these type successfully, but not pack them
|
||||
if err.Error() == "abi: cannot use []uint8 as type [0]int8 as argument" ||
|
||||
err.Error() == "abi: cannot use uint8 as type int8 as argument" {
|
||||
return out, false
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
return out, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func packUnpack(abi 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)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
out := outptr.Elem().Interface()
|
||||
if !reflect.DeepEqual(input, out) {
|
||||
panic(fmt.Sprintf("unpackPack is not equal, \ninput : %x\noutput: %x", input, out))
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type args struct {
|
||||
name string
|
||||
typ string
|
||||
}
|
||||
|
||||
func createABI(name string, stateMutability, payable *string, inputs []args) (abi.ABI, error) {
|
||||
sig := fmt.Sprintf(`[{ "type" : "function", "name" : "%v" `, name)
|
||||
if stateMutability != nil {
|
||||
sig += fmt.Sprintf(`, "stateMutability": "%v" `, *stateMutability)
|
||||
}
|
||||
if payable != nil {
|
||||
sig += fmt.Sprintf(`, "payable": %v `, *payable)
|
||||
}
|
||||
if len(inputs) > 0 {
|
||||
sig += `, "inputs" : [ {`
|
||||
for i, inp := range inputs {
|
||||
sig += fmt.Sprintf(`"name" : "%v", "type" : "%v" `, inp.name, inp.typ)
|
||||
if i+1 < len(inputs) {
|
||||
sig += ","
|
||||
}
|
||||
}
|
||||
sig += "} ]"
|
||||
sig += `, "outputs" : [ {`
|
||||
for i, inp := range inputs {
|
||||
sig += fmt.Sprintf(`"name" : "%v", "type" : "%v" `, inp.name, inp.typ)
|
||||
if i+1 < len(inputs) {
|
||||
sig += ","
|
||||
}
|
||||
}
|
||||
sig += "} ]"
|
||||
}
|
||||
sig += `}]`
|
||||
|
||||
return abi.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,
|
||||
})
|
||||
}
|
||||
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
|
||||
}
|
||||
if good {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func getUInt(fuzzer *fuzz.Fuzzer) int {
|
||||
var i int
|
||||
fuzzer.Fuzz(&i)
|
||||
if i < 0 {
|
||||
i = -i
|
||||
if i < 0 {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
return i
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package keystore
|
||||
|
||||
import "testing"
|
||||
|
||||
func Fuzz(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
fuzz(data)
|
||||
})
|
||||
}
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
Ë€€€À€ÀÃÀÀÀÀ
|
||||
@@ -1,2 +0,0 @@
|
||||
øNƒ“à€€€‚
|
||||
• �aùËåÀP?-'´{ÏЋD�Y¯�³fÆj\ÿE÷ ~ì�•ÒçF?1(íij6�@Év±LÀÝÚ‘‘
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package rlp
|
||||
|
||||
import "testing"
|
||||
|
||||
func Fuzz(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
fuzz(data)
|
||||
})
|
||||
}
|
||||
@@ -1,31 +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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/vm/runtime"
|
||||
)
|
||||
|
||||
func Fuzz(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, code, input []byte) {
|
||||
runtime.Execute(code, input, &runtime.Config{
|
||||
GasLimit: 12000000,
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -1,143 +0,0 @@
|
||||
// Copyright 2021 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package snap
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"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"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
fuzz "github.com/google/gofuzz"
|
||||
)
|
||||
|
||||
var trieRoot common.Hash
|
||||
|
||||
func getChain() *core.BlockChain {
|
||||
ga := make(core.GenesisAlloc, 1000)
|
||||
var a = make([]byte, 20)
|
||||
var mkStorage = func(k, v int) (common.Hash, common.Hash) {
|
||||
var kB = make([]byte, 32)
|
||||
var vB = make([]byte, 32)
|
||||
binary.LittleEndian.PutUint64(kB, uint64(k))
|
||||
binary.LittleEndian.PutUint64(vB, uint64(v))
|
||||
return common.BytesToHash(kB), common.BytesToHash(vB)
|
||||
}
|
||||
storage := make(map[common.Hash]common.Hash)
|
||||
for i := 0; i < 10; i++ {
|
||||
k, v := mkStorage(i, i)
|
||||
storage[k] = v
|
||||
}
|
||||
for i := 0; i < 1000; i++ {
|
||||
binary.LittleEndian.PutUint64(a, uint64(i+0xff))
|
||||
acc := core.GenesisAccount{Balance: big.NewInt(int64(i))}
|
||||
if i%2 == 1 {
|
||||
acc.Storage = storage
|
||||
}
|
||||
ga[common.BytesToAddress(a)] = acc
|
||||
}
|
||||
gspec := &core.Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: ga,
|
||||
}
|
||||
_, blocks, _ := core.GenerateChainWithGenesis(gspec, ethash.NewFaker(), 2, func(i int, gen *core.BlockGen) {})
|
||||
cacheConf := &core.CacheConfig{
|
||||
TrieCleanLimit: 0,
|
||||
TrieDirtyLimit: 0,
|
||||
TrieTimeLimit: 5 * time.Minute,
|
||||
TrieCleanNoPrefetch: true,
|
||||
SnapshotLimit: 100,
|
||||
SnapshotWait: true,
|
||||
}
|
||||
trieRoot = blocks[len(blocks)-1].Root()
|
||||
bc, _ := core.NewBlockChain(rawdb.NewMemoryDatabase(), cacheConf, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
if _, err := bc.InsertChain(blocks); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bc
|
||||
}
|
||||
|
||||
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 }
|
||||
|
||||
type dummyRW struct {
|
||||
code uint64
|
||||
data []byte
|
||||
writeCount int
|
||||
}
|
||||
|
||||
func (d *dummyRW) ReadMsg() (p2p.Msg, error) {
|
||||
return p2p.Msg{
|
||||
Code: d.code,
|
||||
Payload: bytes.NewReader(d.data),
|
||||
ReceivedAt: time.Now(),
|
||||
Size: uint32(len(d.data)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package stacktrie
|
||||
|
||||
import "testing"
|
||||
|
||||
func Fuzz(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
fuzz(data)
|
||||
})
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package trie
|
||||
|
||||
import "testing"
|
||||
|
||||
func Fuzz(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
fuzz(data)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user