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:
parent
24d46224c1
commit
2391fbc676
@ -22,19 +22,19 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
|
||||||
fuzz "github.com/google/gofuzz"
|
fuzz "github.com/google/gofuzz"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestReplicate can be used to replicate crashers from the fuzzing tests.
|
// TestReplicate can be used to replicate crashers from the fuzzing tests.
|
||||||
// Just replace testString with the data in .quoted
|
// Just replace testString with the data in .quoted
|
||||||
func TestReplicate(t *testing.T) {
|
func TestReplicate(t *testing.T) {
|
||||||
testString := "\x20\x20\x20\x20\x20\x20\x20\x20\x80\x00\x00\x00\x20\x20\x20\x20\x00"
|
//t.Skip("Test only useful for reproducing issues")
|
||||||
data := []byte(testString)
|
fuzzAbi([]byte("\x20\x20\x20\x20\x20\x20\x20\x20\x80\x00\x00\x00\x20\x20\x20\x20\x00"))
|
||||||
fuzzAbi(data)
|
//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) {
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
fuzzAbi(data)
|
fuzzAbi(data)
|
||||||
})
|
})
|
||||||
@ -42,10 +42,8 @@ func Fuzz(f *testing.F) {
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
names = []string{"_name", "name", "NAME", "name_", "__", "_name_", "n"}
|
names = []string{"_name", "name", "NAME", "name_", "__", "_name_", "n"}
|
||||||
stateMut = []string{"", "pure", "view", "payable"}
|
stateMut = []string{"pure", "view", "payable"}
|
||||||
stateMutabilites = []*string{&stateMut[0], &stateMut[1], &stateMut[2], &stateMut[3]}
|
pays = []string{"true", "false"}
|
||||||
pays = []string{"", "true", "false"}
|
|
||||||
payables = []*string{&pays[0], &pays[1]}
|
|
||||||
vNames = []string{"a", "b", "c", "d", "e", "f", "g"}
|
vNames = []string{"a", "b", "c", "d", "e", "f", "g"}
|
||||||
varNames = append(vNames, names...)
|
varNames = append(vNames, names...)
|
||||||
varTypes = []string{"bool", "address", "bytes", "string",
|
varTypes = []string{"bool", "address", "bytes", "string",
|
||||||
@ -62,7 +60,7 @@ var (
|
|||||||
"bytes32", "bytes"}
|
"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 {
|
if out, err := abi.Unpack(method, input); err == nil {
|
||||||
_, err := abi.Pack(method, out...)
|
_, err := abi.Pack(method, out...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -78,7 +76,7 @@ func unpackPack(abi abi.ABI, method string, input []byte) ([]interface{}, bool)
|
|||||||
return nil, false
|
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 {
|
if packed, err := abi.Pack(method, input); err == nil {
|
||||||
outptr := reflect.New(reflect.TypeOf(input))
|
outptr := reflect.New(reflect.TypeOf(input))
|
||||||
err := abi.UnpackIntoInterface(outptr.Interface(), method, packed)
|
err := abi.UnpackIntoInterface(outptr.Interface(), method, packed)
|
||||||
@ -94,12 +92,12 @@ func packUnpack(abi abi.ABI, method string, input *[]interface{}) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
type args struct {
|
type arg struct {
|
||||||
name string
|
name string
|
||||||
typ 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)
|
sig := fmt.Sprintf(`[{ "type" : "function", "name" : "%v" `, name)
|
||||||
if stateMutability != nil {
|
if stateMutability != nil {
|
||||||
sig += fmt.Sprintf(`, "stateMutability": "%v" `, *stateMutability)
|
sig += fmt.Sprintf(`, "stateMutability": "%v" `, *stateMutability)
|
||||||
@ -126,56 +124,55 @@ func createABI(name string, stateMutability, payable *string, inputs []args) (ab
|
|||||||
sig += "} ]"
|
sig += "} ]"
|
||||||
}
|
}
|
||||||
sig += `}]`
|
sig += `}]`
|
||||||
|
//fmt.Printf("sig: %s\n", sig)
|
||||||
return abi.JSON(strings.NewReader(sig))
|
return JSON(strings.NewReader(sig))
|
||||||
}
|
}
|
||||||
|
|
||||||
func fuzzAbi(input []byte) int {
|
func fuzzAbi(input []byte) {
|
||||||
good := false
|
var (
|
||||||
fuzzer := fuzz.NewFromGoFuzz(input)
|
fuzzer = fuzz.NewFromGoFuzz(input)
|
||||||
|
name = oneOf(fuzzer, names)
|
||||||
name := names[getUInt(fuzzer)%len(names)]
|
stateM = oneOfOrNil(fuzzer, stateMut)
|
||||||
stateM := stateMutabilites[getUInt(fuzzer)%len(stateMutabilites)]
|
payable = oneOfOrNil(fuzzer, pays)
|
||||||
payable := payables[getUInt(fuzzer)%len(payables)]
|
arguments []arg
|
||||||
maxLen := 5
|
)
|
||||||
for k := 1; k < maxLen; k++ {
|
for i := 0; i < upTo(fuzzer, 10); i++ {
|
||||||
var arg []args
|
argName := oneOf(fuzzer, varNames)
|
||||||
for i := k; i > 0; i-- {
|
argTyp := oneOf(fuzzer, varTypes)
|
||||||
argName := varNames[i]
|
switch upTo(fuzzer, 10) {
|
||||||
argTyp := varTypes[getUInt(fuzzer)%len(varTypes)]
|
case 0: // 10% chance to make it a slice
|
||||||
if getUInt(fuzzer)%10 == 0 {
|
|
||||||
argTyp += "[]"
|
argTyp += "[]"
|
||||||
} else if getUInt(fuzzer)%10 == 0 {
|
case 1: // 10% chance to make it an array
|
||||||
arrayArgs := getUInt(fuzzer)%30 + 1
|
argTyp += fmt.Sprintf("[%d]", 1+upTo(fuzzer, 30))
|
||||||
argTyp += fmt.Sprintf("[%d]", arrayArgs)
|
default:
|
||||||
}
|
}
|
||||||
arg = append(arg, args{
|
arguments = append(arguments, arg{name: argName, typ: argTyp})
|
||||||
name: argName,
|
|
||||||
typ: argTyp,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
abi, err := createABI(name, stateM, payable, arg)
|
abi, err := createABI(name, stateM, payable, arguments)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
//fmt.Printf("err: %v\n", err)
|
||||||
|
panic(err)
|
||||||
}
|
}
|
||||||
structs, b := unpackPack(abi, name, input)
|
structs, _ := unpackPack(abi, name, input)
|
||||||
c := packUnpack(abi, name, &structs)
|
_ = packUnpack(abi, name, &structs)
|
||||||
good = good || b || c
|
|
||||||
}
|
|
||||||
if good {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getUInt(fuzzer *fuzz.Fuzzer) int {
|
func upTo(fuzzer *fuzz.Fuzzer, max int) int {
|
||||||
var i int
|
var i int
|
||||||
fuzzer.Fuzz(&i)
|
fuzzer.Fuzz(&i)
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
i = -i
|
return (-1 - i) % max
|
||||||
if i < 0 {
|
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
}
|
return i % max
|
||||||
return i
|
}
|
||||||
|
|
||||||
|
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
|
||||||
}
|
}
|
@ -16,10 +16,19 @@
|
|||||||
|
|
||||||
package keystore
|
package keystore
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
func Fuzz(f *testing.F) {
|
func FuzzPassword(f *testing.F) {
|
||||||
f.Fuzz(func(t *testing.T, data []byte) {
|
f.Fuzz(func(t *testing.T, password string) {
|
||||||
fuzz(data)
|
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)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
@ -18,6 +18,7 @@ package bitutil
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -48,17 +49,21 @@ func TestEncodingCycle(t *testing.T) {
|
|||||||
"0xdf7070533534333636313639343638373532313536346c1bc333393438373130707063363430353639343638373532313536346c1bc333393438336336346c65fe",
|
"0xdf7070533534333636313639343638373532313536346c1bc333393438373130707063363430353639343638373532313536346c1bc333393438336336346c65fe",
|
||||||
}
|
}
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
data := hexutil.MustDecode(tt)
|
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))
|
proc, err := bitsetDecodeBytes(bitsetEncodeBytes(data), len(data))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("test %d: failed to decompress compressed data: %v", i, err)
|
return fmt.Errorf("failed to decompress compressed data: %v", err)
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
if !bytes.Equal(data, proc) {
|
if !bytes.Equal(data, proc) {
|
||||||
t.Errorf("test %d: compress/decompress mismatch: have %x, want %x", i, proc, data)
|
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.
|
// Tests that data bitset decoding and rencoding works and is bijective.
|
||||||
@ -179,3 +184,40 @@ func benchmarkEncoding(b *testing.B, bytes int, fill float64) {
|
|||||||
bitsetDecodeBytes(bitsetEncodeBytes(data), len(data))
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
147
core/types/rlp_fuzzer_test.go
Normal file
147
core/types/rlp_fuzzer_test.go
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -14,12 +14,31 @@
|
|||||||
// You should have received a copy of the GNU Lesser General Public License
|
// 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/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package rlp
|
package vm
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
func Fuzz(f *testing.F) {
|
"github.com/ethereum/go-ethereum/common"
|
||||||
f.Fuzz(func(t *testing.T, data []byte) {
|
)
|
||||||
fuzz(data)
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
@ -18,13 +18,11 @@ package runtime
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"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) {
|
f.Fuzz(func(t *testing.T, code, input []byte) {
|
||||||
runtime.Execute(code, input, &runtime.Config{
|
Execute(code, input, &Config{
|
||||||
GasLimit: 12000000,
|
GasLimit: 12000000,
|
||||||
})
|
})
|
||||||
})
|
})
|
@ -21,6 +21,7 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
@ -28,7 +29,6 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"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"
|
||||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
@ -36,6 +36,56 @@ import (
|
|||||||
fuzz "github.com/google/gofuzz"
|
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
|
var trieRoot common.Hash
|
||||||
|
|
||||||
func getChain() *core.BlockChain {
|
func getChain() *core.BlockChain {
|
||||||
@ -87,9 +137,9 @@ type dummyBackend struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *dummyBackend) Chain() *core.BlockChain { return d.chain }
|
func (d *dummyBackend) Chain() *core.BlockChain { return d.chain }
|
||||||
func (d *dummyBackend) RunPeer(*snap.Peer, snap.Handler) error { return nil }
|
func (d *dummyBackend) RunPeer(*Peer, Handler) error { return nil }
|
||||||
func (d *dummyBackend) PeerInfo(enode.ID) interface{} { return "Foo" }
|
func (d *dummyBackend) PeerInfo(enode.ID) interface{} { return "Foo" }
|
||||||
func (d *dummyBackend) Handle(*snap.Peer, snap.Packet) error { return nil }
|
func (d *dummyBackend) Handle(*Peer, Packet) error { return nil }
|
||||||
|
|
||||||
type dummyRW struct {
|
type dummyRW struct {
|
||||||
code uint64
|
code uint64
|
||||||
@ -110,34 +160,3 @@ func (d *dummyRW) WriteMsg(msg p2p.Msg) error {
|
|||||||
d.writeCount++
|
d.writeCount++
|
||||||
return nil
|
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
|
|
||||||
}
|
|
2
go.mod
2
go.mod
@ -33,7 +33,7 @@ require (
|
|||||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||||
github.com/golang/protobuf v1.5.3
|
github.com/golang/protobuf v1.5.3
|
||||||
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb
|
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/google/uuid v1.3.0
|
||||||
github.com/gorilla/websocket v1.4.2
|
github.com/gorilla/websocket v1.4.2
|
||||||
github.com/graph-gophers/graphql-go v1.3.0
|
github.com/graph-gophers/graphql-go v1.3.0
|
||||||
|
4
go.sum
4
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 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
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.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.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||||
github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
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 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/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=
|
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
|
200
oss-fuzz.sh
200
oss-fuzz.sh
@ -48,38 +48,26 @@ DOG
|
|||||||
cd -
|
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() {
|
function compile_fuzzer() {
|
||||||
path=$GOPATH/src/github.com/ethereum/go-ethereum/$1
|
package=$1
|
||||||
function=$2
|
function=$2
|
||||||
fuzzer=$3
|
fuzzer=$3
|
||||||
|
file=$4
|
||||||
|
|
||||||
|
path=$GOPATH/src/$package
|
||||||
|
|
||||||
echo "Building $fuzzer"
|
echo "Building $fuzzer"
|
||||||
cd $path
|
cd $path
|
||||||
|
|
||||||
# Install build dependencies
|
# Install build dependencies
|
||||||
go install github.com/AdamKorcz/go-118-fuzz-build@latest
|
go mod tidy
|
||||||
go get github.com/AdamKorcz/go-118-fuzz-build/testing
|
go get github.com/holiman/gofuzz-shim/testing
|
||||||
|
|
||||||
# Test if file contains a line with "func $function(" and "testing.F".
|
if [[ $SANITIZER == *coverage* ]]; then
|
||||||
if [ $(grep -r "func $function(" $path | grep "testing.F" | wc -l) -eq 1 ]
|
coverbuild $path $function $fuzzer $coverpkg
|
||||||
then
|
|
||||||
build_native_go_fuzzer $fuzzer $function $path
|
|
||||||
else
|
else
|
||||||
echo "Could not find the function: func ${function}(f *testing.F)"
|
gofuzz-shim --func $function --package $package -f $file -o $fuzzer.a
|
||||||
|
$CXX $CXXFLAGS $LIB_FUZZING_ENGINE $fuzzer.a -o $OUT/$fuzzer
|
||||||
fi
|
fi
|
||||||
|
|
||||||
## Check if there exists a seed corpus file
|
## Check if there exists a seed corpus file
|
||||||
@ -92,42 +80,140 @@ function compile_fuzzer() {
|
|||||||
cd -
|
cd -
|
||||||
}
|
}
|
||||||
|
|
||||||
compile_fuzzer tests/fuzzers/bitutil FuzzEncoder fuzzBitutilEncoder
|
go install github.com/holiman/gofuzz-shim@latest
|
||||||
compile_fuzzer tests/fuzzers/bitutil FuzzDecoder fuzzBitutilDecoder
|
repo=$GOPATH/src/github.com/ethereum/go-ethereum
|
||||||
compile_fuzzer tests/fuzzers/bn256 FuzzAdd fuzzBn256Add
|
compile_fuzzer github.com/ethereum/go-ethereum/accounts/abi \
|
||||||
compile_fuzzer tests/fuzzers/bn256 FuzzMul fuzzBn256Mul
|
FuzzABI fuzzAbi \
|
||||||
compile_fuzzer tests/fuzzers/bn256 FuzzPair fuzzBn256Pair
|
$repo/accounts/abi/abifuzzer_test.go
|
||||||
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
|
|
||||||
|
|
||||||
compile_fuzzer tests/fuzzers/bls12381 FuzzG1Add fuzz_g1_add
|
compile_fuzzer github.com/ethereum/go-ethereum/common/bitutil \
|
||||||
compile_fuzzer tests/fuzzers/bls12381 FuzzG1Mul fuzz_g1_mul
|
FuzzEncoder fuzzBitutilEncoder \
|
||||||
compile_fuzzer tests/fuzzers/bls12381 FuzzG1MultiExp fuzz_g1_multiexp
|
$repo/common/bitutil/compress_test.go
|
||||||
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 tests/fuzzers/bls12381 FuzzCrossG1Add fuzz_cross_g1_add
|
compile_fuzzer github.com/ethereum/go-ethereum/common/bitutil \
|
||||||
compile_fuzzer tests/fuzzers/bls12381 FuzzCrossG1MultiExp fuzz_cross_g1_multiexp
|
FuzzDecoder fuzzBitutilDecoder \
|
||||||
compile_fuzzer tests/fuzzers/bls12381 FuzzCrossG2Add fuzz_cross_g2_add
|
$repo/common/bitutil/compress_test.go
|
||||||
compile_fuzzer tests/fuzzers/bls12381 FuzzCrossPairing fuzz_cross_pairing
|
|
||||||
|
|
||||||
compile_fuzzer tests/fuzzers/snap FuzzARange fuzz_account_range
|
compile_fuzzer github.com/ethereum/go-ethereum/core/vm/runtime \
|
||||||
compile_fuzzer tests/fuzzers/snap FuzzSRange fuzz_storage_range
|
FuzzVmRuntime fuzzVmRuntime\
|
||||||
compile_fuzzer tests/fuzzers/snap FuzzByteCodes fuzz_byte_codes
|
$repo/core/vm/runtime/runtime_fuzz_test.go
|
||||||
compile_fuzzer tests/fuzzers/snap FuzzTrieNodes fuzz_trie_nodes
|
|
||||||
|
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
|
|
||||||
|
@ -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
|
|
||||||
}
|
|
Binary file not shown.
@ -1 +0,0 @@
|
|||||||
Ë€€€À€ÀÃÀÀÀÀ
|
|
@ -1,2 +0,0 @@
|
|||||||
øNƒ“à€€€‚
|
|
||||||
• <EFBFBD>aùËåÀP?-'´{ÏЋD<>Y¯<1E>³fÆj\ÿE÷ ~ì<>•ÒçF?1(íij6<6A>@É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
|
|
||||||
}
|
|
@ -35,7 +35,7 @@ func Fuzz(f *testing.F) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func fuzz(dataP1, dataP2 []byte) int {
|
func fuzz(dataP1, dataP2 []byte) {
|
||||||
var (
|
var (
|
||||||
curveA = secp256k1.S256()
|
curveA = secp256k1.S256()
|
||||||
curveB = btcec.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)
|
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))
|
panic(fmt.Sprintf("Addition failed: geth: %s %s btcd: %s %s", resAX, resAY, resBX, resBY))
|
||||||
}
|
}
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
@ -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)
|
|
||||||
})
|
|
||||||
}
|
|
155
trie/stacktrie_fuzzer_test.go
Normal file
155
trie/stacktrie_fuzzer_test.go
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
@ -22,6 +22,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash"
|
"hash"
|
||||||
|
"io"
|
||||||
"math/big"
|
"math/big"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"reflect"
|
"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("980c393656413a15c8da01978ed9f89feb80b502f58f2d640e3a2f5f7a99a7018f1b573befd92053ac6f78fca4a87268"), value: common.Hex2Bytes("")}, // step 24
|
||||||
{op: 1, key: common.Hex2Bytes("fd"), value: common.Hex2Bytes("")}, // step 25
|
{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.
|
// randTest performs random trie operations.
|
||||||
@ -389,33 +392,45 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (randTest) Generate(r *rand.Rand, size int) reflect.Value {
|
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 allKeys [][]byte
|
||||||
|
var one = []byte{0}
|
||||||
genKey := func() []byte {
|
genKey := func() []byte {
|
||||||
if len(allKeys) < 2 || r.Intn(100) < 10 {
|
r.Read(one)
|
||||||
|
if len(allKeys) < 2 || one[0]%100 > 90 {
|
||||||
// new key
|
// new key
|
||||||
key := make([]byte, r.Intn(50))
|
size := one[0] % 50
|
||||||
|
key := make([]byte, size)
|
||||||
r.Read(key)
|
r.Read(key)
|
||||||
allKeys = append(allKeys, key)
|
allKeys = append(allKeys, key)
|
||||||
return key
|
return key
|
||||||
}
|
}
|
||||||
// use existing key
|
// use existing key
|
||||||
return allKeys[r.Intn(len(allKeys))]
|
idx := int(one[0]) % len(allKeys)
|
||||||
|
return allKeys[idx]
|
||||||
}
|
}
|
||||||
|
|
||||||
var steps randTest
|
var steps randTest
|
||||||
for i := 0; i < size; i++ {
|
for !finished() {
|
||||||
step := randTestStep{op: r.Intn(opMax)}
|
r.Read(one)
|
||||||
|
step := randTestStep{op: int(one[0]) % opMax}
|
||||||
switch step.op {
|
switch step.op {
|
||||||
case opUpdate:
|
case opUpdate:
|
||||||
step.key = genKey()
|
step.key = genKey()
|
||||||
step.value = make([]byte, 8)
|
step.value = make([]byte, 8)
|
||||||
binary.BigEndian.PutUint64(step.value, uint64(i))
|
binary.BigEndian.PutUint64(step.value, uint64(len(steps)))
|
||||||
case opGet, opDelete, opProve:
|
case opGet, opDelete, opProve:
|
||||||
step.key = genKey()
|
step.key = genKey()
|
||||||
}
|
}
|
||||||
steps = append(steps, step)
|
steps = append(steps, step)
|
||||||
}
|
}
|
||||||
return reflect.ValueOf(steps)
|
return steps
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyAccessList(old *Trie, new *Trie, set *trienode.NodeSet) error {
|
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
|
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
|
var scheme = rawdb.HashScheme
|
||||||
if rand.Intn(2) == 0 {
|
if rand.Intn(2) == 0 {
|
||||||
scheme = rawdb.PathScheme
|
scheme = rawdb.PathScheme
|
||||||
@ -513,12 +533,12 @@ func runRandTest(rt randTest) bool {
|
|||||||
newtr, err := New(TrieID(root), triedb)
|
newtr, err := New(TrieID(root), triedb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rt[i].err = err
|
rt[i].err = err
|
||||||
return false
|
return err
|
||||||
}
|
}
|
||||||
if nodes != nil {
|
if nodes != nil {
|
||||||
if err := verifyAccessList(origTrie, newtr, nodes); err != nil {
|
if err := verifyAccessList(origTrie, newtr, nodes); err != nil {
|
||||||
rt[i].err = err
|
rt[i].err = err
|
||||||
return false
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tr = newtr
|
tr = newtr
|
||||||
@ -587,14 +607,14 @@ func runRandTest(rt randTest) bool {
|
|||||||
}
|
}
|
||||||
// Abort the test on error.
|
// Abort the test on error.
|
||||||
if rt[i].err != nil {
|
if rt[i].err != nil {
|
||||||
return false
|
return rt[i].err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRandom(t *testing.T) {
|
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 {
|
if cerr, ok := err.(*quick.CheckError); ok {
|
||||||
t.Fatalf("random test iteration %d failed: %s", cerr.Count, spew.Sdump(cerr.In))
|
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)
|
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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user